packages feed

pdf2line (empty) → 0.0.1

raw patch · 174 files changed

+89802/−0 lines, 174 filesdep +FindBindep +basedep +binarysetup-changed

Dependencies added: FindBin, base, binary, bytestring, containers, filepath, process

Files

+ COPYING view
@@ -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.
+ LICENSE view
@@ -0,0 +1,6 @@+Copyright 2008 by Audrey Tang.++Files under the "xpdf" directory are copyrighted by Glyph & Cog, LLC.++This package is subject to the GNU General Public License, version 2.+See the COPYING file for details.
+ Makefile.pdfdump view
@@ -0,0 +1,92 @@+#------------------------------------------------------------------------++.SUFFIXES: .cc .c++.cc.o:+	g++ -O -I. -Ixpdf -c $< -o $@++.c.o:+	gcc -O -I. -Ixpdf -c $< -o $@++OBJS = \+	xpdf/Annot.o \+	xpdf/Array.o \+	xpdf/BuiltinFont.o \+	xpdf/BuiltinFontTables.o \+	xpdf/CMap.o \+	xpdf/Catalog.o \+	xpdf/CharCodeToUnicode.o \+	xpdf/CoreOutputDev.o \+	xpdf/Decrypt.o \+	xpdf/Dict.o \+	xpdf/Error.o \+	xpdf/FixedPoint.o \+	xpdf/FoFiBase.o \+	xpdf/FoFiEncodings.o \+	xpdf/FoFiTrueType.o \+	xpdf/FoFiType1.o \+	xpdf/FoFiType1C.o \+	xpdf/FontEncodingTables.o \+	xpdf/Function.o \+	xpdf/GHash.o \+	xpdf/GList.o \+	xpdf/GString.o \+	xpdf/Gfx.o \+	xpdf/GfxFont.o \+	xpdf/GfxState.o \+	xpdf/GlobalParams.o \+	xpdf/JArithmeticDecoder.o \+	xpdf/JBIG2Stream.o \+	xpdf/JPXStream.o \+	xpdf/Lexer.o \+	xpdf/Link.o \+	xpdf/NameToCharCode.o \+	xpdf/Object.o \+	xpdf/Outline.o \+	xpdf/OutputDev.o \+	xpdf/PDFCore.o \+	xpdf/PDFDoc.o \+	xpdf/PDFDocEncoding.o \+	xpdf/PSTokenizer.o \+	xpdf/Page.o \+	xpdf/Parser.o \+	xpdf/SecurityHandler.o \+	xpdf/Splash.o \+	xpdf/SplashBitmap.o \+	xpdf/SplashClip.o \+	xpdf/SplashFTFont.o \+	xpdf/SplashFTFontEngine.o \+	xpdf/SplashFTFontFile.o \+	xpdf/SplashFont.o \+	xpdf/SplashFontEngine.o \+	xpdf/SplashFontFile.o \+	xpdf/SplashFontFileID.o \+	xpdf/SplashOutputDev.o \+	xpdf/SplashPath.o \+	xpdf/SplashPattern.o \+	xpdf/SplashScreen.o \+	xpdf/SplashState.o \+	xpdf/SplashT1Font.o \+	xpdf/SplashT1FontEngine.o \+	xpdf/SplashT1FontFile.o \+	xpdf/SplashXPath.o \+	xpdf/SplashXPathScanner.o \+	xpdf/Stream.o \+	xpdf/TextOutputDev.o \+	xpdf/UnicodeMap.o \+	xpdf/UnicodeTypeTable.o \+	xpdf/XRef.o \+	xpdf/gfile.o \+	xpdf/gmem.o \+	xpdf/gmempp.o \+	xpdf/parseargs.o \+	xpdf/pdfdump.o++all: $(OBJS)+	-@rm dist/build/pdfdump/pdfdump+	g++ -o dist/build/pdfdump/pdfdump $(OBJS)++clean:+	-@rm dist/build/pdfdump/pdfdump xpdf/*.o++#------------------------------------------------------------------------
+ README view
@@ -0,0 +1,8 @@+"pdf2line" is a simple command-line utility to convert PDF into text.++It is based on "pdftotext" from the Xpdf suite, but with a different+layout algorithm that preserves relative column position and line spacing.++To use it for anything other than ASCII text, you'd need to have a+/usr/local/etc/xpdfrc or ~/.xpdfrc configured in a way that allows+Xpdf's "pdftotext" to run correctly for your purpose.
+ Setup.lhs view
@@ -0,0 +1,14 @@+#!/usr/bin/env runhaskell+\begin{code}+import System.Cmd+import Distribution.Simple++main = defaultMainWithHooks $ simpleUserHooks+    { buildHook = myBuild (buildHook simpleUserHooks) }++myBuild doBuild x y z w = do+    rv <- doBuild x y z w+    system "make -f Makefile.pdfdump"+    return rv++\end{code}
+ pdf2line.cabal view
@@ -0,0 +1,190 @@+name:               pdf2line+version:            0.0.1+copyright:          2008 Audrey Tang; distributed with parts of "xpdf" by Glyph & Cog, LLC.+license:            GPL+license-file:       LICENSE+author:             Audrey Tang <audreyt@audreyt.org>+maintainer:         Audrey Tang <audreyt@audreyt.org>+synopsis:           Simple command-line utility to convert PDF into text+description:        Simple command-line utility to convert PDF into text+stability:          experimental+build-type:         Simple+category:           Text+extra-source-files: README COPYING Makefile.pdfdump+                    xpdf/Annot.cc+                    xpdf/Annot.h+                    xpdf/Array.cc+                    xpdf/Array.h+                    xpdf/BuiltinFont.cc+                    xpdf/BuiltinFont.h+                    xpdf/BuiltinFontTables.cc+                    xpdf/BuiltinFontTables.h+                    xpdf/CMap.cc+                    xpdf/CMap.h+                    xpdf/Catalog.cc+                    xpdf/Catalog.h+                    xpdf/CharCodeToUnicode.cc+                    xpdf/CharCodeToUnicode.h+                    xpdf/CharTypes.h+                    xpdf/CompactFontTables.h+                    xpdf/CoreOutputDev.cc+                    xpdf/CoreOutputDev.h+                    xpdf/Decrypt.cc+                    xpdf/Decrypt.h+                    xpdf/Dict.cc+                    xpdf/Dict.h+                    xpdf/Error.cc+                    xpdf/Error.h+                    xpdf/ErrorCodes.h+                    xpdf/FixedPoint.cc+                    xpdf/FixedPoint.h+                    xpdf/FoFiBase.cc+                    xpdf/FoFiBase.h+                    xpdf/FoFiEncodings.cc+                    xpdf/FoFiEncodings.h+                    xpdf/FoFiTrueType.cc+                    xpdf/FoFiTrueType.h+                    xpdf/FoFiType1.cc+                    xpdf/FoFiType1.h+                    xpdf/FoFiType1C.cc+                    xpdf/FoFiType1C.h+                    xpdf/FontEncodingTables.cc+                    xpdf/FontEncodingTables.h+                    xpdf/Function.cc+                    xpdf/Function.h+                    xpdf/GHash.cc+                    xpdf/GHash.h+                    xpdf/GList.cc+                    xpdf/GList.h+                    xpdf/GMutex.h+                    xpdf/GString.cc+                    xpdf/GString.h+                    xpdf/Gfx.cc+                    xpdf/Gfx.h+                    xpdf/GfxFont.cc+                    xpdf/GfxFont.h+                    xpdf/GfxState.cc+                    xpdf/GfxState.h+                    xpdf/GlobalParams.cc+                    xpdf/GlobalParams.h+                    xpdf/ImageOutputDev.cc+                    xpdf/ImageOutputDev.h+                    xpdf/JArithmeticDecoder.cc+                    xpdf/JArithmeticDecoder.h+                    xpdf/JBIG2Stream.cc+                    xpdf/JBIG2Stream.h+                    xpdf/JPXStream.cc+                    xpdf/JPXStream.h+                    xpdf/Lexer.cc+                    xpdf/Lexer.h+                    xpdf/Link.cc+                    xpdf/Link.h+                    xpdf/NameToCharCode.cc+                    xpdf/NameToCharCode.h+                    xpdf/NameToUnicodeTable.h+                    xpdf/Object.cc+                    xpdf/Object.h+                    xpdf/Outline.cc+                    xpdf/Outline.h+                    xpdf/OutputDev.cc+                    xpdf/OutputDev.h+                    xpdf/PDFCore.cc+                    xpdf/PDFCore.h+                    xpdf/PDFDoc.cc+                    xpdf/PDFDoc.h+                    xpdf/PDFDocEncoding.cc+                    xpdf/PDFDocEncoding.h+                    xpdf/PSOutputDev.cc+                    xpdf/PSOutputDev.h+                    xpdf/PSTokenizer.cc+                    xpdf/PSTokenizer.h+                    xpdf/Page.cc+                    xpdf/Page.h+                    xpdf/Parser.cc+                    xpdf/Parser.h+                    xpdf/PreScanOutputDev.cc+                    xpdf/PreScanOutputDev.h+                    xpdf/SecurityHandler.cc+                    xpdf/SecurityHandler.h+                    xpdf/Splash.cc+                    xpdf/Splash.h+                    xpdf/SplashBitmap.cc+                    xpdf/SplashBitmap.h+                    xpdf/SplashClip.cc+                    xpdf/SplashClip.h+                    xpdf/SplashErrorCodes.h+                    xpdf/SplashFTFont.cc+                    xpdf/SplashFTFont.h+                    xpdf/SplashFTFontEngine.cc+                    xpdf/SplashFTFontEngine.h+                    xpdf/SplashFTFontFile.cc+                    xpdf/SplashFTFontFile.h+                    xpdf/SplashFont.cc+                    xpdf/SplashFont.h+                    xpdf/SplashFontEngine.cc+                    xpdf/SplashFontEngine.h+                    xpdf/SplashFontFile.cc+                    xpdf/SplashFontFile.h+                    xpdf/SplashFontFileID.cc+                    xpdf/SplashFontFileID.h+                    xpdf/SplashGlyphBitmap.h+                    xpdf/SplashMath.h+                    xpdf/SplashOutputDev.cc+                    xpdf/SplashOutputDev.h+                    xpdf/SplashPath.cc+                    xpdf/SplashPath.h+                    xpdf/SplashPattern.cc+                    xpdf/SplashPattern.h+                    xpdf/SplashScreen.cc+                    xpdf/SplashScreen.h+                    xpdf/SplashState.cc+                    xpdf/SplashState.h+                    xpdf/SplashT1Font.cc+                    xpdf/SplashT1Font.h+                    xpdf/SplashT1FontEngine.cc+                    xpdf/SplashT1FontEngine.h+                    xpdf/SplashT1FontFile.cc+                    xpdf/SplashT1FontFile.h+                    xpdf/SplashTypes.h+                    xpdf/SplashXPath.cc+                    xpdf/SplashXPath.h+                    xpdf/SplashXPathScanner.cc+                    xpdf/SplashXPathScanner.h+                    xpdf/Stream-CCITT.h+                    xpdf/Stream.cc+                    xpdf/Stream.h+                    xpdf/TextOutputDev.cc+                    xpdf/TextOutputDev.h+                    xpdf/UTF8.h+                    xpdf/UnicodeMap.cc+                    xpdf/UnicodeMap.h+                    xpdf/UnicodeMapTables.h+                    xpdf/UnicodeTypeTable.cc+                    xpdf/UnicodeTypeTable.h+                    xpdf/XRef.cc+                    xpdf/XRef.h+                    xpdf/XpdfPluginAPI.cc+                    xpdf/XpdfPluginAPI.h+                    xpdf/about-text.h+                    xpdf/aconf.h+                    xpdf/aconf2.h+                    xpdf/config.h+                    xpdf/gfile.cc+                    xpdf/gfile.h+                    xpdf/gmem.cc+                    xpdf/gmem.h+                    xpdf/gmempp.cc+                    xpdf/gtypes.h+                    xpdf/parseargs.c+                    xpdf/parseargs.h+                    xpdf/pdfdump.cc++executable:         pdf2line+main-is:            pdf2line.hs+build-depends:      base, bytestring, binary, FindBin, containers, filepath, process+hs-source-dirs:     .++executable:         pdfdump+main-is:            pdf2line.hs+build-depends:      base, bytestring, binary, FindBin, containers, filepath, process+hs-source-dirs:     .
+ pdf2line.hs view
@@ -0,0 +1,149 @@+{-# OPTIONS_GHC -O2 -fglasgow-exts -funbox-strict-fields #-}++module Main where+import Data.Binary+import Data.Binary.Get+import System.Process+import Control.Monad+import Data.IORef+import System.FilePath+import System.IO+import System.IO.Unsafe+import System.Environment (getArgs)+import System.Environment.FindBin+import Data.ByteString (ByteString)+import qualified Data.IntMap as IM+import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString.Char8 as C++__AlignRight__ :: Bool+__AlignRight__ = False++main :: IO ()+main = do+    args <- getArgs+    hSetBinaryMode stdout True+    case args of+        []  -> putStrLn "Usage: pdf2line input.pdf ... > output.txt"+        ["-"] -> do+            hSetBinaryMode stdin True+            res <- L.getContents+            mapM_ dumpPage (fromDoc $! decode res)+        _   -> forM_ args $! \inFile -> do+            let pdfdump = __Bin__ </> "pdfdump"+            (_,out,err,pid) <- runInteractiveCommand $! pdfdump ++ " \"" ++ inFile ++ "\""+            res <- L.hGetContents out+            L.length res `seq` waitForProcess pid+            when (L.null res) $! do+                L.hPutStr stderr =<< L.hGetContents err+            mapM_ dumpPage (fromDoc $! decode res)++dumpPage :: Page -> IO ()+dumpPage page+    | IM.null pg    = return ()+    | otherwise     = do+        _CurrentLine <- newIORef maxBound+        forM_ (IM.toAscList pg) $! \(lineNum, MkLine pt strs) -> do+            linePrev <- readIORef _CurrentLine+            replicateM_ ((lineNum - linePrev + (pt `div` 4)) `div` pt) (S.putStr _NewLine)+            _CurrentColumn <- newIORef 0+            forM_ (IM.toAscList strs) $! \(col, str) -> do+                cur <- readIORef _CurrentColumn+                S.putStr $! S.take (col - cur) _Spaces+                S.putStr str+                writeIORef _CurrentColumn (col + S.length str)+            S.putStr _NewLine+            writeIORef _CurrentLine (lineNum+pt)+        S.putStr _NewPage+    where+    pg = fromPage page++_Spaces, _NewLine, _NewPage :: ByteString+_Spaces  = S.replicate 4096 0x20+_NewLine = C.pack "\r\n"+_NewPage = C.pack "\r\n\x0C\r\n"++-- A Page is a IntMap from line-number to a map from column-number to bytestring.+newtype Doc = MkDoc { fromDoc :: [Page] } deriving Show+newtype Page = MkPage { fromPage :: IM.IntMap Line } deriving Show+data Line = MkLine+    { linePt    :: !Int+    , lineStrs  :: !(IM.IntMap S.ByteString)+    }+    deriving Show++instance Binary Doc where+    put = undefined+    get = liftM MkDoc getList+        where+        getList = do+            rv  <- isEmpty+            if rv then return []+                  else liftM2 (:) get getList++data Chunk = MkChunk+    { c_right   :: !Int+    , c_upper   :: !Int+    , c_pt      :: !Int+    , c_str     :: !ByteString+    }+    deriving Show++instance Binary Page where+    put = error "put Page is not defined"+    get = getChunk maxBound []+        where+        getChunk minPt chunks = do+            rv  <- isEmpty+            if rv then done else do+                w8  <- getWord8+                case w8 of+                    0x6C    -> do -- 'l'+                        skip 1+                        col <- if __AlignRight__+                            then skip 9 >> getInt 6+                            else getInt 6 >>= ((skip 9 >>) . return)+                        skip 21+                        ln      <- getInt 6+                        skip 3+                        pt      <- getInt 6+                        skip 7+                        sz      <- getInt 4+                        skip 1+                        str     <- getByteString sz+                        w8'     <- getWord8+                        case w8' of+                            0x0D    -> skip 1+                            0x0A    -> return ()+                            _       -> fail $! "Bad parse: " ++ show w8'+                        let pt' = min minPt pt+                        getChunk pt' (MkChunk col ln pt str:chunks)+                    0x0D    -> skip 1 >> done+                    0x0A    -> done+                    _       -> fail $! "Bad parse: " ++ show w8+            where+            done = return $! pageOf (foldl (buildPage minPt) (MkBuild (MkPage IM.empty) 0) chunks)+        getInt :: Int -> Get Int+        getInt (n+1) = liftM2 mkInt getWord8 (getInt n)+            where+            mkInt digit rest = fromEnum (digit - 0x30) * (10 ^ n) + rest+        getInt _     = return 0+        buildPage minPt (MkBuild (MkPage pg) base) (MkChunk col ln pt str)+            = MkBuild (MkPage (IM.insert base' entry pg)) base'+            where+            sz      = S.length str+            width   = if __AlignRight__+                then ((col * 2) `div` minPt) - sz+                else ((col * 2) `div` minPt)+            base'   = if abs (ln - base) + (minPt `div` 4) < minPt then base else ln+            entry   = case IM.lookup base' pg of+                Just (MkLine pt' strs)  -> MkLine (max pt pt') (IM.insert width str strs)+                _                       -> MkLine pt (IM.singleton width str)++data Build = MkBuild+    { pageOf    :: !Page+    , baseOf    :: !Int+    }+    deriving Show+
+ xpdf/Annot.cc view
@@ -0,0 +1,1556 @@+//========================================================================+//+// 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 "GList.h"+#include "Error.h"+#include "Object.h"+#include "Catalog.h"+#include "Gfx.h"+#include "GfxFont.h"+#include "Lexer.h"+#include "Annot.h"++//------------------------------------------------------------------------++#define annotFlagHidden    0x0002+#define annotFlagPrint     0x0004+#define annotFlagNoView    0x0020++#define fieldFlagReadOnly           0x00000001+#define fieldFlagRequired           0x00000002+#define fieldFlagNoExport           0x00000004+#define fieldFlagMultiline          0x00001000+#define fieldFlagPassword           0x00002000+#define fieldFlagNoToggleToOff      0x00004000+#define fieldFlagRadio              0x00008000+#define fieldFlagPushbutton         0x00010000+#define fieldFlagCombo              0x00020000+#define fieldFlagEdit               0x00040000+#define fieldFlagSort               0x00080000+#define fieldFlagFileSelect         0x00100000+#define fieldFlagMultiSelect        0x00200000+#define fieldFlagDoNotSpellCheck    0x00400000+#define fieldFlagDoNotScroll        0x00800000+#define fieldFlagComb               0x01000000+#define fieldFlagRichText           0x02000000+#define fieldFlagRadiosInUnison     0x02000000+#define fieldFlagCommitOnSelChange  0x04000000++#define fieldQuadLeft   0+#define fieldQuadCenter 1+#define fieldQuadRight  2++// distance of Bezier control point from center for circle approximation+// = (4 * (sqrt(2) - 1) / 3) * r+#define bezierCircle 0.55228475++//------------------------------------------------------------------------+// AnnotBorderStyle+//------------------------------------------------------------------------++AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType typeA, double widthA,+				   double *dashA, int dashLengthA,+				   double rA, double gA, double bA) {+  type = typeA;+  width = widthA;+  dash = dashA;+  dashLength = dashLengthA;+  r = rA;+  g = gA;+  b = bA;+}++AnnotBorderStyle::~AnnotBorderStyle() {+  if (dash) {+    gfree(dash);+  }+}++//------------------------------------------------------------------------+// Annot+//------------------------------------------------------------------------++Annot::Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref *refA) {+  Object apObj, asObj, obj1, obj2, obj3;+  AnnotBorderType borderType;+  double borderWidth;+  double *borderDash;+  int borderDashLength;+  double borderR, borderG, borderB;+  double t;+  int i;++  ok = gTrue;+  xref = xrefA;+  ref = *refA;+  type = 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(-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;+  borderR = 0;+  borderG = 0;+  borderB = 1;+  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();+	}+      }+    }+  }+  obj1.free();+  if (dict->lookup("C", &obj1)->isArray() && obj1.arrayGetLength() == 3) {+    if (obj1.arrayGet(0, &obj2)->isNum()) {+      borderR = obj2.getNum();+    }+    obj1.free();+    if (obj1.arrayGet(1, &obj2)->isNum()) {+      borderG = obj2.getNum();+    }+    obj1.free();+    if (obj1.arrayGet(2, &obj2)->isNum()) {+      borderB = obj2.getNum();+    }+    obj1.free();+  }+  obj1.free();+  borderStyle = new AnnotBorderStyle(borderType, borderWidth,+				     borderDash, borderDashLength,+				     borderR, borderG, borderB);++  //----- get the annotation appearance++  if (dict->lookup("AP", &apObj)->isDict()) {+    if (dict->lookup("AS", &asObj)->isName()) {+      if (apObj.dictLookup("N", &obj1)->isDict()) {+	if (obj1.dictLookupNF(asObj.getName(), &obj2)->isRef()) {+	  obj2.copy(&appearance);+	  ok = gTrue;+	} else {+	  obj2.free();+	  if (obj1.dictLookupNF("Off", &obj2)->isRef()) {+	    obj2.copy(&appearance);+	  }+	}+	obj2.free();+      }+      obj1.free();+    } else {+      if (apObj.dictLookupNF("N", &obj1)->isRef()) {+	obj1.copy(&appearance);+      }+      obj1.free();+    }+    asObj.free();+  }+  apObj.free();+}++Annot::~Annot() {+  if (type) {+    delete type;+  }+  appearance.free();+  if (appearBuf) {+    delete appearBuf;+  }+  if (borderStyle) {+    delete borderStyle;+  }+}++void Annot::generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm) {+  Object mkObj, ftObj, appearDict, drObj, obj1, obj2, obj3;+  Dict *mkDict;+  MemStream *appearStream;+  GfxFontDict *fontDict;+  GBool hasCaption;+  double w, dx, dy, r;+  double *dash;+  GString *caption, *da;+  GString **text;+  GBool *selection;+  int dashLength, ff, quadding, comb, nOptions, topIdx, i, j;++  // must be a Widget annotation+  if (type->cmp("Widget")) {+    return;+  }++  appearBuf = new GString();++  // 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->appendf("0 0 {0:.2f} {1:.2f} re f\n",+			 xMax - xMin, yMax - yMin);+    }+    obj1.free();+  }++  // get the field type+  fieldLookup(field, "FT", &ftObj);++  // get the field flags (Ff) value+  if (fieldLookup(field, "Ff", &obj1)->isInt()) {+    ff = obj1.getInt();+  } else {+    ff = 0;+  }+  obj1.free();++  // draw the border+  if (mkDict) {+    w = borderStyle->getWidth();+    if (w > 0) {+      mkDict->lookup("BC", &obj1);+      if (!(obj1.isArray() && obj1.arrayGetLength() > 0)) {+	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") && (ff & fieldFlagRadio) && !hasCaption) {+	  r = 0.5 * (dx < dy ? dx : dy);+	  switch (borderStyle->getType()) {+	  case annotBorderDashed:+	    appearBuf->append("[");+	    borderStyle->getDash(&dash, &dashLength);+	    for (i = 0; i < dashLength; ++i) {+	      appearBuf->appendf(" {0:.2f}", dash[i]);+	    }+	    appearBuf->append("] 0 d\n");+	    // fall through to the solid case+	  case annotBorderSolid:+	  case annotBorderUnderlined:+	    appearBuf->appendf("{0:.2f} w\n", w);+	    setColor(obj1.getArray(), gFalse, 0);+	    drawCircle(0.5 * dx, 0.5 * dy, r - 0.5 * w, gFalse);+	    break;+	  case annotBorderBeveled:+	  case annotBorderInset:+	    appearBuf->appendf("{0:.2f} w\n", 0.5 * w);+	    setColor(obj1.getArray(), gFalse, 0);+	    drawCircle(0.5 * dx, 0.5 * dy, r - 0.25 * w, gFalse);+	    setColor(obj1.getArray(), gFalse,+		     borderStyle->getType() == annotBorderBeveled ? 1 : -1);+	    drawCircleTopLeft(0.5 * dx, 0.5 * dy, r - 0.75 * w);+	    setColor(obj1.getArray(), gFalse,+		     borderStyle->getType() == annotBorderBeveled ? -1 : 1);+	    drawCircleBottomRight(0.5 * dx, 0.5 * dy, r - 0.75 * w);+	    break;+	  }++	} else {+	  switch (borderStyle->getType()) {+	  case annotBorderDashed:+	    appearBuf->append("[");+	    borderStyle->getDash(&dash, &dashLength);+	    for (i = 0; i < dashLength; ++i) {+	      appearBuf->appendf(" {0:.2f}", dash[i]);+	    }+	    appearBuf->append("] 0 d\n");+	    // fall through to the solid case+	  case annotBorderSolid:+	    appearBuf->appendf("{0:.2f} w\n", w);+	    setColor(obj1.getArray(), gFalse, 0);+	    appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n",+			       0.5 * w, dx - w, dy - w);+	    break;+	  case annotBorderBeveled:+	  case annotBorderInset:+	    setColor(obj1.getArray(), gTrue,+		     borderStyle->getType() == annotBorderBeveled ? 1 : -1);+	    appearBuf->append("0 0 m\n");+	    appearBuf->appendf("0 {0:.2f} l\n", dy);+	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);+	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);+	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", w, dy - w);+	    appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);+	    appearBuf->append("f\n");+	    setColor(obj1.getArray(), gTrue,+		     borderStyle->getType() == annotBorderBeveled ? -1 : 1);+	    appearBuf->append("0 0 m\n");+	    appearBuf->appendf("{0:.2f} 0 l\n", dx);+	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx, dy);+	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, dy - w);+	    appearBuf->appendf("{0:.2f} {1:.2f} l\n", dx - w, w);+	    appearBuf->appendf("{0:.2f} {0:.2f} l\n", w);+	    appearBuf->append("f\n");+	    break;+	  case annotBorderUnderlined:+	    appearBuf->appendf("{0:.2f} w\n", w);+	    setColor(obj1.getArray(), gFalse, 0);+	    appearBuf->appendf("0 0 m {0:.2f} 0 l s\n", dx);+	    break;+	  }++	  // clip to the inside of the border+	  appearBuf->appendf("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n",+			     w, dx - 2 * w, dy - 2 * w);+	}+      }+      obj1.free();+    }+  }++  // get the resource dictionary+  acroForm->lookup("DR", &drObj);++  // build the font dictionary+  if (drObj.isDict() && drObj.dictLookup("Font", &obj1)->isDict()) {+    fontDict = new GfxFontDict(xref, NULL, obj1.getDict());+  } else {+    fontDict = NULL;+  }+  obj1.free();++  // get the default appearance string+  if (fieldLookup(field, "DA", &obj1)->isNull()) {+    obj1.free();+    acroForm->lookup("DA", &obj1);+  }+  if (obj1.isString()) {+    da = obj1.getString()->copy();+  } else {+    da = NULL;+  }+  obj1.free();++  // 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 (ff & fieldFlagRadio) {+      //~ Acrobat doesn't draw a caption if there is no AP dict (?)+      if (fieldLookup(field, "V", &obj1)->isName()) {+	if (annot->lookup("AS", &obj2)->isName(obj1.getName())) {+	  if (caption) {+	    drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,+		     gFalse, gTrue);+	  } else {+	    if (mkDict) {+	      if (mkDict->lookup("BC", &obj3)->isArray() &&+		  obj3.arrayGetLength() > 0) {+		dx = xMax - xMin;+		dy = yMax - yMin;+		setColor(obj3.getArray(), gTrue, 0);+		drawCircle(0.5 * dx, 0.5 * dy, 0.2 * (dx < dy ? dx : dy),+			   gTrue);+	      }+	      obj3.free();+	    }+	  }+	}+	obj2.free();+      }+      obj1.free();+    // pushbutton+    } else if (ff & fieldFlagPushbutton) {+      if (caption) {+	drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,+		 gFalse, gFalse);+      }+    // checkbox+    } else {+      // According to the PDF spec the off state must be named "Off",+      // and the on state can be named anything, but Acrobat apparently+      // looks for "Yes" and treats anything else as off.+      if (fieldLookup(field, "V", &obj1)->isName("Yes")) {+	if (!caption) {+	  caption = new GString("3"); // ZapfDingbats checkmark+	}+	drawText(caption, da, fontDict, gFalse, 0, fieldQuadCenter,+		 gFalse, gTrue);+      }+      obj1.free();+    }+    if (caption) {+      delete caption;+    }+  } else if (ftObj.isName("Tx")) {+    //~ value strings can be Unicode+    if (fieldLookup(field, "V", &obj1)->isString()) {+      if (fieldLookup(field, "Q", &obj2)->isInt()) {+	quadding = obj2.getInt();+      } else {+	quadding = fieldQuadLeft;+      }+      obj2.free();+      comb = 0;+      if (ff & fieldFlagComb) {+	if (fieldLookup(field, "MaxLen", &obj2)->isInt()) {+	  comb = obj2.getInt();+	}+	obj2.free();+      }+      drawText(obj1.getString(), da, fontDict,+	       ff & fieldFlagMultiline, comb, quadding, gTrue, gFalse);+    }+    obj1.free();+  } else if (ftObj.isName("Ch")) {+    //~ value/option strings can be Unicode+    if (fieldLookup(field, "Q", &obj1)->isInt()) {+      quadding = obj1.getInt();+    } else {+      quadding = fieldQuadLeft;+    }+    obj1.free();+    // combo box+    if (ff & fieldFlagCombo) {+      if (fieldLookup(field, "V", &obj1)->isString()) {+	drawText(obj1.getString(), da, fontDict,+		 gFalse, 0, quadding, gTrue, gFalse);+	//~ Acrobat draws a popup icon on the right side+      }+      obj1.free();+    // list box+    } else {+      if (field->lookup("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+	fieldLookup(field, "V", &obj2);+	for (i = 0; i < nOptions; ++i) {+	  selection[i] = gFalse;+	  if (obj2.isString()) {+	    if (!obj2.getString()->cmp(text[i])) {+	      selection[i] = gTrue;+	    }+	  } else if (obj2.isArray()) {+	    for (j = 0; j < obj2.arrayGetLength(); ++j) {+	      if (obj2.arrayGet(j, &obj3)->isString() &&+		  !obj3.getString()->cmp(text[i])) {+		selection[i] = gTrue;+	      }+	      obj3.free();+	    }+	  }+	}+	obj2.free();+	// get the top index+	if (field->lookup("TI", &obj2)->isInt()) {+	  topIdx = obj2.getInt();+	} else {+	  topIdx = 0;+	}+	obj2.free();+	// draw the text+	drawListBox(text, selection, nOptions, topIdx, da, fontDict, quadding);+	for (i = 0; i < nOptions; ++i) {+	  delete text[i];+	}+	gfree(text);+	gfree(selection);+      }+      obj1.free();+    }+  } else if (ftObj.isName("Sig")) {+    //~unimp+  } else {+    error(-1, "Unknown field type");+  }++  if (da) {+    delete da;+  }++  // build the appearance stream dictionary+  appearDict.initDict(xref);+  appearDict.dictAdd(copyString("Length"),+		     obj1.initInt(appearBuf->getLength()));+  appearDict.dictAdd(copyString("Subtype"), obj1.initName("Form"));+  obj1.initArray(xref);+  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+  if (drObj.isDict()) {+    appearDict.dictAdd(copyString("Resources"), drObj.copy(&obj1));+  }+  drObj.free();++  // build the appearance stream+  appearStream = new MemStream(appearBuf->getCString(), 0,+			       appearBuf->getLength(), &appearDict);+  appearance.free();+  appearance.initStream(appearStream);++  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 Annot::setColor(Array *a, GBool fill, int adjust) {+  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 Annot::drawText(GString *text, GString *da, GfxFontDict *fontDict,+		     GBool multiline, int comb, int quadding,+		     GBool txField, GBool forceZapfDingbats) {+  GList *daToks;+  GString *tok;+  GfxFont *font;+  double fontSize, fontSize2, border, x, xPrev, y, w, w2, wMax;+  int tfPos, tmPos, 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)++  // 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;+  }++  // 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(-1, "Unknown font in field's DA string");+      }+    } else {+      error(-1, "Invalid font name in 'Tf' operator in field's DA string");+    }+    tok = (GString *)daToks->get(tfPos + 1);+    fontSize = atof(tok->getCString());+  } else {+    error(-1, "Missing 'Tf' operator in field's DA string");+  }++  // get the border width+  border = borderStyle->getWidth();++  // setup+  if (txField) {+    appearBuf->append("/Tx BMC\n");+  }+  appearBuf->append("q\n");+  appearBuf->append("BT\n");++  // multi-line text+  if (multiline) {+    // note: the comb flag is ignored in multiline mode++    wMax = xMax - xMin - 2 * border - 4;++    // compute font autosize+    if (fontSize == 0) {+      for (fontSize = 20; fontSize > 1; --fontSize) {+	y = yMax - yMin;+	w2 = 0;+	i = 0;+	while (i < text->getLength()) {+	  getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);+	  if (w > w2) {+	    w2 = w;+	  }+	  i = k;+	  y -= fontSize;+	}+	// approximate the descender for the last line+	if (y >= 0.33 * fontSize) {+	  break;+	}+      }+      if (tfPos >= 0) {+	tok = (GString *)daToks->get(tfPos + 1);+	tok->clear();+	tok->appendf("{0:.2f}", fontSize);+      }+    }++    // starting y coordinate+    // (note: each line of text starts with a Td operator that moves+    // down a line)+    y = yMax - yMin;++    // set the font matrix+    if (tmPos >= 0) {+      tok = (GString *)daToks->get(tmPos + 4);+      tok->clear();+      tok->append('0');+      tok = (GString *)daToks->get(tmPos + 5);+      tok->clear();+      tok->appendf("{0:.2f}", 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 {0:.2f} Tm\n", y);+    }++    // write a series of lines of text+    i = 0;+    xPrev = 0;+    while (i < text->getLength()) {++      getNextLine(text, i, font, fontSize, wMax, &j, &w, &k);++      // compute text start position+      switch (quadding) {+      case fieldQuadLeft:+      default:+	x = border + 2;+	break;+      case fieldQuadCenter:+	x = (xMax - xMin - w) / 2;+	break;+      case fieldQuadRight:+	x = xMax - xMin - border - 2 - w;+	break;+      }++      // draw the line+      appearBuf->appendf("{0:.2f} {1:.2f} Td\n", x - xPrev, -fontSize);+      appearBuf->append('(');+      for (; i < j; ++i) {+	c = text->getChar(i) & 0xff;+	if (c == '(' || c == ')' || c == '\\') {+	  appearBuf->append('\\');+	  appearBuf->append(c);+	} else if (c < 0x20 || c >= 0x80) {+	  appearBuf->appendf("\\{0:03o}", c);+	} else {+	  appearBuf->append(c);+	}+      }+      appearBuf->append(") Tj\n");++      // next line+      i = k;+      xPrev = x;+    }++  // single-line text+  } else {+    //~ replace newlines with spaces? - what does Acrobat do?++    // comb formatting+    if (comb > 0) {++      // compute comb spacing+      w = (xMax - xMin - 2 * border) / comb;++      // compute font autosize+      if (fontSize == 0) {+	fontSize = yMax - yMin - 2 * border;+	if (w < fontSize) {+	  fontSize = w;+	}+	fontSize = floor(fontSize);+	if (tfPos >= 0) {+	  tok = (GString *)daToks->get(tfPos + 1);+	  tok->clear();+	  tok->appendf("{0:.2f}", fontSize);+	}+      }++      // compute text start position+      switch (quadding) {+      case fieldQuadLeft:+      default:+	x = border + 2;+	break;+      case fieldQuadCenter:+	x = border + 2 + 0.5 * (comb - text->getLength()) * w;+	break;+      case fieldQuadRight:+	x = border + 2 + (comb - text->getLength()) * w;+	break;+      }+      y = 0.5 * (yMax - yMin) - 0.4 * fontSize;++      // set the font matrix+      if (tmPos >= 0) {+	tok = (GString *)daToks->get(tmPos + 4);+	tok->clear();+	tok->appendf("{0:.2f}", x);+	tok = (GString *)daToks->get(tmPos + 5);+	tok->clear();+	tok->appendf("{0:.2f}", 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:.2f} {1:.2f} Tm\n", x, y);+      }++      // write the text string+      //~ this should center (instead of left-justify) each character within+      //~     its comb cell+      for (i = 0; i < text->getLength(); ++i) {+	if (i > 0) {+	  appearBuf->appendf("{0:.2f} 0 Td\n", w);+	}+	appearBuf->append('(');+	c = text->getChar(i) & 0xff;+	if (c == '(' || c == ')' || c == '\\') {+	  appearBuf->append('\\');+	  appearBuf->append(c);+	} else if (c < 0x20 || c >= 0x80) {+	  appearBuf->appendf("{0:.2f} 0 Td\n", w);+	} else {+	  appearBuf->append(c);+	}+	appearBuf->append(") Tj\n");+      }++    // regular (non-comb) formatting+    } else {++      // compute string width+      if (font && !font->isCIDFont()) {+	w = 0;+	for (i = 0; i < text->getLength(); ++i) {+	  w += ((Gfx8BitFont *)font)->getWidth(text->getChar(i));+	}+      } else {+	// otherwise, make a crude estimate+	w = text->getLength() * 0.5;+      }++      // compute font autosize+      if (fontSize == 0) {+	fontSize = yMax - yMin - 2 * border;+	fontSize2 = (xMax - xMin - 4 - 2 * border) / w;+	if (fontSize2 < fontSize) {+	  fontSize = fontSize2;+	}+	fontSize = floor(fontSize);+	if (tfPos >= 0) {+	  tok = (GString *)daToks->get(tfPos + 1);+	  tok->clear();+	  tok->appendf("{0:.2f}", fontSize);+	}+      }++      // compute text start position+      w *= fontSize;+      switch (quadding) {+      case fieldQuadLeft:+      default:+	x = border + 2;+	break;+      case fieldQuadCenter:+	x = (xMax - xMin - w) / 2;+	break;+      case fieldQuadRight:+	x = xMax - xMin - border - 2 - w;+	break;+      }+      y = 0.5 * (yMax - yMin) - 0.4 * fontSize;++      // set the font matrix+      if (tmPos >= 0) {+	tok = (GString *)daToks->get(tmPos + 4);+	tok->clear();+	tok->appendf("{0:.2f}", x);+	tok = (GString *)daToks->get(tmPos + 5);+	tok->clear();+	tok->appendf("{0:.2f}", 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:.2f} {1:.2f} Tm\n", x, y);+      }++      // write the text string+      appearBuf->append('(');+      for (i = 0; i < text->getLength(); ++i) {+	c = text->getChar(i) & 0xff;+	if (c == '(' || c == ')' || c == '\\') {+	  appearBuf->append('\\');+	  appearBuf->append(c);+	} else if (c < 0x20 || c >= 0x80) {+	  appearBuf->appendf("\\{0:03o}", c);+	} else {+	  appearBuf->append(c);+	}+      }+      appearBuf->append(") Tj\n");+    }+  }++  // cleanup+  appearBuf->append("ET\n");+  appearBuf->append("Q\n");+  if (txField) {+    appearBuf->append("EMC\n");+  }++  if (daToks) {+    deleteGList(daToks, GString);+  }+}++// Draw the variable text or caption for a field.+void Annot::drawListBox(GString **text, GBool *selection,+			int nOptions, int topIdx,+			GString *da, GfxFontDict *fontDict, GBool quadding) {+  GList *daToks;+  GString *tok;+  GfxFont *font;+  double fontSize, fontSize2, border, 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 = 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+  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(-1, "Unknown font in field's DA string");+      }+    } else {+      error(-1, "Invalid font name in 'Tf' operator in field's DA string");+    }+    tok = (GString *)daToks->get(tfPos + 1);+    fontSize = atof(tok->getCString());+  } else {+    error(-1, "Missing 'Tf' operator in field's DA string");+  }++  // get the border width+  border = borderStyle->getWidth();++  // 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 (tfPos >= 0) {+      tok = (GString *)daToks->get(tfPos + 1);+      tok->clear();+      tok->appendf("{0:.2f}", 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:.2f} {1:.2f} {2:.2f} {3:.2f} 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 fieldQuadLeft:+    default:+      x = border + 2;+      break;+    case fieldQuadCenter:+      x = (xMax - xMin - w) / 2;+      break;+    case fieldQuadRight:+      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:.2f}", x);+      tok = (GString *)daToks->get(tmPos + 5);+      tok->clear();+      tok->appendf("{0:.2f}", 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:.2f} {1:.2f} 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(c);+      } else if (c < 0x20 || c >= 0x80) {+	appearBuf->appendf("\\{0:03o}", c);+      } else {+	appearBuf->append(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 Annot::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(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>).+// If <fill> is true, the circle is filled; otherwise it is stroked.+void Annot::drawCircle(double cx, double cy, double r, GBool fill) {+  appearBuf->appendf("{0:.2f} {1:.2f} m\n",+		     cx + r, cy);+  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",+		     cx + r, cy + bezierCircle * r,+		     cx + bezierCircle * r, cy + r,+		     cx, cy + r);+  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",+		     cx - bezierCircle * r, cy + r,+		     cx - r, cy + bezierCircle * r,+		     cx - r, cy);+  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",+		     cx - r, cy - bezierCircle * r,+		     cx - bezierCircle * r, cy - r,+		     cx, cy - r);+  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n",+		     cx + bezierCircle * r, cy - r,+		     cx + r, cy - bezierCircle * r,+		     cx + r, cy);+  appearBuf->append(fill ? "f\n" : "s\n");+}++// 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:.2f} {1:.2f} m\n",+		     cx + r2, cy + r2);+  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} 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:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} 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:.2f} {1:.2f} m\n",+		     cx - r2, cy - r2);+  appearBuf->appendf("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} 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:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} 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");+}++// Look up an inheritable field dictionary entry.+Object *Annot::fieldLookup(Dict *field, char *key, Object *obj) {+  Dict *dict;+  Object parent;++  dict = field;+  if (!dict->lookup(key, obj)->isNull()) {+    return obj;+  }+  obj->free();+  if (dict->lookup("Parent", &parent)->isDict()) {+    fieldLookup(parent.getDict(), key, obj);+  } else {+    obj->initNull();+  }+  parent.free();+  return obj;+}++void Annot::draw(Gfx *gfx, GBool printing) {+  Object obj;+  GBool isLink;++  // check the flags+  if ((flags & annotFlagHidden) ||+      (printing && !(flags & annotFlagPrint)) ||+      (!printing && (flags & annotFlagNoView))) {+    return;+  }++  // draw the appearance stream+  isLink = type && !type->cmp("Link");+  appearance.fetch(xref, &obj);+  gfx->drawAnnot(&obj, isLink ? borderStyle : (AnnotBorderStyle *)NULL,+		 xMin, yMin, xMax, yMax);+  obj.free();+}++//------------------------------------------------------------------------+// Annots+//------------------------------------------------------------------------++Annots::Annots(XRef *xref, Catalog *catalog, Object *annotsObj) {+  Dict *acroForm;+  Annot *annot;+  Object obj1;+  Ref ref;+  int size;+  int i;++  annots = NULL;+  size = 0;+  nAnnots = 0;++  acroForm = catalog->getAcroForm()->isDict() ?+               catalog->getAcroForm()->getDict() : NULL;+  if (annotsObj->isArray()) {+    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()) {+	annot = new Annot(xref, acroForm, obj1.getDict(), &ref);+	if (annot->isOk()) {+	  if (nAnnots >= size) {+	    size += 16;+	    annots = (Annot **)greallocn(annots, size, sizeof(Annot *));+	  }+	  annots[nAnnots++] = annot;+	} else {+	  delete annot;+	}+      }+      obj1.free();+    }+  }+}++Annots::~Annots() {+  int i;++  for (i = 0; i < nAnnots; ++i) {+    delete annots[i];+  }+  gfree(annots);+}++void Annots::generateAppearances(Dict *acroForm) {+  Object obj1, obj2;+  Ref ref;+  int i;++  if (acroForm->lookup("Fields", &obj1)->isArray()) {+    for (i = 0; i < obj1.arrayGetLength(); ++i) {+      if (obj1.arrayGetNF(i, &obj2)->isRef()) {+	ref = obj2.getRef();+	obj2.free();+	obj1.arrayGet(i, &obj2);+      } else {+	ref.num = ref.gen = -1;+      }+      if (obj2.isDict()) {+	scanFieldAppearances(obj2.getDict(), &ref, NULL, acroForm);+      }+      obj2.free();+    }+  }+  obj1.free();+}++void Annots::scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,+				  Dict *acroForm) {+  Annot *annot;+  Object obj1, obj2;+  Ref ref2;+  int i;++  // non-terminal node: scan the children+  if (node->lookup("Kids", &obj1)->isArray()) {+    for (i = 0; i < obj1.arrayGetLength(); ++i) {+      if (obj1.arrayGetNF(i, &obj2)->isRef()) {+	ref2 = obj2.getRef();+	obj2.free();+	obj1.arrayGet(i, &obj2);+      } else {+	ref2.num = ref2.gen = -1;+      }+      if (obj2.isDict()) {+	scanFieldAppearances(obj2.getDict(), &ref2, node, acroForm);+      }+      obj2.free();+    }+    obj1.free();+    return;+  }+  obj1.free();++  // terminal node: this is either a combined annot/field dict, or an+  // annot dict whose parent is a field+  if ((annot = findAnnot(ref))) {+    node->lookupNF("Parent", &obj1);+    if (!parent || !obj1.isNull()) {+      annot->generateFieldAppearance(node, node, acroForm);+    } else {+      annot->generateFieldAppearance(parent, node, acroForm);+    }+    obj1.free();+  }+}++Annot *Annots::findAnnot(Ref *ref) {+  int i;++  for (i = 0; i < nAnnots; ++i) {+    if (annots[i]->match(ref)) {+      return annots[i];+    }+  }+  return NULL;+}
+ xpdf/Annot.h view
@@ -0,0 +1,142 @@+//========================================================================+//+// 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;++//------------------------------------------------------------------------+// AnnotBorderStyle+//------------------------------------------------------------------------++enum AnnotBorderType {+  annotBorderSolid,+  annotBorderDashed,+  annotBorderBeveled,+  annotBorderInset,+  annotBorderUnderlined+};++class AnnotBorderStyle {+public:++  AnnotBorderStyle(AnnotBorderType typeA, double widthA,+		   double *dashA, int dashLengthA,+		   double rA, double gA, double bA);+  ~AnnotBorderStyle();++  AnnotBorderType getType() { return type; }+  double getWidth() { return width; }+  void getDash(double **dashA, int *dashLengthA)+    { *dashA = dash; *dashLengthA = dashLength; }+  void getColor(double *rA, double *gA, double *bA)+    { *rA = r; *gA = g; *bA = b; }++private:++  AnnotBorderType type;+  double width;+  double *dash;+  int dashLength;+  double r, g, b;+};++//------------------------------------------------------------------------+// Annot+//------------------------------------------------------------------------++class Annot {+public:++  Annot(XRef *xrefA, Dict *acroForm, Dict *dict, Ref *refA);+  ~Annot();+  GBool isOk() { return ok; }++  void draw(Gfx *gfx, GBool printing);++  // 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 generateFieldAppearance(Dict *field, Dict *annot, Dict *acroForm);++private:+ +  void setColor(Array *a, GBool fill, int adjust);+  void drawText(GString *text, GString *da, GfxFontDict *fontDict,+		GBool multiline, int comb, int quadding,+		GBool txField, GBool forceZapfDingbats);+  void drawListBox(GString **text, GBool *selection,+		   int nOptions, int topIdx,+		   GString *da, GfxFontDict *fontDict, GBool quadding);+  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, GBool fill);+  void drawCircleTopLeft(double cx, double cy, double r);+  void drawCircleBottomRight(double cx, double cy, double r);+  Object *fieldLookup(Dict *field, char *key, Object *obj);++  XRef *xref;			// the xref table for this PDF file+  Ref ref;			// object ref identifying this annotation+  GString *type;		// annotation type+  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;+  GBool ok;+};++//------------------------------------------------------------------------+// Annots+//------------------------------------------------------------------------++class Annots {+public:++  // Build a list of Annot objects.+  Annots(XRef *xref, Catalog *catalog, Object *annotsObj);++  ~Annots();++  // Iterate through list of annotations.+  int getNumAnnots() { return nAnnots; }+  Annot *getAnnot(int i) { return annots[i]; }++  // (Re)generate the appearance streams for all annotations belonging+  // to a form field.+  void generateAppearances(Dict *acroForm);++private:++  void scanFieldAppearances(Dict *node, Ref *ref, Dict *parent,+			    Dict *acroForm);+  Annot *findAnnot(Ref *ref);++  Annot **annots;+  int nAnnots;+};++#endif
+ xpdf/Array.cc view
@@ -0,0 +1,73 @@+//========================================================================+//+// 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 "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) {+  if (i < 0 || i >= length) {+#ifdef DEBUG_MEM+    abort();+#else+    return obj->initNull();+#endif+  }+  return elems[i].fetch(xref, obj);+}++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);+}
+ xpdf/Array.h view
@@ -0,0 +1,58 @@+//========================================================================+//+// Array.h+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef ARRAY_H+#define ARRAY_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "Object.h"++class XRef;++//------------------------------------------------------------------------+// Array+//------------------------------------------------------------------------++class Array {+public:++  // Constructor.+  Array(XRef *xrefA);++  // Destructor.+  ~Array();++  // Reference counting.+  int incRef() { return ++ref; }+  int decRef() { return --ref; }++  // Get number of elements.+  int getLength() { return length; }++  // Add an element.+  void add(Object *elem);++  // Accessors.+  Object *get(int i, Object *obj);+  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+  int ref;			// reference count+};++#endif
+ xpdf/BuiltinFont.cc view
@@ -0,0 +1,65 @@+//========================================================================+//+// 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 "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(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;+    }+  }+  return gFalse;+}++int BuiltinFontWidths::hash(char *name) {+  char *p;+  unsigned int h;++  h = 0;+  for (p = name; *p; ++p) {+    h = 17 * h + (int)(*p & 0xff);+  }+  return (int)(h % size);+}
+ xpdf/BuiltinFont.h view
@@ -0,0 +1,57 @@+//========================================================================+//+// 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 {+  char *name;+  char **defaultBaseEnc;+  short ascent;+  short descent;+  short bbox[4];+  BuiltinFontWidths *widths;+};++//------------------------------------------------------------------------++struct BuiltinFontWidth {+  char *name;+  Gushort width;+  BuiltinFontWidth *next;+};++class BuiltinFontWidths {+public:++  BuiltinFontWidths(BuiltinFontWidth *widths, int sizeA);+  ~BuiltinFontWidths();+  GBool getWidth(char *name, Gushort *width);++private:++  int hash(char *name);++  BuiltinFontWidth **tab;+  int size;+};++#endif
+ xpdf/BuiltinFontTables.cc view
@@ -0,0 +1,4284 @@+//========================================================================+//+// BuiltinFontTables.cc+//+// Copyright 2001-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>+#include <stdlib.h>+#include "FontEncodingTables.h"+#include "BuiltinFontTables.h"++static BuiltinFontWidth courierWidthsTab[] = {+  { "Ntilde",                            600, NULL },+  { "rcaron",                            600, NULL },+  { "kcommaaccent",                      600, NULL },+  { "Ncommaaccent",                      600, NULL },+  { "Zacute",                            600, NULL },+  { "comma",                             600, NULL },+  { "cedilla",                           600, NULL },+  { "plusminus",                         600, NULL },+  { "circumflex",                        600, NULL },+  { "dotaccent",                         600, NULL },+  { "edotaccent",                        600, NULL },+  { "asciitilde",                        600, NULL },+  { "colon",                             600, NULL },+  { "onehalf",                           600, NULL },+  { "dollar",                            600, NULL },+  { "Lcaron",                            600, NULL },+  { "ntilde",                            600, NULL },+  { "Aogonek",                           600, NULL },+  { "ncommaaccent",                      600, NULL },+  { "minus",                             600, NULL },+  { "Iogonek",                           600, NULL },+  { "zacute",                            600, NULL },+  { "yen",                               600, NULL },+  { "space",                             600, NULL },+  { "Omacron",                           600, NULL },+  { "questiondown",                      600, NULL },+  { "emdash",                            600, NULL },+  { "Agrave",                            600, NULL },+  { "three",                             600, NULL },+  { "numbersign",                        600, NULL },+  { "lcaron",                            600, NULL },+  { "A",                                 600, NULL },+  { "B",                                 600, NULL },+  { "C",                                 600, NULL },+  { "aogonek",                           600, NULL },+  { "D",                                 600, NULL },+  { "E",                                 600, NULL },+  { "onequarter",                        600, NULL },+  { "F",                                 600, NULL },+  { "G",                                 600, NULL },+  { "H",                                 600, NULL },+  { "I",                                 600, NULL },+  { "J",                                 600, NULL },+  { "K",                                 600, NULL },+  { "iogonek",                           600, NULL },+  { "L",                                 600, NULL },+  { "backslash",                         600, NULL },+  { "periodcentered",                    600, NULL },+  { "M",                                 600, NULL },+  { "N",                                 600, NULL },+  { "omacron",                           600, NULL },+  { "Tcommaaccent",                      600, NULL },+  { "O",                                 600, NULL },+  { "P",                                 600, NULL },+  { "Q",                                 600, NULL },+  { "Uhungarumlaut",                     600, NULL },+  { "R",                                 600, NULL },+  { "Aacute",                            600, NULL },+  { "caron",                             600, NULL },+  { "S",                                 600, NULL },+  { "T",                                 600, NULL },+  { "U",                                 600, NULL },+  { "agrave",                            600, NULL },+  { "V",                                 600, NULL },+  { "W",                                 600, NULL },+  { "equal",                             600, NULL },+  { "question",                          600, NULL },+  { "X",                                 600, NULL },+  { "Y",                                 600, NULL },+  { "Z",                                 600, NULL },+  { "four",                              600, NULL },+  { "a",                                 600, NULL },+  { "Gcommaaccent",                      600, NULL },+  { "b",                                 600, NULL },+  { "c",                                 600, NULL },+  { "d",                                 600, NULL },+  { "e",                                 600, NULL },+  { "f",                                 600, NULL },+  { "g",                                 600, NULL },+  { "bullet",                            600, NULL },+  { "h",                                 600, NULL },+  { "i",                                 600, NULL },+  { "Oslash",                            600, NULL },+  { "dagger",                            600, NULL },+  { "j",                                 600, NULL },+  { "k",                                 600, NULL },+  { "l",                                 600, NULL },+  { "m",                                 600, NULL },+  { "n",                                 600, NULL },+  { "tcommaaccent",                      600, NULL },+  { "o",                                 600, NULL },+  { "ordfeminine",                       600, NULL },+  { "ring",                              600, NULL },+  { "p",                                 600, NULL },+  { "q",                                 600, NULL },+  { "uhungarumlaut",                     600, NULL },+  { "r",                                 600, NULL },+  { "twosuperior",                       600, NULL },+  { "aacute",                            600, NULL },+  { "s",                                 600, NULL },+  { "OE",                                600, NULL },+  { "t",                                 600, NULL },+  { "divide",                            600, NULL },+  { "u",                                 600, NULL },+  { "Ccaron",                            600, NULL },+  { "v",                                 600, NULL },+  { "w",                                 600, NULL },+  { "x",                                 600, NULL },+  { "y",                                 600, NULL },+  { "z",                                 600, NULL },+  { "Gbreve",                            600, NULL },+  { "commaaccent",                       600, NULL },+  { "hungarumlaut",                      600, NULL },+  { "Idotaccent",                        600, NULL },+  { "Nacute",                            600, NULL },+  { "quotedbl",                          600, NULL },+  { "gcommaaccent",                      600, NULL },+  { "mu",                                600, NULL },+  { "greaterequal",                      600, NULL },+  { "Scaron",                            600, NULL },+  { "Lslash",                            600, NULL },+  { "semicolon",                         600, NULL },+  { "oslash",                            600, NULL },+  { "lessequal",                         600, NULL },+  { "lozenge",                           600, NULL },+  { "parenright",                        600, NULL },+  { "ccaron",                            600, NULL },+  { "Ecircumflex",                       600, NULL },+  { "gbreve",                            600, NULL },+  { "trademark",                         600, NULL },+  { "daggerdbl",                         600, NULL },+  { "nacute",                            600, NULL },+  { "macron",                            600, NULL },+  { "Otilde",                            600, NULL },+  { "Emacron",                           600, NULL },+  { "ellipsis",                          600, NULL },+  { "scaron",                            600, NULL },+  { "AE",                                600, NULL },+  { "Ucircumflex",                       600, NULL },+  { "lslash",                            600, NULL },+  { "quotedblleft",                      600, NULL },+  { "hyphen",                            600, NULL },+  { "guilsinglright",                    600, NULL },+  { "quotesingle",                       600, NULL },+  { "eight",                             600, NULL },+  { "exclamdown",                        600, NULL },+  { "endash",                            600, NULL },+  { "oe",                                600, NULL },+  { "Abreve",                            600, NULL },+  { "Umacron",                           600, NULL },+  { "ecircumflex",                       600, NULL },+  { "Adieresis",                         600, NULL },+  { "copyright",                         600, NULL },+  { "Egrave",                            600, NULL },+  { "slash",                             600, NULL },+  { "Edieresis",                         600, NULL },+  { "otilde",                            600, NULL },+  { "Idieresis",                         600, NULL },+  { "parenleft",                         600, NULL },+  { "one",                               600, NULL },+  { "emacron",                           600, NULL },+  { "Odieresis",                         600, NULL },+  { "ucircumflex",                       600, NULL },+  { "bracketleft",                       600, NULL },+  { "Ugrave",                            600, NULL },+  { "quoteright",                        600, NULL },+  { "Udieresis",                         600, NULL },+  { "perthousand",                       600, NULL },+  { "Ydieresis",                         600, NULL },+  { "umacron",                           600, NULL },+  { "abreve",                            600, NULL },+  { "Eacute",                            600, NULL },+  { "adieresis",                         600, NULL },+  { "egrave",                            600, NULL },+  { "edieresis",                         600, NULL },+  { "idieresis",                         600, NULL },+  { "Eth",                               600, NULL },+  { "ae",                                600, NULL },+  { "asterisk",                          600, NULL },+  { "odieresis",                         600, NULL },+  { "Uacute",                            600, NULL },+  { "ugrave",                            600, NULL },+  { "five",                              600, NULL },+  { "nine",                              600, NULL },+  { "udieresis",                         600, NULL },+  { "Zcaron",                            600, NULL },+  { "Scommaaccent",                      600, NULL },+  { "threequarters",                     600, NULL },+  { "guillemotright",                    600, NULL },+  { "Ccedilla",                          600, NULL },+  { "ydieresis",                         600, NULL },+  { "tilde",                             600, NULL },+  { "at",                                600, NULL },+  { "eacute",                            600, NULL },+  { "underscore",                        600, NULL },+  { "Euro",                              600, NULL },+  { "Dcroat",                            600, NULL },+  { "zero",                              600, NULL },+  { "multiply",                          600, NULL },+  { "eth",                               600, NULL },+  { "Scedilla",                          600, NULL },+  { "Racute",                            600, NULL },+  { "Ograve",                            600, NULL },+  { "partialdiff",                       600, NULL },+  { "uacute",                            600, NULL },+  { "braceleft",                         600, NULL },+  { "Thorn",                             600, NULL },+  { "zcaron",                            600, NULL },+  { "scommaaccent",                      600, NULL },+  { "ccedilla",                          600, NULL },+  { "Dcaron",                            600, NULL },+  { "dcroat",                            600, NULL },+  { "scedilla",                          600, NULL },+  { "Oacute",                            600, NULL },+  { "Ocircumflex",                       600, NULL },+  { "ogonek",                            600, NULL },+  { "ograve",                            600, NULL },+  { "racute",                            600, NULL },+  { "Tcaron",                            600, NULL },+  { "Eogonek",                           600, NULL },+  { "thorn",                             600, NULL },+  { "degree",                            600, NULL },+  { "registered",                        600, NULL },+  { "radical",                           600, NULL },+  { "Aring",                             600, NULL },+  { "percent",                           600, NULL },+  { "six",                               600, NULL },+  { "paragraph",                         600, NULL },+  { "dcaron",                            600, NULL },+  { "Uogonek",                           600, NULL },+  { "two",                               600, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            600, NULL },+  { "Lacute",                            600, NULL },+  { "ocircumflex",                       600, NULL },+  { "oacute",                            600, NULL },+  { "Uring",                             600, NULL },+  { "Lcommaaccent",                      600, NULL },+  { "tcaron",                            600, NULL },+  { "eogonek",                           600, NULL },+  { "Delta",                             600, NULL },+  { "Ohungarumlaut",                     600, NULL },+  { "asciicircum",                       600, NULL },+  { "aring",                             600, NULL },+  { "grave",                             600, NULL },+  { "uogonek",                           600, NULL },+  { "bracketright",                      600, NULL },+  { "ampersand",                         600, NULL },+  { "Iacute",                            600, NULL },+  { "lacute",                            600, NULL },+  { "igrave",                            600, NULL },+  { "Ncaron",                            600, NULL },+  { "plus",                              600, NULL },+  { "uring",                             600, NULL },+  { "quotesinglbase",                    600, NULL },+  { "lcommaaccent",                      600, NULL },+  { "Yacute",                            600, NULL },+  { "ohungarumlaut",                     600, NULL },+  { "threesuperior",                     600, NULL },+  { "acute",                             600, NULL },+  { "section",                           600, NULL },+  { "dieresis",                          600, NULL },+  { "quotedblbase",                      600, NULL },+  { "iacute",                            600, NULL },+  { "ncaron",                            600, NULL },+  { "florin",                            600, NULL },+  { "yacute",                            600, NULL },+  { "Rcommaaccent",                      600, NULL },+  { "fi",                                600, NULL },+  { "fl",                                600, NULL },+  { "Acircumflex",                       600, NULL },+  { "Cacute",                            600, NULL },+  { "Icircumflex",                       600, NULL },+  { "guillemotleft",                     600, NULL },+  { "germandbls",                        600, NULL },+  { "seven",                             600, NULL },+  { "Amacron",                           600, NULL },+  { "Sacute",                            600, NULL },+  { "ordmasculine",                      600, NULL },+  { "dotlessi",                          600, NULL },+  { "sterling",                          600, NULL },+  { "notequal",                          600, NULL },+  { "Imacron",                           600, NULL },+  { "rcommaaccent",                      600, NULL },+  { "Zdotaccent",                        600, NULL },+  { "acircumflex",                       600, NULL },+  { "cacute",                            600, NULL },+  { "Ecaron",                            600, NULL },+  { "braceright",                        600, NULL },+  { "icircumflex",                       600, NULL },+  { "quotedblright",                     600, NULL },+  { "amacron",                           600, NULL },+  { "sacute",                            600, NULL },+  { "imacron",                           600, NULL },+  { "cent",                              600, NULL },+  { "currency",                          600, NULL },+  { "logicalnot",                        600, NULL },+  { "zdotaccent",                        600, NULL },+  { "Atilde",                            600, NULL },+  { "breve",                             600, NULL },+  { "bar",                               600, NULL },+  { "fraction",                          600, NULL },+  { "less",                              600, NULL },+  { "ecaron",                            600, NULL },+  { "guilsinglleft",                     600, NULL },+  { "exclam",                            600, NULL },+  { "period",                            600, NULL },+  { "Rcaron",                            600, NULL },+  { "Kcommaaccent",                      600, NULL },+  { "greater",                           600, NULL },+  { "atilde",                            600, NULL },+  { "brokenbar",                         600, NULL },+  { "quoteleft",                         600, NULL },+  { "Edotaccent",                        600, NULL },+  { "onesuperior",                       600, NULL }+};++static BuiltinFontWidth courierBoldWidthsTab[] = {+  { "Ntilde",                            600, NULL },+  { "rcaron",                            600, NULL },+  { "kcommaaccent",                      600, NULL },+  { "Ncommaaccent",                      600, NULL },+  { "Zacute",                            600, NULL },+  { "comma",                             600, NULL },+  { "cedilla",                           600, NULL },+  { "plusminus",                         600, NULL },+  { "circumflex",                        600, NULL },+  { "dotaccent",                         600, NULL },+  { "edotaccent",                        600, NULL },+  { "asciitilde",                        600, NULL },+  { "colon",                             600, NULL },+  { "onehalf",                           600, NULL },+  { "dollar",                            600, NULL },+  { "Lcaron",                            600, NULL },+  { "ntilde",                            600, NULL },+  { "Aogonek",                           600, NULL },+  { "ncommaaccent",                      600, NULL },+  { "minus",                             600, NULL },+  { "Iogonek",                           600, NULL },+  { "zacute",                            600, NULL },+  { "yen",                               600, NULL },+  { "space",                             600, NULL },+  { "Omacron",                           600, NULL },+  { "questiondown",                      600, NULL },+  { "emdash",                            600, NULL },+  { "Agrave",                            600, NULL },+  { "three",                             600, NULL },+  { "numbersign",                        600, NULL },+  { "lcaron",                            600, NULL },+  { "A",                                 600, NULL },+  { "B",                                 600, NULL },+  { "C",                                 600, NULL },+  { "aogonek",                           600, NULL },+  { "D",                                 600, NULL },+  { "E",                                 600, NULL },+  { "onequarter",                        600, NULL },+  { "F",                                 600, NULL },+  { "G",                                 600, NULL },+  { "H",                                 600, NULL },+  { "I",                                 600, NULL },+  { "J",                                 600, NULL },+  { "K",                                 600, NULL },+  { "iogonek",                           600, NULL },+  { "backslash",                         600, NULL },+  { "L",                                 600, NULL },+  { "periodcentered",                    600, NULL },+  { "M",                                 600, NULL },+  { "N",                                 600, NULL },+  { "omacron",                           600, NULL },+  { "Tcommaaccent",                      600, NULL },+  { "O",                                 600, NULL },+  { "P",                                 600, NULL },+  { "Q",                                 600, NULL },+  { "Uhungarumlaut",                     600, NULL },+  { "R",                                 600, NULL },+  { "Aacute",                            600, NULL },+  { "caron",                             600, NULL },+  { "S",                                 600, NULL },+  { "T",                                 600, NULL },+  { "U",                                 600, NULL },+  { "agrave",                            600, NULL },+  { "V",                                 600, NULL },+  { "W",                                 600, NULL },+  { "X",                                 600, NULL },+  { "question",                          600, NULL },+  { "equal",                             600, NULL },+  { "Y",                                 600, NULL },+  { "Z",                                 600, NULL },+  { "four",                              600, NULL },+  { "a",                                 600, NULL },+  { "Gcommaaccent",                      600, NULL },+  { "b",                                 600, NULL },+  { "c",                                 600, NULL },+  { "d",                                 600, NULL },+  { "e",                                 600, NULL },+  { "f",                                 600, NULL },+  { "g",                                 600, NULL },+  { "bullet",                            600, NULL },+  { "h",                                 600, NULL },+  { "i",                                 600, NULL },+  { "Oslash",                            600, NULL },+  { "dagger",                            600, NULL },+  { "j",                                 600, NULL },+  { "k",                                 600, NULL },+  { "l",                                 600, NULL },+  { "m",                                 600, NULL },+  { "n",                                 600, NULL },+  { "tcommaaccent",                      600, NULL },+  { "o",                                 600, NULL },+  { "ordfeminine",                       600, NULL },+  { "ring",                              600, NULL },+  { "p",                                 600, NULL },+  { "q",                                 600, NULL },+  { "uhungarumlaut",                     600, NULL },+  { "r",                                 600, NULL },+  { "twosuperior",                       600, NULL },+  { "aacute",                            600, NULL },+  { "s",                                 600, NULL },+  { "OE",                                600, NULL },+  { "t",                                 600, NULL },+  { "divide",                            600, NULL },+  { "u",                                 600, NULL },+  { "Ccaron",                            600, NULL },+  { "v",                                 600, NULL },+  { "w",                                 600, NULL },+  { "x",                                 600, NULL },+  { "y",                                 600, NULL },+  { "z",                                 600, NULL },+  { "Gbreve",                            600, NULL },+  { "commaaccent",                       600, NULL },+  { "hungarumlaut",                      600, NULL },+  { "Idotaccent",                        600, NULL },+  { "Nacute",                            600, NULL },+  { "quotedbl",                          600, NULL },+  { "gcommaaccent",                      600, NULL },+  { "mu",                                600, NULL },+  { "greaterequal",                      600, NULL },+  { "Scaron",                            600, NULL },+  { "Lslash",                            600, NULL },+  { "semicolon",                         600, NULL },+  { "oslash",                            600, NULL },+  { "lessequal",                         600, NULL },+  { "lozenge",                           600, NULL },+  { "parenright",                        600, NULL },+  { "ccaron",                            600, NULL },+  { "Ecircumflex",                       600, NULL },+  { "gbreve",                            600, NULL },+  { "trademark",                         600, NULL },+  { "daggerdbl",                         600, NULL },+  { "nacute",                            600, NULL },+  { "macron",                            600, NULL },+  { "Otilde",                            600, NULL },+  { "Emacron",                           600, NULL },+  { "ellipsis",                          600, NULL },+  { "scaron",                            600, NULL },+  { "AE",                                600, NULL },+  { "Ucircumflex",                       600, NULL },+  { "lslash",                            600, NULL },+  { "quotedblleft",                      600, NULL },+  { "guilsinglright",                    600, NULL },+  { "hyphen",                            600, NULL },+  { "quotesingle",                       600, NULL },+  { "eight",                             600, NULL },+  { "exclamdown",                        600, NULL },+  { "endash",                            600, NULL },+  { "oe",                                600, NULL },+  { "Abreve",                            600, NULL },+  { "Umacron",                           600, NULL },+  { "ecircumflex",                       600, NULL },+  { "Adieresis",                         600, NULL },+  { "copyright",                         600, NULL },+  { "Egrave",                            600, NULL },+  { "slash",                             600, NULL },+  { "Edieresis",                         600, NULL },+  { "otilde",                            600, NULL },+  { "Idieresis",                         600, NULL },+  { "parenleft",                         600, NULL },+  { "one",                               600, NULL },+  { "emacron",                           600, NULL },+  { "Odieresis",                         600, NULL },+  { "ucircumflex",                       600, NULL },+  { "bracketleft",                       600, NULL },+  { "Ugrave",                            600, NULL },+  { "quoteright",                        600, NULL },+  { "Udieresis",                         600, NULL },+  { "perthousand",                       600, NULL },+  { "Ydieresis",                         600, NULL },+  { "umacron",                           600, NULL },+  { "abreve",                            600, NULL },+  { "Eacute",                            600, NULL },+  { "adieresis",                         600, NULL },+  { "egrave",                            600, NULL },+  { "edieresis",                         600, NULL },+  { "idieresis",                         600, NULL },+  { "Eth",                               600, NULL },+  { "ae",                                600, NULL },+  { "asterisk",                          600, NULL },+  { "odieresis",                         600, NULL },+  { "Uacute",                            600, NULL },+  { "ugrave",                            600, NULL },+  { "nine",                              600, NULL },+  { "five",                              600, NULL },+  { "udieresis",                         600, NULL },+  { "Zcaron",                            600, NULL },+  { "Scommaaccent",                      600, NULL },+  { "threequarters",                     600, NULL },+  { "guillemotright",                    600, NULL },+  { "Ccedilla",                          600, NULL },+  { "ydieresis",                         600, NULL },+  { "tilde",                             600, NULL },+  { "at",                                600, NULL },+  { "eacute",                            600, NULL },+  { "underscore",                        600, NULL },+  { "Euro",                              600, NULL },+  { "Dcroat",                            600, NULL },+  { "multiply",                          600, NULL },+  { "zero",                              600, NULL },+  { "eth",                               600, NULL },+  { "Scedilla",                          600, NULL },+  { "Ograve",                            600, NULL },+  { "Racute",                            600, NULL },+  { "partialdiff",                       600, NULL },+  { "uacute",                            600, NULL },+  { "braceleft",                         600, NULL },+  { "Thorn",                             600, NULL },+  { "zcaron",                            600, NULL },+  { "scommaaccent",                      600, NULL },+  { "ccedilla",                          600, NULL },+  { "Dcaron",                            600, NULL },+  { "dcroat",                            600, NULL },+  { "Ocircumflex",                       600, NULL },+  { "Oacute",                            600, NULL },+  { "scedilla",                          600, NULL },+  { "ogonek",                            600, NULL },+  { "ograve",                            600, NULL },+  { "racute",                            600, NULL },+  { "Tcaron",                            600, NULL },+  { "Eogonek",                           600, NULL },+  { "thorn",                             600, NULL },+  { "degree",                            600, NULL },+  { "registered",                        600, NULL },+  { "radical",                           600, NULL },+  { "Aring",                             600, NULL },+  { "percent",                           600, NULL },+  { "six",                               600, NULL },+  { "paragraph",                         600, NULL },+  { "dcaron",                            600, NULL },+  { "Uogonek",                           600, NULL },+  { "two",                               600, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            600, NULL },+  { "Lacute",                            600, NULL },+  { "ocircumflex",                       600, NULL },+  { "oacute",                            600, NULL },+  { "Uring",                             600, NULL },+  { "Lcommaaccent",                      600, NULL },+  { "tcaron",                            600, NULL },+  { "eogonek",                           600, NULL },+  { "Delta",                             600, NULL },+  { "Ohungarumlaut",                     600, NULL },+  { "asciicircum",                       600, NULL },+  { "aring",                             600, NULL },+  { "grave",                             600, NULL },+  { "uogonek",                           600, NULL },+  { "bracketright",                      600, NULL },+  { "Iacute",                            600, NULL },+  { "ampersand",                         600, NULL },+  { "igrave",                            600, NULL },+  { "lacute",                            600, NULL },+  { "Ncaron",                            600, NULL },+  { "plus",                              600, NULL },+  { "uring",                             600, NULL },+  { "quotesinglbase",                    600, NULL },+  { "lcommaaccent",                      600, NULL },+  { "Yacute",                            600, NULL },+  { "ohungarumlaut",                     600, NULL },+  { "threesuperior",                     600, NULL },+  { "acute",                             600, NULL },+  { "section",                           600, NULL },+  { "dieresis",                          600, NULL },+  { "iacute",                            600, NULL },+  { "quotedblbase",                      600, NULL },+  { "ncaron",                            600, NULL },+  { "florin",                            600, NULL },+  { "yacute",                            600, NULL },+  { "Rcommaaccent",                      600, NULL },+  { "fi",                                600, NULL },+  { "fl",                                600, NULL },+  { "Acircumflex",                       600, NULL },+  { "Cacute",                            600, NULL },+  { "Icircumflex",                       600, NULL },+  { "guillemotleft",                     600, NULL },+  { "germandbls",                        600, NULL },+  { "Amacron",                           600, NULL },+  { "seven",                             600, NULL },+  { "Sacute",                            600, NULL },+  { "ordmasculine",                      600, NULL },+  { "dotlessi",                          600, NULL },+  { "sterling",                          600, NULL },+  { "notequal",                          600, NULL },+  { "Imacron",                           600, NULL },+  { "rcommaaccent",                      600, NULL },+  { "Zdotaccent",                        600, NULL },+  { "acircumflex",                       600, NULL },+  { "cacute",                            600, NULL },+  { "Ecaron",                            600, NULL },+  { "icircumflex",                       600, NULL },+  { "braceright",                        600, NULL },+  { "quotedblright",                     600, NULL },+  { "amacron",                           600, NULL },+  { "sacute",                            600, NULL },+  { "imacron",                           600, NULL },+  { "cent",                              600, NULL },+  { "currency",                          600, NULL },+  { "logicalnot",                        600, NULL },+  { "zdotaccent",                        600, NULL },+  { "Atilde",                            600, NULL },+  { "breve",                             600, NULL },+  { "bar",                               600, NULL },+  { "fraction",                          600, NULL },+  { "less",                              600, NULL },+  { "ecaron",                            600, NULL },+  { "guilsinglleft",                     600, NULL },+  { "exclam",                            600, NULL },+  { "period",                            600, NULL },+  { "Rcaron",                            600, NULL },+  { "Kcommaaccent",                      600, NULL },+  { "greater",                           600, NULL },+  { "atilde",                            600, NULL },+  { "brokenbar",                         600, NULL },+  { "quoteleft",                         600, NULL },+  { "Edotaccent",                        600, NULL },+  { "onesuperior",                       600, NULL }+};++static BuiltinFontWidth courierBoldObliqueWidthsTab[] = {+  { "Ntilde",                            600, NULL },+  { "rcaron",                            600, NULL },+  { "kcommaaccent",                      600, NULL },+  { "Ncommaaccent",                      600, NULL },+  { "Zacute",                            600, NULL },+  { "comma",                             600, NULL },+  { "cedilla",                           600, NULL },+  { "plusminus",                         600, NULL },+  { "circumflex",                        600, NULL },+  { "dotaccent",                         600, NULL },+  { "edotaccent",                        600, NULL },+  { "asciitilde",                        600, NULL },+  { "colon",                             600, NULL },+  { "onehalf",                           600, NULL },+  { "dollar",                            600, NULL },+  { "Lcaron",                            600, NULL },+  { "ntilde",                            600, NULL },+  { "Aogonek",                           600, NULL },+  { "ncommaaccent",                      600, NULL },+  { "minus",                             600, NULL },+  { "Iogonek",                           600, NULL },+  { "zacute",                            600, NULL },+  { "yen",                               600, NULL },+  { "space",                             600, NULL },+  { "Omacron",                           600, NULL },+  { "questiondown",                      600, NULL },+  { "emdash",                            600, NULL },+  { "Agrave",                            600, NULL },+  { "three",                             600, NULL },+  { "numbersign",                        600, NULL },+  { "lcaron",                            600, NULL },+  { "A",                                 600, NULL },+  { "B",                                 600, NULL },+  { "C",                                 600, NULL },+  { "aogonek",                           600, NULL },+  { "D",                                 600, NULL },+  { "E",                                 600, NULL },+  { "onequarter",                        600, NULL },+  { "F",                                 600, NULL },+  { "G",                                 600, NULL },+  { "H",                                 600, NULL },+  { "I",                                 600, NULL },+  { "J",                                 600, NULL },+  { "K",                                 600, NULL },+  { "iogonek",                           600, NULL },+  { "backslash",                         600, NULL },+  { "L",                                 600, NULL },+  { "periodcentered",                    600, NULL },+  { "M",                                 600, NULL },+  { "N",                                 600, NULL },+  { "omacron",                           600, NULL },+  { "Tcommaaccent",                      600, NULL },+  { "O",                                 600, NULL },+  { "P",                                 600, NULL },+  { "Q",                                 600, NULL },+  { "Uhungarumlaut",                     600, NULL },+  { "R",                                 600, NULL },+  { "Aacute",                            600, NULL },+  { "caron",                             600, NULL },+  { "S",                                 600, NULL },+  { "T",                                 600, NULL },+  { "U",                                 600, NULL },+  { "agrave",                            600, NULL },+  { "V",                                 600, NULL },+  { "W",                                 600, NULL },+  { "X",                                 600, NULL },+  { "question",                          600, NULL },+  { "equal",                             600, NULL },+  { "Y",                                 600, NULL },+  { "Z",                                 600, NULL },+  { "four",                              600, NULL },+  { "a",                                 600, NULL },+  { "Gcommaaccent",                      600, NULL },+  { "b",                                 600, NULL },+  { "c",                                 600, NULL },+  { "d",                                 600, NULL },+  { "e",                                 600, NULL },+  { "f",                                 600, NULL },+  { "g",                                 600, NULL },+  { "bullet",                            600, NULL },+  { "h",                                 600, NULL },+  { "i",                                 600, NULL },+  { "Oslash",                            600, NULL },+  { "dagger",                            600, NULL },+  { "j",                                 600, NULL },+  { "k",                                 600, NULL },+  { "l",                                 600, NULL },+  { "m",                                 600, NULL },+  { "n",                                 600, NULL },+  { "tcommaaccent",                      600, NULL },+  { "o",                                 600, NULL },+  { "ordfeminine",                       600, NULL },+  { "ring",                              600, NULL },+  { "p",                                 600, NULL },+  { "q",                                 600, NULL },+  { "uhungarumlaut",                     600, NULL },+  { "r",                                 600, NULL },+  { "twosuperior",                       600, NULL },+  { "aacute",                            600, NULL },+  { "s",                                 600, NULL },+  { "OE",                                600, NULL },+  { "t",                                 600, NULL },+  { "divide",                            600, NULL },+  { "u",                                 600, NULL },+  { "Ccaron",                            600, NULL },+  { "v",                                 600, NULL },+  { "w",                                 600, NULL },+  { "x",                                 600, NULL },+  { "y",                                 600, NULL },+  { "z",                                 600, NULL },+  { "Gbreve",                            600, NULL },+  { "commaaccent",                       600, NULL },+  { "hungarumlaut",                      600, NULL },+  { "Idotaccent",                        600, NULL },+  { "Nacute",                            600, NULL },+  { "quotedbl",                          600, NULL },+  { "gcommaaccent",                      600, NULL },+  { "mu",                                600, NULL },+  { "greaterequal",                      600, NULL },+  { "Scaron",                            600, NULL },+  { "Lslash",                            600, NULL },+  { "semicolon",                         600, NULL },+  { "oslash",                            600, NULL },+  { "lessequal",                         600, NULL },+  { "lozenge",                           600, NULL },+  { "parenright",                        600, NULL },+  { "ccaron",                            600, NULL },+  { "Ecircumflex",                       600, NULL },+  { "gbreve",                            600, NULL },+  { "trademark",                         600, NULL },+  { "daggerdbl",                         600, NULL },+  { "nacute",                            600, NULL },+  { "macron",                            600, NULL },+  { "Otilde",                            600, NULL },+  { "Emacron",                           600, NULL },+  { "ellipsis",                          600, NULL },+  { "scaron",                            600, NULL },+  { "AE",                                600, NULL },+  { "Ucircumflex",                       600, NULL },+  { "lslash",                            600, NULL },+  { "quotedblleft",                      600, NULL },+  { "guilsinglright",                    600, NULL },+  { "hyphen",                            600, NULL },+  { "quotesingle",                       600, NULL },+  { "eight",                             600, NULL },+  { "exclamdown",                        600, NULL },+  { "endash",                            600, NULL },+  { "oe",                                600, NULL },+  { "Abreve",                            600, NULL },+  { "Umacron",                           600, NULL },+  { "ecircumflex",                       600, NULL },+  { "Adieresis",                         600, NULL },+  { "copyright",                         600, NULL },+  { "Egrave",                            600, NULL },+  { "slash",                             600, NULL },+  { "Edieresis",                         600, NULL },+  { "otilde",                            600, NULL },+  { "Idieresis",                         600, NULL },+  { "parenleft",                         600, NULL },+  { "one",                               600, NULL },+  { "emacron",                           600, NULL },+  { "Odieresis",                         600, NULL },+  { "ucircumflex",                       600, NULL },+  { "bracketleft",                       600, NULL },+  { "Ugrave",                            600, NULL },+  { "quoteright",                        600, NULL },+  { "Udieresis",                         600, NULL },+  { "perthousand",                       600, NULL },+  { "Ydieresis",                         600, NULL },+  { "umacron",                           600, NULL },+  { "abreve",                            600, NULL },+  { "Eacute",                            600, NULL },+  { "adieresis",                         600, NULL },+  { "egrave",                            600, NULL },+  { "edieresis",                         600, NULL },+  { "idieresis",                         600, NULL },+  { "Eth",                               600, NULL },+  { "ae",                                600, NULL },+  { "asterisk",                          600, NULL },+  { "odieresis",                         600, NULL },+  { "Uacute",                            600, NULL },+  { "ugrave",                            600, NULL },+  { "nine",                              600, NULL },+  { "five",                              600, NULL },+  { "udieresis",                         600, NULL },+  { "Zcaron",                            600, NULL },+  { "Scommaaccent",                      600, NULL },+  { "threequarters",                     600, NULL },+  { "guillemotright",                    600, NULL },+  { "Ccedilla",                          600, NULL },+  { "ydieresis",                         600, NULL },+  { "tilde",                             600, NULL },+  { "at",                                600, NULL },+  { "eacute",                            600, NULL },+  { "underscore",                        600, NULL },+  { "Euro",                              600, NULL },+  { "Dcroat",                            600, NULL },+  { "multiply",                          600, NULL },+  { "zero",                              600, NULL },+  { "eth",                               600, NULL },+  { "Scedilla",                          600, NULL },+  { "Ograve",                            600, NULL },+  { "Racute",                            600, NULL },+  { "partialdiff",                       600, NULL },+  { "uacute",                            600, NULL },+  { "braceleft",                         600, NULL },+  { "Thorn",                             600, NULL },+  { "zcaron",                            600, NULL },+  { "scommaaccent",                      600, NULL },+  { "ccedilla",                          600, NULL },+  { "Dcaron",                            600, NULL },+  { "dcroat",                            600, NULL },+  { "Ocircumflex",                       600, NULL },+  { "Oacute",                            600, NULL },+  { "scedilla",                          600, NULL },+  { "ogonek",                            600, NULL },+  { "ograve",                            600, NULL },+  { "racute",                            600, NULL },+  { "Tcaron",                            600, NULL },+  { "Eogonek",                           600, NULL },+  { "thorn",                             600, NULL },+  { "degree",                            600, NULL },+  { "registered",                        600, NULL },+  { "radical",                           600, NULL },+  { "Aring",                             600, NULL },+  { "percent",                           600, NULL },+  { "six",                               600, NULL },+  { "paragraph",                         600, NULL },+  { "dcaron",                            600, NULL },+  { "Uogonek",                           600, NULL },+  { "two",                               600, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            600, NULL },+  { "Lacute",                            600, NULL },+  { "ocircumflex",                       600, NULL },+  { "oacute",                            600, NULL },+  { "Uring",                             600, NULL },+  { "Lcommaaccent",                      600, NULL },+  { "tcaron",                            600, NULL },+  { "eogonek",                           600, NULL },+  { "Delta",                             600, NULL },+  { "Ohungarumlaut",                     600, NULL },+  { "asciicircum",                       600, NULL },+  { "aring",                             600, NULL },+  { "grave",                             600, NULL },+  { "uogonek",                           600, NULL },+  { "bracketright",                      600, NULL },+  { "Iacute",                            600, NULL },+  { "ampersand",                         600, NULL },+  { "igrave",                            600, NULL },+  { "lacute",                            600, NULL },+  { "Ncaron",                            600, NULL },+  { "plus",                              600, NULL },+  { "uring",                             600, NULL },+  { "quotesinglbase",                    600, NULL },+  { "lcommaaccent",                      600, NULL },+  { "Yacute",                            600, NULL },+  { "ohungarumlaut",                     600, NULL },+  { "threesuperior",                     600, NULL },+  { "acute",                             600, NULL },+  { "section",                           600, NULL },+  { "dieresis",                          600, NULL },+  { "iacute",                            600, NULL },+  { "quotedblbase",                      600, NULL },+  { "ncaron",                            600, NULL },+  { "florin",                            600, NULL },+  { "yacute",                            600, NULL },+  { "Rcommaaccent",                      600, NULL },+  { "fi",                                600, NULL },+  { "fl",                                600, NULL },+  { "Acircumflex",                       600, NULL },+  { "Cacute",                            600, NULL },+  { "Icircumflex",                       600, NULL },+  { "guillemotleft",                     600, NULL },+  { "germandbls",                        600, NULL },+  { "Amacron",                           600, NULL },+  { "seven",                             600, NULL },+  { "Sacute",                            600, NULL },+  { "ordmasculine",                      600, NULL },+  { "dotlessi",                          600, NULL },+  { "sterling",                          600, NULL },+  { "notequal",                          600, NULL },+  { "Imacron",                           600, NULL },+  { "rcommaaccent",                      600, NULL },+  { "Zdotaccent",                        600, NULL },+  { "acircumflex",                       600, NULL },+  { "cacute",                            600, NULL },+  { "Ecaron",                            600, NULL },+  { "icircumflex",                       600, NULL },+  { "braceright",                        600, NULL },+  { "quotedblright",                     600, NULL },+  { "amacron",                           600, NULL },+  { "sacute",                            600, NULL },+  { "imacron",                           600, NULL },+  { "cent",                              600, NULL },+  { "currency",                          600, NULL },+  { "logicalnot",                        600, NULL },+  { "zdotaccent",                        600, NULL },+  { "Atilde",                            600, NULL },+  { "breve",                             600, NULL },+  { "bar",                               600, NULL },+  { "fraction",                          600, NULL },+  { "less",                              600, NULL },+  { "ecaron",                            600, NULL },+  { "guilsinglleft",                     600, NULL },+  { "exclam",                            600, NULL },+  { "period",                            600, NULL },+  { "Rcaron",                            600, NULL },+  { "Kcommaaccent",                      600, NULL },+  { "greater",                           600, NULL },+  { "atilde",                            600, NULL },+  { "brokenbar",                         600, NULL },+  { "quoteleft",                         600, NULL },+  { "Edotaccent",                        600, NULL },+  { "onesuperior",                       600, NULL }+};++static BuiltinFontWidth courierObliqueWidthsTab[] = {+  { "Ntilde",                            600, NULL },+  { "rcaron",                            600, NULL },+  { "kcommaaccent",                      600, NULL },+  { "Ncommaaccent",                      600, NULL },+  { "Zacute",                            600, NULL },+  { "comma",                             600, NULL },+  { "cedilla",                           600, NULL },+  { "plusminus",                         600, NULL },+  { "circumflex",                        600, NULL },+  { "dotaccent",                         600, NULL },+  { "edotaccent",                        600, NULL },+  { "asciitilde",                        600, NULL },+  { "colon",                             600, NULL },+  { "onehalf",                           600, NULL },+  { "dollar",                            600, NULL },+  { "Lcaron",                            600, NULL },+  { "ntilde",                            600, NULL },+  { "Aogonek",                           600, NULL },+  { "ncommaaccent",                      600, NULL },+  { "minus",                             600, NULL },+  { "Iogonek",                           600, NULL },+  { "zacute",                            600, NULL },+  { "yen",                               600, NULL },+  { "space",                             600, NULL },+  { "Omacron",                           600, NULL },+  { "questiondown",                      600, NULL },+  { "emdash",                            600, NULL },+  { "Agrave",                            600, NULL },+  { "three",                             600, NULL },+  { "numbersign",                        600, NULL },+  { "lcaron",                            600, NULL },+  { "A",                                 600, NULL },+  { "B",                                 600, NULL },+  { "C",                                 600, NULL },+  { "aogonek",                           600, NULL },+  { "D",                                 600, NULL },+  { "E",                                 600, NULL },+  { "onequarter",                        600, NULL },+  { "F",                                 600, NULL },+  { "G",                                 600, NULL },+  { "H",                                 600, NULL },+  { "I",                                 600, NULL },+  { "J",                                 600, NULL },+  { "K",                                 600, NULL },+  { "iogonek",                           600, NULL },+  { "backslash",                         600, NULL },+  { "L",                                 600, NULL },+  { "periodcentered",                    600, NULL },+  { "M",                                 600, NULL },+  { "N",                                 600, NULL },+  { "omacron",                           600, NULL },+  { "Tcommaaccent",                      600, NULL },+  { "O",                                 600, NULL },+  { "P",                                 600, NULL },+  { "Q",                                 600, NULL },+  { "Uhungarumlaut",                     600, NULL },+  { "R",                                 600, NULL },+  { "Aacute",                            600, NULL },+  { "caron",                             600, NULL },+  { "S",                                 600, NULL },+  { "T",                                 600, NULL },+  { "U",                                 600, NULL },+  { "agrave",                            600, NULL },+  { "V",                                 600, NULL },+  { "W",                                 600, NULL },+  { "X",                                 600, NULL },+  { "question",                          600, NULL },+  { "equal",                             600, NULL },+  { "Y",                                 600, NULL },+  { "Z",                                 600, NULL },+  { "four",                              600, NULL },+  { "a",                                 600, NULL },+  { "Gcommaaccent",                      600, NULL },+  { "b",                                 600, NULL },+  { "c",                                 600, NULL },+  { "d",                                 600, NULL },+  { "e",                                 600, NULL },+  { "f",                                 600, NULL },+  { "g",                                 600, NULL },+  { "bullet",                            600, NULL },+  { "h",                                 600, NULL },+  { "i",                                 600, NULL },+  { "Oslash",                            600, NULL },+  { "dagger",                            600, NULL },+  { "j",                                 600, NULL },+  { "k",                                 600, NULL },+  { "l",                                 600, NULL },+  { "m",                                 600, NULL },+  { "n",                                 600, NULL },+  { "tcommaaccent",                      600, NULL },+  { "o",                                 600, NULL },+  { "ordfeminine",                       600, NULL },+  { "ring",                              600, NULL },+  { "p",                                 600, NULL },+  { "q",                                 600, NULL },+  { "uhungarumlaut",                     600, NULL },+  { "r",                                 600, NULL },+  { "twosuperior",                       600, NULL },+  { "aacute",                            600, NULL },+  { "s",                                 600, NULL },+  { "OE",                                600, NULL },+  { "t",                                 600, NULL },+  { "divide",                            600, NULL },+  { "u",                                 600, NULL },+  { "Ccaron",                            600, NULL },+  { "v",                                 600, NULL },+  { "w",                                 600, NULL },+  { "x",                                 600, NULL },+  { "y",                                 600, NULL },+  { "z",                                 600, NULL },+  { "Gbreve",                            600, NULL },+  { "commaaccent",                       600, NULL },+  { "hungarumlaut",                      600, NULL },+  { "Idotaccent",                        600, NULL },+  { "Nacute",                            600, NULL },+  { "quotedbl",                          600, NULL },+  { "gcommaaccent",                      600, NULL },+  { "mu",                                600, NULL },+  { "greaterequal",                      600, NULL },+  { "Scaron",                            600, NULL },+  { "Lslash",                            600, NULL },+  { "semicolon",                         600, NULL },+  { "oslash",                            600, NULL },+  { "lessequal",                         600, NULL },+  { "lozenge",                           600, NULL },+  { "parenright",                        600, NULL },+  { "ccaron",                            600, NULL },+  { "Ecircumflex",                       600, NULL },+  { "gbreve",                            600, NULL },+  { "trademark",                         600, NULL },+  { "daggerdbl",                         600, NULL },+  { "nacute",                            600, NULL },+  { "macron",                            600, NULL },+  { "Otilde",                            600, NULL },+  { "Emacron",                           600, NULL },+  { "ellipsis",                          600, NULL },+  { "scaron",                            600, NULL },+  { "AE",                                600, NULL },+  { "Ucircumflex",                       600, NULL },+  { "lslash",                            600, NULL },+  { "quotedblleft",                      600, NULL },+  { "guilsinglright",                    600, NULL },+  { "hyphen",                            600, NULL },+  { "quotesingle",                       600, NULL },+  { "eight",                             600, NULL },+  { "exclamdown",                        600, NULL },+  { "endash",                            600, NULL },+  { "oe",                                600, NULL },+  { "Abreve",                            600, NULL },+  { "Umacron",                           600, NULL },+  { "ecircumflex",                       600, NULL },+  { "Adieresis",                         600, NULL },+  { "copyright",                         600, NULL },+  { "Egrave",                            600, NULL },+  { "slash",                             600, NULL },+  { "Edieresis",                         600, NULL },+  { "otilde",                            600, NULL },+  { "Idieresis",                         600, NULL },+  { "parenleft",                         600, NULL },+  { "one",                               600, NULL },+  { "emacron",                           600, NULL },+  { "Odieresis",                         600, NULL },+  { "ucircumflex",                       600, NULL },+  { "bracketleft",                       600, NULL },+  { "Ugrave",                            600, NULL },+  { "quoteright",                        600, NULL },+  { "Udieresis",                         600, NULL },+  { "perthousand",                       600, NULL },+  { "Ydieresis",                         600, NULL },+  { "umacron",                           600, NULL },+  { "abreve",                            600, NULL },+  { "Eacute",                            600, NULL },+  { "adieresis",                         600, NULL },+  { "egrave",                            600, NULL },+  { "edieresis",                         600, NULL },+  { "idieresis",                         600, NULL },+  { "Eth",                               600, NULL },+  { "ae",                                600, NULL },+  { "asterisk",                          600, NULL },+  { "odieresis",                         600, NULL },+  { "Uacute",                            600, NULL },+  { "ugrave",                            600, NULL },+  { "nine",                              600, NULL },+  { "five",                              600, NULL },+  { "udieresis",                         600, NULL },+  { "Zcaron",                            600, NULL },+  { "Scommaaccent",                      600, NULL },+  { "threequarters",                     600, NULL },+  { "guillemotright",                    600, NULL },+  { "Ccedilla",                          600, NULL },+  { "ydieresis",                         600, NULL },+  { "tilde",                             600, NULL },+  { "at",                                600, NULL },+  { "eacute",                            600, NULL },+  { "underscore",                        600, NULL },+  { "Euro",                              600, NULL },+  { "Dcroat",                            600, NULL },+  { "multiply",                          600, NULL },+  { "zero",                              600, NULL },+  { "eth",                               600, NULL },+  { "Scedilla",                          600, NULL },+  { "Ograve",                            600, NULL },+  { "Racute",                            600, NULL },+  { "partialdiff",                       600, NULL },+  { "uacute",                            600, NULL },+  { "braceleft",                         600, NULL },+  { "Thorn",                             600, NULL },+  { "zcaron",                            600, NULL },+  { "scommaaccent",                      600, NULL },+  { "ccedilla",                          600, NULL },+  { "Dcaron",                            600, NULL },+  { "dcroat",                            600, NULL },+  { "Ocircumflex",                       600, NULL },+  { "Oacute",                            600, NULL },+  { "scedilla",                          600, NULL },+  { "ogonek",                            600, NULL },+  { "ograve",                            600, NULL },+  { "racute",                            600, NULL },+  { "Tcaron",                            600, NULL },+  { "Eogonek",                           600, NULL },+  { "thorn",                             600, NULL },+  { "degree",                            600, NULL },+  { "registered",                        600, NULL },+  { "radical",                           600, NULL },+  { "Aring",                             600, NULL },+  { "percent",                           600, NULL },+  { "six",                               600, NULL },+  { "paragraph",                         600, NULL },+  { "dcaron",                            600, NULL },+  { "Uogonek",                           600, NULL },+  { "two",                               600, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            600, NULL },+  { "Lacute",                            600, NULL },+  { "ocircumflex",                       600, NULL },+  { "oacute",                            600, NULL },+  { "Uring",                             600, NULL },+  { "Lcommaaccent",                      600, NULL },+  { "tcaron",                            600, NULL },+  { "eogonek",                           600, NULL },+  { "Delta",                             600, NULL },+  { "Ohungarumlaut",                     600, NULL },+  { "asciicircum",                       600, NULL },+  { "aring",                             600, NULL },+  { "grave",                             600, NULL },+  { "uogonek",                           600, NULL },+  { "bracketright",                      600, NULL },+  { "Iacute",                            600, NULL },+  { "ampersand",                         600, NULL },+  { "igrave",                            600, NULL },+  { "lacute",                            600, NULL },+  { "Ncaron",                            600, NULL },+  { "plus",                              600, NULL },+  { "uring",                             600, NULL },+  { "quotesinglbase",                    600, NULL },+  { "lcommaaccent",                      600, NULL },+  { "Yacute",                            600, NULL },+  { "ohungarumlaut",                     600, NULL },+  { "threesuperior",                     600, NULL },+  { "acute",                             600, NULL },+  { "section",                           600, NULL },+  { "dieresis",                          600, NULL },+  { "iacute",                            600, NULL },+  { "quotedblbase",                      600, NULL },+  { "ncaron",                            600, NULL },+  { "florin",                            600, NULL },+  { "yacute",                            600, NULL },+  { "Rcommaaccent",                      600, NULL },+  { "fi",                                600, NULL },+  { "fl",                                600, NULL },+  { "Acircumflex",                       600, NULL },+  { "Cacute",                            600, NULL },+  { "Icircumflex",                       600, NULL },+  { "guillemotleft",                     600, NULL },+  { "germandbls",                        600, NULL },+  { "Amacron",                           600, NULL },+  { "seven",                             600, NULL },+  { "Sacute",                            600, NULL },+  { "ordmasculine",                      600, NULL },+  { "dotlessi",                          600, NULL },+  { "sterling",                          600, NULL },+  { "notequal",                          600, NULL },+  { "Imacron",                           600, NULL },+  { "rcommaaccent",                      600, NULL },+  { "Zdotaccent",                        600, NULL },+  { "acircumflex",                       600, NULL },+  { "cacute",                            600, NULL },+  { "Ecaron",                            600, NULL },+  { "icircumflex",                       600, NULL },+  { "braceright",                        600, NULL },+  { "quotedblright",                     600, NULL },+  { "amacron",                           600, NULL },+  { "sacute",                            600, NULL },+  { "imacron",                           600, NULL },+  { "cent",                              600, NULL },+  { "currency",                          600, NULL },+  { "logicalnot",                        600, NULL },+  { "zdotaccent",                        600, NULL },+  { "Atilde",                            600, NULL },+  { "breve",                             600, NULL },+  { "bar",                               600, NULL },+  { "fraction",                          600, NULL },+  { "less",                              600, NULL },+  { "ecaron",                            600, NULL },+  { "guilsinglleft",                     600, NULL },+  { "exclam",                            600, NULL },+  { "period",                            600, NULL },+  { "Rcaron",                            600, NULL },+  { "Kcommaaccent",                      600, NULL },+  { "greater",                           600, NULL },+  { "atilde",                            600, NULL },+  { "brokenbar",                         600, NULL },+  { "quoteleft",                         600, NULL },+  { "Edotaccent",                        600, NULL },+  { "onesuperior",                       600, NULL }+};++static BuiltinFontWidth helveticaWidthsTab[] = {+  { "Ntilde",                            722, NULL },+  { "rcaron",                            333, NULL },+  { "kcommaaccent",                      500, NULL },+  { "Ncommaaccent",                      722, NULL },+  { "Zacute",                            611, NULL },+  { "comma",                             278, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         584, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        556, NULL },+  { "asciitilde",                        584, NULL },+  { "colon",                             278, NULL },+  { "onehalf",                           834, NULL },+  { "dollar",                            556, NULL },+  { "Lcaron",                            556, NULL },+  { "ntilde",                            556, NULL },+  { "Aogonek",                           667, NULL },+  { "ncommaaccent",                      556, NULL },+  { "minus",                             584, NULL },+  { "Iogonek",                           278, NULL },+  { "zacute",                            500, NULL },+  { "yen",                               556, NULL },+  { "space",                             278, NULL },+  { "Omacron",                           778, NULL },+  { "questiondown",                      611, NULL },+  { "emdash",                           1000, NULL },+  { "Agrave",                            667, NULL },+  { "three",                             556, NULL },+  { "numbersign",                        556, NULL },+  { "lcaron",                            299, NULL },+  { "A",                                 667, NULL },+  { "B",                                 667, NULL },+  { "C",                                 722, NULL },+  { "aogonek",                           556, NULL },+  { "D",                                 722, NULL },+  { "E",                                 667, NULL },+  { "onequarter",                        834, NULL },+  { "F",                                 611, NULL },+  { "G",                                 778, NULL },+  { "H",                                 722, NULL },+  { "I",                                 278, NULL },+  { "J",                                 500, NULL },+  { "K",                                 667, NULL },+  { "iogonek",                           222, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 556, NULL },+  { "periodcentered",                    278, NULL },+  { "M",                                 833, NULL },+  { "N",                                 722, NULL },+  { "omacron",                           556, NULL },+  { "Tcommaaccent",                      611, NULL },+  { "O",                                 778, NULL },+  { "P",                                 667, NULL },+  { "Q",                                 778, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 722, NULL },+  { "Aacute",                            667, NULL },+  { "caron",                             333, NULL },+  { "S",                                 667, NULL },+  { "T",                                 611, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            556, NULL },+  { "V",                                 667, NULL },+  { "W",                                 944, NULL },+  { "X",                                 667, NULL },+  { "question",                          556, NULL },+  { "equal",                             584, NULL },+  { "Y",                                 667, NULL },+  { "Z",                                 611, NULL },+  { "four",                              556, NULL },+  { "a",                                 556, NULL },+  { "Gcommaaccent",                      778, NULL },+  { "b",                                 556, NULL },+  { "c",                                 500, NULL },+  { "d",                                 556, NULL },+  { "e",                                 556, NULL },+  { "f",                                 278, NULL },+  { "g",                                 556, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 556, NULL },+  { "i",                                 222, NULL },+  { "Oslash",                            778, NULL },+  { "dagger",                            556, NULL },+  { "j",                                 222, NULL },+  { "k",                                 500, NULL },+  { "l",                                 222, NULL },+  { "m",                                 833, NULL },+  { "n",                                 556, NULL },+  { "tcommaaccent",                      278, NULL },+  { "o",                                 556, NULL },+  { "ordfeminine",                       370, NULL },+  { "ring",                              333, NULL },+  { "p",                                 556, NULL },+  { "q",                                 556, NULL },+  { "uhungarumlaut",                     556, NULL },+  { "r",                                 333, NULL },+  { "twosuperior",                       333, NULL },+  { "aacute",                            556, NULL },+  { "s",                                 500, NULL },+  { "OE",                               1000, NULL },+  { "t",                                 278, NULL },+  { "divide",                            584, NULL },+  { "u",                                 556, NULL },+  { "Ccaron",                            722, NULL },+  { "v",                                 500, NULL },+  { "w",                                 722, NULL },+  { "x",                                 500, NULL },+  { "y",                                 500, NULL },+  { "z",                                 500, NULL },+  { "Gbreve",                            778, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        278, NULL },+  { "Nacute",                            722, NULL },+  { "quotedbl",                          355, NULL },+  { "gcommaaccent",                      556, NULL },+  { "mu",                                556, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            667, NULL },+  { "Lslash",                            556, NULL },+  { "semicolon",                         278, NULL },+  { "oslash",                            611, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           471, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            500, NULL },+  { "Ecircumflex",                       667, NULL },+  { "gbreve",                            556, NULL },+  { "trademark",                        1000, NULL },+  { "daggerdbl",                         556, NULL },+  { "nacute",                            556, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            778, NULL },+  { "Emacron",                           667, NULL },+  { "ellipsis",                         1000, NULL },+  { "scaron",                            500, NULL },+  { "AE",                               1000, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            222, NULL },+  { "quotedblleft",                      333, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       191, NULL },+  { "eight",                             556, NULL },+  { "exclamdown",                        333, NULL },+  { "endash",                            556, NULL },+  { "oe",                                944, NULL },+  { "Abreve",                            667, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       556, NULL },+  { "Adieresis",                         667, NULL },+  { "copyright",                         737, NULL },+  { "Egrave",                            667, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         667, NULL },+  { "otilde",                            556, NULL },+  { "Idieresis",                         278, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               556, NULL },+  { "emacron",                           556, NULL },+  { "Odieresis",                         778, NULL },+  { "ucircumflex",                       556, NULL },+  { "bracketleft",                       278, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        222, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         667, NULL },+  { "umacron",                           556, NULL },+  { "abreve",                            556, NULL },+  { "Eacute",                            667, NULL },+  { "adieresis",                         556, NULL },+  { "egrave",                            556, NULL },+  { "edieresis",                         556, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                889, NULL },+  { "asterisk",                          389, NULL },+  { "odieresis",                         556, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            556, NULL },+  { "nine",                              556, NULL },+  { "five",                              556, NULL },+  { "udieresis",                         556, NULL },+  { "Zcaron",                            611, NULL },+  { "Scommaaccent",                      667, NULL },+  { "threequarters",                     834, NULL },+  { "guillemotright",                    556, NULL },+  { "Ccedilla",                          722, NULL },+  { "ydieresis",                         500, NULL },+  { "tilde",                             333, NULL },+  { "at",                               1015, NULL },+  { "eacute",                            556, NULL },+  { "underscore",                        556, NULL },+  { "Euro",                              556, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          584, NULL },+  { "zero",                              556, NULL },+  { "eth",                               556, NULL },+  { "Scedilla",                          667, NULL },+  { "Ograve",                            778, NULL },+  { "Racute",                            722, NULL },+  { "partialdiff",                       476, NULL },+  { "uacute",                            556, NULL },+  { "braceleft",                         334, NULL },+  { "Thorn",                             667, NULL },+  { "zcaron",                            500, NULL },+  { "scommaaccent",                      500, NULL },+  { "ccedilla",                          500, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            556, NULL },+  { "Ocircumflex",                       778, NULL },+  { "Oacute",                            778, NULL },+  { "scedilla",                          500, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            556, NULL },+  { "racute",                            333, NULL },+  { "Tcaron",                            611, NULL },+  { "Eogonek",                           667, NULL },+  { "thorn",                             556, NULL },+  { "degree",                            400, NULL },+  { "registered",                        737, NULL },+  { "radical",                           453, NULL },+  { "Aring",                             667, NULL },+  { "percent",                           889, NULL },+  { "six",                               556, NULL },+  { "paragraph",                         537, NULL },+  { "dcaron",                            643, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               556, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            278, NULL },+  { "Lacute",                            556, NULL },+  { "ocircumflex",                       556, NULL },+  { "oacute",                            556, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      556, NULL },+  { "tcaron",                            317, NULL },+  { "eogonek",                           556, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     778, NULL },+  { "asciicircum",                       469, NULL },+  { "aring",                             556, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           556, NULL },+  { "bracketright",                      278, NULL },+  { "Iacute",                            278, NULL },+  { "ampersand",                         667, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            222, NULL },+  { "Ncaron",                            722, NULL },+  { "plus",                              584, NULL },+  { "uring",                             556, NULL },+  { "quotesinglbase",                    222, NULL },+  { "lcommaaccent",                      222, NULL },+  { "Yacute",                            667, NULL },+  { "ohungarumlaut",                     556, NULL },+  { "threesuperior",                     333, NULL },+  { "acute",                             333, NULL },+  { "section",                           556, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      333, NULL },+  { "ncaron",                            556, NULL },+  { "florin",                            556, NULL },+  { "yacute",                            500, NULL },+  { "Rcommaaccent",                      722, NULL },+  { "fi",                                500, NULL },+  { "fl",                                500, NULL },+  { "Acircumflex",                       667, NULL },+  { "Cacute",                            722, NULL },+  { "Icircumflex",                       278, NULL },+  { "guillemotleft",                     556, NULL },+  { "germandbls",                        611, NULL },+  { "Amacron",                           667, NULL },+  { "seven",                             556, NULL },+  { "Sacute",                            667, NULL },+  { "ordmasculine",                      365, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          556, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           278, NULL },+  { "rcommaaccent",                      333, NULL },+  { "Zdotaccent",                        611, NULL },+  { "acircumflex",                       556, NULL },+  { "cacute",                            500, NULL },+  { "Ecaron",                            667, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        334, NULL },+  { "quotedblright",                     333, NULL },+  { "amacron",                           556, NULL },+  { "sacute",                            500, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              556, NULL },+  { "currency",                          556, NULL },+  { "logicalnot",                        584, NULL },+  { "zdotaccent",                        500, NULL },+  { "Atilde",                            667, NULL },+  { "breve",                             333, NULL },+  { "bar",                               260, NULL },+  { "fraction",                          167, NULL },+  { "less",                              584, NULL },+  { "ecaron",                            556, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            278, NULL },+  { "period",                            278, NULL },+  { "Rcaron",                            722, NULL },+  { "Kcommaaccent",                      667, NULL },+  { "greater",                           584, NULL },+  { "atilde",                            556, NULL },+  { "brokenbar",                         260, NULL },+  { "quoteleft",                         222, NULL },+  { "Edotaccent",                        667, NULL },+  { "onesuperior",                       333, NULL }+};++static BuiltinFontWidth helveticaBoldWidthsTab[] = {+  { "Ntilde",                            722, NULL },+  { "rcaron",                            389, NULL },+  { "kcommaaccent",                      556, NULL },+  { "Ncommaaccent",                      722, NULL },+  { "Zacute",                            611, NULL },+  { "comma",                             278, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         584, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        556, NULL },+  { "asciitilde",                        584, NULL },+  { "colon",                             333, NULL },+  { "onehalf",                           834, NULL },+  { "dollar",                            556, NULL },+  { "Lcaron",                            611, NULL },+  { "ntilde",                            611, NULL },+  { "Aogonek",                           722, NULL },+  { "ncommaaccent",                      611, NULL },+  { "minus",                             584, NULL },+  { "Iogonek",                           278, NULL },+  { "zacute",                            500, NULL },+  { "yen",                               556, NULL },+  { "space",                             278, NULL },+  { "Omacron",                           778, NULL },+  { "questiondown",                      611, NULL },+  { "emdash",                           1000, NULL },+  { "Agrave",                            722, NULL },+  { "three",                             556, NULL },+  { "numbersign",                        556, NULL },+  { "lcaron",                            400, NULL },+  { "A",                                 722, NULL },+  { "B",                                 722, NULL },+  { "C",                                 722, NULL },+  { "aogonek",                           556, NULL },+  { "D",                                 722, NULL },+  { "E",                                 667, NULL },+  { "onequarter",                        834, NULL },+  { "F",                                 611, NULL },+  { "G",                                 778, NULL },+  { "H",                                 722, NULL },+  { "I",                                 278, NULL },+  { "J",                                 556, NULL },+  { "K",                                 722, NULL },+  { "iogonek",                           278, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 611, NULL },+  { "periodcentered",                    278, NULL },+  { "M",                                 833, NULL },+  { "N",                                 722, NULL },+  { "omacron",                           611, NULL },+  { "Tcommaaccent",                      611, NULL },+  { "O",                                 778, NULL },+  { "P",                                 667, NULL },+  { "Q",                                 778, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 722, NULL },+  { "Aacute",                            722, NULL },+  { "caron",                             333, NULL },+  { "S",                                 667, NULL },+  { "T",                                 611, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            556, NULL },+  { "V",                                 667, NULL },+  { "W",                                 944, NULL },+  { "X",                                 667, NULL },+  { "question",                          611, NULL },+  { "equal",                             584, NULL },+  { "Y",                                 667, NULL },+  { "Z",                                 611, NULL },+  { "four",                              556, NULL },+  { "a",                                 556, NULL },+  { "Gcommaaccent",                      778, NULL },+  { "b",                                 611, NULL },+  { "c",                                 556, NULL },+  { "d",                                 611, NULL },+  { "e",                                 556, NULL },+  { "f",                                 333, NULL },+  { "g",                                 611, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 611, NULL },+  { "i",                                 278, NULL },+  { "Oslash",                            778, NULL },+  { "dagger",                            556, NULL },+  { "j",                                 278, NULL },+  { "k",                                 556, NULL },+  { "l",                                 278, NULL },+  { "m",                                 889, NULL },+  { "n",                                 611, NULL },+  { "tcommaaccent",                      333, NULL },+  { "o",                                 611, NULL },+  { "ordfeminine",                       370, NULL },+  { "ring",                              333, NULL },+  { "p",                                 611, NULL },+  { "q",                                 611, NULL },+  { "uhungarumlaut",                     611, NULL },+  { "r",                                 389, NULL },+  { "twosuperior",                       333, NULL },+  { "aacute",                            556, NULL },+  { "s",                                 556, NULL },+  { "OE",                               1000, NULL },+  { "t",                                 333, NULL },+  { "divide",                            584, NULL },+  { "u",                                 611, NULL },+  { "Ccaron",                            722, NULL },+  { "v",                                 556, NULL },+  { "w",                                 778, NULL },+  { "x",                                 556, NULL },+  { "y",                                 556, NULL },+  { "z",                                 500, NULL },+  { "Gbreve",                            778, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        278, NULL },+  { "Nacute",                            722, NULL },+  { "quotedbl",                          474, NULL },+  { "gcommaaccent",                      611, NULL },+  { "mu",                                611, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            667, NULL },+  { "Lslash",                            611, NULL },+  { "semicolon",                         333, NULL },+  { "oslash",                            611, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           494, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            556, NULL },+  { "Ecircumflex",                       667, NULL },+  { "gbreve",                            611, NULL },+  { "trademark",                        1000, NULL },+  { "daggerdbl",                         556, NULL },+  { "nacute",                            611, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            778, NULL },+  { "Emacron",                           667, NULL },+  { "ellipsis",                         1000, NULL },+  { "scaron",                            556, NULL },+  { "AE",                               1000, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            278, NULL },+  { "quotedblleft",                      500, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       238, NULL },+  { "eight",                             556, NULL },+  { "exclamdown",                        333, NULL },+  { "endash",                            556, NULL },+  { "oe",                                944, NULL },+  { "Abreve",                            722, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       556, NULL },+  { "Adieresis",                         722, NULL },+  { "copyright",                         737, NULL },+  { "Egrave",                            667, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         667, NULL },+  { "otilde",                            611, NULL },+  { "Idieresis",                         278, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               556, NULL },+  { "emacron",                           556, NULL },+  { "Odieresis",                         778, NULL },+  { "ucircumflex",                       611, NULL },+  { "bracketleft",                       333, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        278, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         667, NULL },+  { "umacron",                           611, NULL },+  { "abreve",                            556, NULL },+  { "Eacute",                            667, NULL },+  { "adieresis",                         556, NULL },+  { "egrave",                            556, NULL },+  { "edieresis",                         556, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                889, NULL },+  { "asterisk",                          389, NULL },+  { "odieresis",                         611, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            611, NULL },+  { "nine",                              556, NULL },+  { "five",                              556, NULL },+  { "udieresis",                         611, NULL },+  { "Zcaron",                            611, NULL },+  { "Scommaaccent",                      667, NULL },+  { "threequarters",                     834, NULL },+  { "guillemotright",                    556, NULL },+  { "Ccedilla",                          722, NULL },+  { "ydieresis",                         556, NULL },+  { "tilde",                             333, NULL },+  { "dbldaggerumlaut",                   556, NULL },+  { "at",                                975, NULL },+  { "eacute",                            556, NULL },+  { "underscore",                        556, NULL },+  { "Euro",                              556, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          584, NULL },+  { "zero",                              556, NULL },+  { "eth",                               611, NULL },+  { "Scedilla",                          667, NULL },+  { "Ograve",                            778, NULL },+  { "Racute",                            722, NULL },+  { "partialdiff",                       494, NULL },+  { "uacute",                            611, NULL },+  { "braceleft",                         389, NULL },+  { "Thorn",                             667, NULL },+  { "zcaron",                            500, NULL },+  { "scommaaccent",                      556, NULL },+  { "ccedilla",                          556, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            611, NULL },+  { "Ocircumflex",                       778, NULL },+  { "Oacute",                            778, NULL },+  { "scedilla",                          556, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            611, NULL },+  { "racute",                            389, NULL },+  { "Tcaron",                            611, NULL },+  { "Eogonek",                           667, NULL },+  { "thorn",                             611, NULL },+  { "degree",                            400, NULL },+  { "registered",                        737, NULL },+  { "radical",                           549, NULL },+  { "Aring",                             722, NULL },+  { "percent",                           889, NULL },+  { "six",                               556, NULL },+  { "paragraph",                         556, NULL },+  { "dcaron",                            743, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               556, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            278, NULL },+  { "Lacute",                            611, NULL },+  { "ocircumflex",                       611, NULL },+  { "oacute",                            611, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      611, NULL },+  { "tcaron",                            389, NULL },+  { "eogonek",                           556, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     778, NULL },+  { "asciicircum",                       584, NULL },+  { "aring",                             556, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           611, NULL },+  { "bracketright",                      333, NULL },+  { "Iacute",                            278, NULL },+  { "ampersand",                         722, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            278, NULL },+  { "Ncaron",                            722, NULL },+  { "plus",                              584, NULL },+  { "uring",                             611, NULL },+  { "quotesinglbase",                    278, NULL },+  { "lcommaaccent",                      278, NULL },+  { "Yacute",                            667, NULL },+  { "ohungarumlaut",                     611, NULL },+  { "threesuperior",                     333, NULL },+  { "acute",                             333, NULL },+  { "section",                           556, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      500, NULL },+  { "ncaron",                            611, NULL },+  { "florin",                            556, NULL },+  { "yacute",                            556, NULL },+  { "Rcommaaccent",                      722, NULL },+  { "fi",                                611, NULL },+  { "fl",                                611, NULL },+  { "Acircumflex",                       722, NULL },+  { "Cacute",                            722, NULL },+  { "Icircumflex",                       278, NULL },+  { "guillemotleft",                     556, NULL },+  { "germandbls",                        611, NULL },+  { "Amacron",                           722, NULL },+  { "seven",                             556, NULL },+  { "Sacute",                            667, NULL },+  { "ordmasculine",                      365, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          556, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           278, NULL },+  { "rcommaaccent",                      389, NULL },+  { "Zdotaccent",                        611, NULL },+  { "acircumflex",                       556, NULL },+  { "cacute",                            556, NULL },+  { "Ecaron",                            667, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        389, NULL },+  { "quotedblright",                     500, NULL },+  { "amacron",                           556, NULL },+  { "sacute",                            556, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              556, NULL },+  { "currency",                          556, NULL },+  { "logicalnot",                        584, NULL },+  { "zdotaccent",                        500, NULL },+  { "Atilde",                            722, NULL },+  { "breve",                             333, NULL },+  { "bar",                               280, NULL },+  { "fraction",                          167, NULL },+  { "less",                              584, NULL },+  { "ecaron",                            556, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            333, NULL },+  { "period",                            278, NULL },+  { "Rcaron",                            722, NULL },+  { "Kcommaaccent",                      722, NULL },+  { "greater",                           584, NULL },+  { "atilde",                            556, NULL },+  { "brokenbar",                         280, NULL },+  { "quoteleft",                         278, NULL },+  { "Edotaccent",                        667, NULL },+  { "onesuperior",                       333, NULL }+};++static BuiltinFontWidth helveticaBoldObliqueWidthsTab[] = {+  { "Ntilde",                            722, NULL },+  { "rcaron",                            389, NULL },+  { "kcommaaccent",                      556, NULL },+  { "Ncommaaccent",                      722, NULL },+  { "Zacute",                            611, NULL },+  { "comma",                             278, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         584, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        556, NULL },+  { "asciitilde",                        584, NULL },+  { "colon",                             333, NULL },+  { "onehalf",                           834, NULL },+  { "dollar",                            556, NULL },+  { "Lcaron",                            611, NULL },+  { "ntilde",                            611, NULL },+  { "Aogonek",                           722, NULL },+  { "ncommaaccent",                      611, NULL },+  { "minus",                             584, NULL },+  { "Iogonek",                           278, NULL },+  { "zacute",                            500, NULL },+  { "yen",                               556, NULL },+  { "space",                             278, NULL },+  { "Omacron",                           778, NULL },+  { "questiondown",                      611, NULL },+  { "emdash",                           1000, NULL },+  { "Agrave",                            722, NULL },+  { "three",                             556, NULL },+  { "numbersign",                        556, NULL },+  { "lcaron",                            400, NULL },+  { "A",                                 722, NULL },+  { "B",                                 722, NULL },+  { "C",                                 722, NULL },+  { "aogonek",                           556, NULL },+  { "D",                                 722, NULL },+  { "E",                                 667, NULL },+  { "onequarter",                        834, NULL },+  { "F",                                 611, NULL },+  { "G",                                 778, NULL },+  { "H",                                 722, NULL },+  { "I",                                 278, NULL },+  { "J",                                 556, NULL },+  { "K",                                 722, NULL },+  { "iogonek",                           278, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 611, NULL },+  { "periodcentered",                    278, NULL },+  { "M",                                 833, NULL },+  { "N",                                 722, NULL },+  { "omacron",                           611, NULL },+  { "Tcommaaccent",                      611, NULL },+  { "O",                                 778, NULL },+  { "P",                                 667, NULL },+  { "Q",                                 778, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 722, NULL },+  { "Aacute",                            722, NULL },+  { "caron",                             333, NULL },+  { "S",                                 667, NULL },+  { "T",                                 611, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            556, NULL },+  { "V",                                 667, NULL },+  { "W",                                 944, NULL },+  { "X",                                 667, NULL },+  { "question",                          611, NULL },+  { "equal",                             584, NULL },+  { "Y",                                 667, NULL },+  { "Z",                                 611, NULL },+  { "four",                              556, NULL },+  { "a",                                 556, NULL },+  { "Gcommaaccent",                      778, NULL },+  { "b",                                 611, NULL },+  { "c",                                 556, NULL },+  { "d",                                 611, NULL },+  { "e",                                 556, NULL },+  { "f",                                 333, NULL },+  { "g",                                 611, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 611, NULL },+  { "i",                                 278, NULL },+  { "Oslash",                            778, NULL },+  { "dagger",                            556, NULL },+  { "j",                                 278, NULL },+  { "k",                                 556, NULL },+  { "l",                                 278, NULL },+  { "m",                                 889, NULL },+  { "n",                                 611, NULL },+  { "tcommaaccent",                      333, NULL },+  { "o",                                 611, NULL },+  { "ordfeminine",                       370, NULL },+  { "ring",                              333, NULL },+  { "p",                                 611, NULL },+  { "q",                                 611, NULL },+  { "uhungarumlaut",                     611, NULL },+  { "r",                                 389, NULL },+  { "twosuperior",                       333, NULL },+  { "aacute",                            556, NULL },+  { "s",                                 556, NULL },+  { "OE",                               1000, NULL },+  { "t",                                 333, NULL },+  { "divide",                            584, NULL },+  { "u",                                 611, NULL },+  { "Ccaron",                            722, NULL },+  { "v",                                 556, NULL },+  { "w",                                 778, NULL },+  { "x",                                 556, NULL },+  { "y",                                 556, NULL },+  { "z",                                 500, NULL },+  { "Gbreve",                            778, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        278, NULL },+  { "Nacute",                            722, NULL },+  { "quotedbl",                          474, NULL },+  { "gcommaaccent",                      611, NULL },+  { "mu",                                611, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            667, NULL },+  { "Lslash",                            611, NULL },+  { "semicolon",                         333, NULL },+  { "oslash",                            611, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           494, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            556, NULL },+  { "Ecircumflex",                       667, NULL },+  { "gbreve",                            611, NULL },+  { "trademark",                        1000, NULL },+  { "daggerdbl",                         556, NULL },+  { "nacute",                            611, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            778, NULL },+  { "Emacron",                           667, NULL },+  { "ellipsis",                         1000, NULL },+  { "scaron",                            556, NULL },+  { "AE",                               1000, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            278, NULL },+  { "quotedblleft",                      500, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       238, NULL },+  { "eight",                             556, NULL },+  { "exclamdown",                        333, NULL },+  { "endash",                            556, NULL },+  { "oe",                                944, NULL },+  { "Abreve",                            722, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       556, NULL },+  { "Adieresis",                         722, NULL },+  { "copyright",                         737, NULL },+  { "Egrave",                            667, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         667, NULL },+  { "otilde",                            611, NULL },+  { "Idieresis",                         278, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               556, NULL },+  { "emacron",                           556, NULL },+  { "Odieresis",                         778, NULL },+  { "ucircumflex",                       611, NULL },+  { "bracketleft",                       333, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        278, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         667, NULL },+  { "umacron",                           611, NULL },+  { "abreve",                            556, NULL },+  { "Eacute",                            667, NULL },+  { "adieresis",                         556, NULL },+  { "egrave",                            556, NULL },+  { "edieresis",                         556, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                889, NULL },+  { "asterisk",                          389, NULL },+  { "odieresis",                         611, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            611, NULL },+  { "nine",                              556, NULL },+  { "five",                              556, NULL },+  { "udieresis",                         611, NULL },+  { "Zcaron",                            611, NULL },+  { "Scommaaccent",                      667, NULL },+  { "threequarters",                     834, NULL },+  { "guillemotright",                    556, NULL },+  { "Ccedilla",                          722, NULL },+  { "ydieresis",                         556, NULL },+  { "tilde",                             333, NULL },+  { "at",                                975, NULL },+  { "eacute",                            556, NULL },+  { "underscore",                        556, NULL },+  { "Euro",                              556, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          584, NULL },+  { "zero",                              556, NULL },+  { "eth",                               611, NULL },+  { "Scedilla",                          667, NULL },+  { "Ograve",                            778, NULL },+  { "Racute",                            722, NULL },+  { "partialdiff",                       494, NULL },+  { "uacute",                            611, NULL },+  { "braceleft",                         389, NULL },+  { "Thorn",                             667, NULL },+  { "zcaron",                            500, NULL },+  { "scommaaccent",                      556, NULL },+  { "ccedilla",                          556, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            611, NULL },+  { "Ocircumflex",                       778, NULL },+  { "Oacute",                            778, NULL },+  { "scedilla",                          556, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            611, NULL },+  { "racute",                            389, NULL },+  { "Tcaron",                            611, NULL },+  { "Eogonek",                           667, NULL },+  { "thorn",                             611, NULL },+  { "degree",                            400, NULL },+  { "registered",                        737, NULL },+  { "radical",                           549, NULL },+  { "Aring",                             722, NULL },+  { "percent",                           889, NULL },+  { "six",                               556, NULL },+  { "paragraph",                         556, NULL },+  { "dcaron",                            743, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               556, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            278, NULL },+  { "Lacute",                            611, NULL },+  { "ocircumflex",                       611, NULL },+  { "oacute",                            611, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      611, NULL },+  { "tcaron",                            389, NULL },+  { "eogonek",                           556, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     778, NULL },+  { "asciicircum",                       584, NULL },+  { "aring",                             556, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           611, NULL },+  { "bracketright",                      333, NULL },+  { "Iacute",                            278, NULL },+  { "ampersand",                         722, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            278, NULL },+  { "Ncaron",                            722, NULL },+  { "plus",                              584, NULL },+  { "uring",                             611, NULL },+  { "quotesinglbase",                    278, NULL },+  { "lcommaaccent",                      278, NULL },+  { "Yacute",                            667, NULL },+  { "ohungarumlaut",                     611, NULL },+  { "threesuperior",                     333, NULL },+  { "acute",                             333, NULL },+  { "section",                           556, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      500, NULL },+  { "ncaron",                            611, NULL },+  { "florin",                            556, NULL },+  { "yacute",                            556, NULL },+  { "Rcommaaccent",                      722, NULL },+  { "fi",                                611, NULL },+  { "fl",                                611, NULL },+  { "Acircumflex",                       722, NULL },+  { "Cacute",                            722, NULL },+  { "Icircumflex",                       278, NULL },+  { "guillemotleft",                     556, NULL },+  { "germandbls",                        611, NULL },+  { "Amacron",                           722, NULL },+  { "seven",                             556, NULL },+  { "Sacute",                            667, NULL },+  { "ordmasculine",                      365, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          556, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           278, NULL },+  { "rcommaaccent",                      389, NULL },+  { "Zdotaccent",                        611, NULL },+  { "acircumflex",                       556, NULL },+  { "cacute",                            556, NULL },+  { "Ecaron",                            667, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        389, NULL },+  { "quotedblright",                     500, NULL },+  { "amacron",                           556, NULL },+  { "sacute",                            556, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              556, NULL },+  { "currency",                          556, NULL },+  { "logicalnot",                        584, NULL },+  { "zdotaccent",                        500, NULL },+  { "Atilde",                            722, NULL },+  { "breve",                             333, NULL },+  { "bar",                               280, NULL },+  { "fraction",                          167, NULL },+  { "less",                              584, NULL },+  { "ecaron",                            556, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            333, NULL },+  { "period",                            278, NULL },+  { "Rcaron",                            722, NULL },+  { "Kcommaaccent",                      722, NULL },+  { "greater",                           584, NULL },+  { "atilde",                            556, NULL },+  { "brokenbar",                         280, NULL },+  { "quoteleft",                         278, NULL },+  { "Edotaccent",                        667, NULL },+  { "onesuperior",                       333, NULL }+};++static BuiltinFontWidth helveticaObliqueWidthsTab[] = {+  { "Ntilde",                            722, NULL },+  { "rcaron",                            333, NULL },+  { "kcommaaccent",                      500, NULL },+  { "Ncommaaccent",                      722, NULL },+  { "Zacute",                            611, NULL },+  { "comma",                             278, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         584, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        556, NULL },+  { "asciitilde",                        584, NULL },+  { "colon",                             278, NULL },+  { "onehalf",                           834, NULL },+  { "dollar",                            556, NULL },+  { "Lcaron",                            556, NULL },+  { "ntilde",                            556, NULL },+  { "Aogonek",                           667, NULL },+  { "ncommaaccent",                      556, NULL },+  { "minus",                             584, NULL },+  { "Iogonek",                           278, NULL },+  { "zacute",                            500, NULL },+  { "yen",                               556, NULL },+  { "space",                             278, NULL },+  { "Omacron",                           778, NULL },+  { "questiondown",                      611, NULL },+  { "emdash",                           1000, NULL },+  { "Agrave",                            667, NULL },+  { "three",                             556, NULL },+  { "numbersign",                        556, NULL },+  { "lcaron",                            299, NULL },+  { "A",                                 667, NULL },+  { "B",                                 667, NULL },+  { "C",                                 722, NULL },+  { "aogonek",                           556, NULL },+  { "D",                                 722, NULL },+  { "E",                                 667, NULL },+  { "onequarter",                        834, NULL },+  { "F",                                 611, NULL },+  { "G",                                 778, NULL },+  { "H",                                 722, NULL },+  { "I",                                 278, NULL },+  { "J",                                 500, NULL },+  { "K",                                 667, NULL },+  { "iogonek",                           222, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 556, NULL },+  { "periodcentered",                    278, NULL },+  { "M",                                 833, NULL },+  { "N",                                 722, NULL },+  { "omacron",                           556, NULL },+  { "Tcommaaccent",                      611, NULL },+  { "O",                                 778, NULL },+  { "P",                                 667, NULL },+  { "Q",                                 778, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 722, NULL },+  { "Aacute",                            667, NULL },+  { "caron",                             333, NULL },+  { "S",                                 667, NULL },+  { "T",                                 611, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            556, NULL },+  { "V",                                 667, NULL },+  { "W",                                 944, NULL },+  { "X",                                 667, NULL },+  { "question",                          556, NULL },+  { "equal",                             584, NULL },+  { "Y",                                 667, NULL },+  { "Z",                                 611, NULL },+  { "four",                              556, NULL },+  { "a",                                 556, NULL },+  { "Gcommaaccent",                      778, NULL },+  { "b",                                 556, NULL },+  { "c",                                 500, NULL },+  { "d",                                 556, NULL },+  { "e",                                 556, NULL },+  { "f",                                 278, NULL },+  { "g",                                 556, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 556, NULL },+  { "i",                                 222, NULL },+  { "Oslash",                            778, NULL },+  { "dagger",                            556, NULL },+  { "j",                                 222, NULL },+  { "k",                                 500, NULL },+  { "l",                                 222, NULL },+  { "m",                                 833, NULL },+  { "n",                                 556, NULL },+  { "tcommaaccent",                      278, NULL },+  { "o",                                 556, NULL },+  { "ordfeminine",                       370, NULL },+  { "ring",                              333, NULL },+  { "p",                                 556, NULL },+  { "q",                                 556, NULL },+  { "uhungarumlaut",                     556, NULL },+  { "r",                                 333, NULL },+  { "twosuperior",                       333, NULL },+  { "aacute",                            556, NULL },+  { "s",                                 500, NULL },+  { "OE",                               1000, NULL },+  { "t",                                 278, NULL },+  { "divide",                            584, NULL },+  { "u",                                 556, NULL },+  { "Ccaron",                            722, NULL },+  { "v",                                 500, NULL },+  { "w",                                 722, NULL },+  { "x",                                 500, NULL },+  { "y",                                 500, NULL },+  { "z",                                 500, NULL },+  { "Gbreve",                            778, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        278, NULL },+  { "Nacute",                            722, NULL },+  { "quotedbl",                          355, NULL },+  { "gcommaaccent",                      556, NULL },+  { "mu",                                556, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            667, NULL },+  { "Lslash",                            556, NULL },+  { "semicolon",                         278, NULL },+  { "oslash",                            611, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           471, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            500, NULL },+  { "Ecircumflex",                       667, NULL },+  { "gbreve",                            556, NULL },+  { "trademark",                        1000, NULL },+  { "daggerdbl",                         556, NULL },+  { "nacute",                            556, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            778, NULL },+  { "Emacron",                           667, NULL },+  { "ellipsis",                         1000, NULL },+  { "scaron",                            500, NULL },+  { "AE",                               1000, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            222, NULL },+  { "quotedblleft",                      333, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       191, NULL },+  { "eight",                             556, NULL },+  { "exclamdown",                        333, NULL },+  { "endash",                            556, NULL },+  { "oe",                                944, NULL },+  { "Abreve",                            667, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       556, NULL },+  { "Adieresis",                         667, NULL },+  { "copyright",                         737, NULL },+  { "Egrave",                            667, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         667, NULL },+  { "otilde",                            556, NULL },+  { "Idieresis",                         278, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               556, NULL },+  { "emacron",                           556, NULL },+  { "Odieresis",                         778, NULL },+  { "ucircumflex",                       556, NULL },+  { "bracketleft",                       278, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        222, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         667, NULL },+  { "umacron",                           556, NULL },+  { "abreve",                            556, NULL },+  { "Eacute",                            667, NULL },+  { "adieresis",                         556, NULL },+  { "egrave",                            556, NULL },+  { "edieresis",                         556, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                889, NULL },+  { "asterisk",                          389, NULL },+  { "odieresis",                         556, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            556, NULL },+  { "nine",                              556, NULL },+  { "five",                              556, NULL },+  { "udieresis",                         556, NULL },+  { "Zcaron",                            611, NULL },+  { "Scommaaccent",                      667, NULL },+  { "threequarters",                     834, NULL },+  { "guillemotright",                    556, NULL },+  { "Ccedilla",                          722, NULL },+  { "ydieresis",                         500, NULL },+  { "tilde",                             333, NULL },+  { "at",                               1015, NULL },+  { "eacute",                            556, NULL },+  { "underscore",                        556, NULL },+  { "Euro",                              556, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          584, NULL },+  { "zero",                              556, NULL },+  { "eth",                               556, NULL },+  { "Scedilla",                          667, NULL },+  { "Ograve",                            778, NULL },+  { "Racute",                            722, NULL },+  { "partialdiff",                       476, NULL },+  { "uacute",                            556, NULL },+  { "braceleft",                         334, NULL },+  { "Thorn",                             667, NULL },+  { "zcaron",                            500, NULL },+  { "scommaaccent",                      500, NULL },+  { "ccedilla",                          500, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            556, NULL },+  { "Ocircumflex",                       778, NULL },+  { "Oacute",                            778, NULL },+  { "scedilla",                          500, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            556, NULL },+  { "racute",                            333, NULL },+  { "Tcaron",                            611, NULL },+  { "Eogonek",                           667, NULL },+  { "thorn",                             556, NULL },+  { "degree",                            400, NULL },+  { "registered",                        737, NULL },+  { "radical",                           453, NULL },+  { "Aring",                             667, NULL },+  { "percent",                           889, NULL },+  { "six",                               556, NULL },+  { "paragraph",                         537, NULL },+  { "dcaron",                            643, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               556, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            278, NULL },+  { "Lacute",                            556, NULL },+  { "ocircumflex",                       556, NULL },+  { "oacute",                            556, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      556, NULL },+  { "tcaron",                            317, NULL },+  { "eogonek",                           556, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     778, NULL },+  { "asciicircum",                       469, NULL },+  { "aring",                             556, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           556, NULL },+  { "bracketright",                      278, NULL },+  { "Iacute",                            278, NULL },+  { "ampersand",                         667, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            222, NULL },+  { "Ncaron",                            722, NULL },+  { "plus",                              584, NULL },+  { "uring",                             556, NULL },+  { "quotesinglbase",                    222, NULL },+  { "lcommaaccent",                      222, NULL },+  { "Yacute",                            667, NULL },+  { "ohungarumlaut",                     556, NULL },+  { "threesuperior",                     333, NULL },+  { "acute",                             333, NULL },+  { "section",                           556, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      333, NULL },+  { "ncaron",                            556, NULL },+  { "florin",                            556, NULL },+  { "yacute",                            500, NULL },+  { "Rcommaaccent",                      722, NULL },+  { "fi",                                500, NULL },+  { "fl",                                500, NULL },+  { "Acircumflex",                       667, NULL },+  { "Cacute",                            722, NULL },+  { "Icircumflex",                       278, NULL },+  { "guillemotleft",                     556, NULL },+  { "germandbls",                        611, NULL },+  { "Amacron",                           667, NULL },+  { "seven",                             556, NULL },+  { "Sacute",                            667, NULL },+  { "ordmasculine",                      365, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          556, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           278, NULL },+  { "rcommaaccent",                      333, NULL },+  { "Zdotaccent",                        611, NULL },+  { "acircumflex",                       556, NULL },+  { "cacute",                            500, NULL },+  { "Ecaron",                            667, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        334, NULL },+  { "quotedblright",                     333, NULL },+  { "amacron",                           556, NULL },+  { "sacute",                            500, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              556, NULL },+  { "currency",                          556, NULL },+  { "logicalnot",                        584, NULL },+  { "zdotaccent",                        500, NULL },+  { "Atilde",                            667, NULL },+  { "breve",                             333, NULL },+  { "bar",                               260, NULL },+  { "fraction",                          167, NULL },+  { "less",                              584, NULL },+  { "ecaron",                            556, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            278, NULL },+  { "period",                            278, NULL },+  { "Rcaron",                            722, NULL },+  { "Kcommaaccent",                      667, NULL },+  { "greater",                           584, NULL },+  { "atilde",                            556, NULL },+  { "brokenbar",                         260, NULL },+  { "quoteleft",                         222, NULL },+  { "Edotaccent",                        667, NULL },+  { "onesuperior",                       333, NULL }+};++static BuiltinFontWidth symbolWidthsTab[] = {+  { "bracketleftex",                     384, NULL },+  { "alpha",                             631, NULL },+  { "union",                             768, NULL },+  { "infinity",                          713, NULL },+  { "comma",                             250, NULL },+  { "copyrightsans",                     790, NULL },+  { "plusminus",                         549, NULL },+  { "arrowup",                           603, NULL },+  { "apple",                             790, NULL },+  { "parenleftbt",                       384, NULL },+  { "notelement",                        713, NULL },+  { "colon",                             278, NULL },+  { "beta",                              549, NULL },+  { "braceleftbt",                       494, NULL },+  { "Lambda",                            686, NULL },+  { "Phi",                               763, NULL },+  { "minus",                             549, NULL },+  { "space",                             250, NULL },+  { "Sigma",                             592, NULL },+  { "approxequal",                       549, NULL },+  { "minute",                            247, NULL },+  { "circleplus",                        768, NULL },+  { "Omicron",                           722, NULL },+  { "three",                             500, NULL },+  { "numbersign",                        500, NULL },+  { "lambda",                            549, NULL },+  { "phi",                               521, NULL },+  { "aleph",                             823, NULL },+  { "Tau",                               611, NULL },+  { "spade",                             753, NULL },+  { "logicaland",                        603, NULL },+  { "sigma",                             603, NULL },+  { "propersuperset",                    713, NULL },+  { "omicron",                           549, NULL },+  { "question",                          444, NULL },+  { "equal",                             549, NULL },+  { "Epsilon",                           611, NULL },+  { "emptyset",                          823, NULL },+  { "diamond",                           753, NULL },+  { "four",                              500, NULL },+  { "Mu",                                889, NULL },+  { "parenlefttp",                       384, NULL },+  { "club",                              753, NULL },+  { "bullet",                            460, NULL },+  { "Omega",                             768, NULL },+  { "tau",                               439, NULL },+  { "Upsilon",                           690, NULL },+  { "bracelefttp",                       494, NULL },+  { "heart",                             753, NULL },+  { "divide",                            549, NULL },+  { "epsilon",                           439, NULL },+  { "logicalor",                         603, NULL },+  { "parenleftex",                       384, NULL },+  { "greaterequal",                      549, NULL },+  { "mu",                                576, NULL },+  { "Nu",                                722, NULL },+  { "therefore",                         863, NULL },+  { "notsubset",                         713, NULL },+  { "omega",                             686, NULL },+  { "semicolon",                         278, NULL },+  { "element",                           713, NULL },+  { "upsilon",                           576, NULL },+  { "existential",                       549, NULL },+  { "integralbt",                        686, NULL },+  { "lessequal",                         549, NULL },+  { "phi1",                              603, NULL },+  { "lozenge",                           494, NULL },+  { "trademarkserif",                    890, NULL },+  { "parenright",                        333, NULL },+  { "reflexsuperset",                    713, NULL },+  { "sigma1",                            439, NULL },+  { "nu",                                521, NULL },+  { "Gamma",                             603, NULL },+  { "angleright",                        329, NULL },+  { "ellipsis",                         1000, NULL },+  { "Rho",                               556, NULL },+  { "parenrightbt",                      384, NULL },+  { "radicalex",                         500, NULL },+  { "eight",                             500, NULL },+  { "angleleft",                         329, NULL },+  { "arrowdbldown",                      603, NULL },+  { "congruent",                         549, NULL },+  { "Theta",                             741, NULL },+  { "intersection",                      768, NULL },+  { "Pi",                                768, NULL },+  { "slash",                             278, NULL },+  { "registerserif",                     790, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               500, NULL },+  { "gamma",                             411, NULL },+  { "bracketleft",                       333, NULL },+  { "rho",                               549, NULL },+  { "circlemultiply",                    768, NULL },+  { "Chi",                               722, NULL },+  { "theta",                             521, NULL },+  { "pi",                                549, NULL },+  { "integraltp",                        686, NULL },+  { "Eta",                               722, NULL },+  { "product",                           823, NULL },+  { "nine",                              500, NULL },+  { "five",                              500, NULL },+  { "propersubset",                      713, NULL },+  { "bracketrightbt",                    384, NULL },+  { "trademarksans",                     786, NULL },+  { "dotmath",                           250, NULL },+  { "integralex",                        686, NULL },+  { "chi",                               549, NULL },+  { "parenrighttp",                      384, NULL },+  { "eta",                               603, NULL },+  { "underscore",                        500, NULL },+  { "Euro",                              750, NULL },+  { "multiply",                          549, NULL },+  { "zero",                              500, NULL },+  { "partialdiff",                       494, NULL },+  { "angle",                             768, NULL },+  { "arrowdblleft",                      987, NULL },+  { "braceleft",                         480, NULL },+  { "parenrightex",                      384, NULL },+  { "Rfraktur",                          795, NULL },+  { "Zeta",                              611, NULL },+  { "braceex",                           494, NULL },+  { "arrowdblup",                        603, NULL },+  { "arrowdown",                         603, NULL },+  { "Ifraktur",                          686, NULL },+  { "degree",                            400, NULL },+  { "Iota",                              333, NULL },+  { "perpendicular",                     658, NULL },+  { "radical",                           549, NULL },+  { "asteriskmath",                      500, NULL },+  { "percent",                           833, NULL },+  { "zeta",                              494, NULL },+  { "six",                               500, NULL },+  { "two",                               500, NULL },+  { "weierstrass",                       987, NULL },+  { "summation",                         713, NULL },+  { "bracketrighttp",                    384, NULL },+  { "carriagereturn",                    658, NULL },+  { "suchthat",                          439, NULL },+  { "arrowvertex",                       603, NULL },+  { "Delta",                             612, NULL },+  { "iota",                              329, NULL },+  { "arrowhorizex",                     1000, NULL },+  { "bracketrightex",                    384, NULL },+  { "bracketright",                      333, NULL },+  { "ampersand",                         778, NULL },+  { "plus",                              549, NULL },+  { "proportional",                      713, NULL },+  { "delta",                             494, NULL },+  { "copyrightserif",                    790, NULL },+  { "bracerightmid",                     494, NULL },+  { "arrowleft",                         987, NULL },+  { "second",                            411, NULL },+  { "arrowdblboth",                     1042, NULL },+  { "florin",                            500, NULL },+  { "Psi",                               795, NULL },+  { "bracerightbt",                      494, NULL },+  { "bracketleftbt",                     384, NULL },+  { "seven",                             500, NULL },+  { "braceleftmid",                      494, NULL },+  { "notequal",                          549, NULL },+  { "psi",                               686, NULL },+  { "equivalence",                       549, NULL },+  { "universal",                         713, NULL },+  { "arrowdblright",                     987, NULL },+  { "braceright",                        480, NULL },+  { "reflexsubset",                      713, NULL },+  { "Xi",                                645, NULL },+  { "theta1",                            631, NULL },+  { "logicalnot",                        713, NULL },+  { "Kappa",                             722, NULL },+  { "similar",                           549, NULL },+  { "bar",                               200, NULL },+  { "fraction",                          167, NULL },+  { "less",                              549, NULL },+  { "registersans",                      790, NULL },+  { "omega1",                            713, NULL },+  { "exclam",                            333, NULL },+  { "Upsilon1",                          620, NULL },+  { "bracerighttp",                      494, NULL },+  { "xi",                                493, NULL },+  { "period",                            250, NULL },+  { "Alpha",                             722, NULL },+  { "arrowright",                        987, NULL },+  { "greater",                           549, NULL },+  { "bracketlefttp",                     384, NULL },+  { "kappa",                             549, NULL },+  { "gradient",                          713, NULL },+  { "integral",                          274, NULL },+  { "arrowboth",                        1042, NULL },+  { "Beta",                              667, NULL }+};++static BuiltinFontWidth timesBoldWidthsTab[] = {+  { "Ntilde",                            722, NULL },+  { "rcaron",                            444, NULL },+  { "kcommaaccent",                      556, NULL },+  { "Ncommaaccent",                      722, NULL },+  { "Zacute",                            667, NULL },+  { "comma",                             250, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         570, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        444, NULL },+  { "asciitilde",                        520, NULL },+  { "colon",                             333, NULL },+  { "onehalf",                           750, NULL },+  { "dollar",                            500, NULL },+  { "Lcaron",                            667, NULL },+  { "ntilde",                            556, NULL },+  { "Aogonek",                           722, NULL },+  { "ncommaaccent",                      556, NULL },+  { "minus",                             570, NULL },+  { "Iogonek",                           389, NULL },+  { "zacute",                            444, NULL },+  { "yen",                               500, NULL },+  { "space",                             250, NULL },+  { "Omacron",                           778, NULL },+  { "questiondown",                      500, NULL },+  { "emdash",                           1000, NULL },+  { "Agrave",                            722, NULL },+  { "three",                             500, NULL },+  { "numbersign",                        500, NULL },+  { "lcaron",                            394, NULL },+  { "A",                                 722, NULL },+  { "B",                                 667, NULL },+  { "C",                                 722, NULL },+  { "aogonek",                           500, NULL },+  { "D",                                 722, NULL },+  { "E",                                 667, NULL },+  { "onequarter",                        750, NULL },+  { "F",                                 611, NULL },+  { "G",                                 778, NULL },+  { "H",                                 778, NULL },+  { "I",                                 389, NULL },+  { "J",                                 500, NULL },+  { "K",                                 778, NULL },+  { "iogonek",                           278, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 667, NULL },+  { "periodcentered",                    250, NULL },+  { "M",                                 944, NULL },+  { "N",                                 722, NULL },+  { "omacron",                           500, NULL },+  { "Tcommaaccent",                      667, NULL },+  { "O",                                 778, NULL },+  { "P",                                 611, NULL },+  { "Q",                                 778, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 722, NULL },+  { "Aacute",                            722, NULL },+  { "caron",                             333, NULL },+  { "S",                                 556, NULL },+  { "T",                                 667, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            500, NULL },+  { "V",                                 722, NULL },+  { "W",                                1000, NULL },+  { "X",                                 722, NULL },+  { "question",                          500, NULL },+  { "equal",                             570, NULL },+  { "Y",                                 722, NULL },+  { "Z",                                 667, NULL },+  { "four",                              500, NULL },+  { "a",                                 500, NULL },+  { "Gcommaaccent",                      778, NULL },+  { "b",                                 556, NULL },+  { "c",                                 444, NULL },+  { "d",                                 556, NULL },+  { "e",                                 444, NULL },+  { "f",                                 333, NULL },+  { "g",                                 500, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 556, NULL },+  { "i",                                 278, NULL },+  { "Oslash",                            778, NULL },+  { "dagger",                            500, NULL },+  { "j",                                 333, NULL },+  { "k",                                 556, NULL },+  { "l",                                 278, NULL },+  { "m",                                 833, NULL },+  { "n",                                 556, NULL },+  { "tcommaaccent",                      333, NULL },+  { "o",                                 500, NULL },+  { "ordfeminine",                       300, NULL },+  { "ring",                              333, NULL },+  { "p",                                 556, NULL },+  { "q",                                 556, NULL },+  { "uhungarumlaut",                     556, NULL },+  { "r",                                 444, NULL },+  { "twosuperior",                       300, NULL },+  { "aacute",                            500, NULL },+  { "s",                                 389, NULL },+  { "OE",                               1000, NULL },+  { "t",                                 333, NULL },+  { "divide",                            570, NULL },+  { "u",                                 556, NULL },+  { "Ccaron",                            722, NULL },+  { "v",                                 500, NULL },+  { "w",                                 722, NULL },+  { "x",                                 500, NULL },+  { "y",                                 500, NULL },+  { "z",                                 444, NULL },+  { "Gbreve",                            778, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        389, NULL },+  { "Nacute",                            722, NULL },+  { "quotedbl",                          555, NULL },+  { "gcommaaccent",                      500, NULL },+  { "mu",                                556, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            556, NULL },+  { "Lslash",                            667, NULL },+  { "semicolon",                         333, NULL },+  { "oslash",                            500, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           494, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            444, NULL },+  { "Ecircumflex",                       667, NULL },+  { "gbreve",                            500, NULL },+  { "trademark",                        1000, NULL },+  { "daggerdbl",                         500, NULL },+  { "nacute",                            556, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            778, NULL },+  { "Emacron",                           667, NULL },+  { "ellipsis",                         1000, NULL },+  { "scaron",                            389, NULL },+  { "AE",                               1000, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            278, NULL },+  { "quotedblleft",                      500, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       278, NULL },+  { "eight",                             500, NULL },+  { "exclamdown",                        333, NULL },+  { "endash",                            500, NULL },+  { "oe",                                722, NULL },+  { "Abreve",                            722, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       444, NULL },+  { "Adieresis",                         722, NULL },+  { "copyright",                         747, NULL },+  { "Egrave",                            667, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         667, NULL },+  { "otilde",                            500, NULL },+  { "Idieresis",                         389, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               500, NULL },+  { "emacron",                           444, NULL },+  { "Odieresis",                         778, NULL },+  { "ucircumflex",                       556, NULL },+  { "bracketleft",                       333, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        333, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         722, NULL },+  { "umacron",                           556, NULL },+  { "abreve",                            500, NULL },+  { "Eacute",                            667, NULL },+  { "adieresis",                         500, NULL },+  { "egrave",                            444, NULL },+  { "edieresis",                         444, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                722, NULL },+  { "asterisk",                          500, NULL },+  { "odieresis",                         500, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            556, NULL },+  { "nine",                              500, NULL },+  { "five",                              500, NULL },+  { "udieresis",                         556, NULL },+  { "Zcaron",                            667, NULL },+  { "Scommaaccent",                      556, NULL },+  { "threequarters",                     750, NULL },+  { "guillemotright",                    500, NULL },+  { "Ccedilla",                          722, NULL },+  { "ydieresis",                         500, NULL },+  { "tilde",                             333, NULL },+  { "at",                                930, NULL },+  { "eacute",                            444, NULL },+  { "underscore",                        500, NULL },+  { "Euro",                              500, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          570, NULL },+  { "zero",                              500, NULL },+  { "eth",                               500, NULL },+  { "Scedilla",                          556, NULL },+  { "Ograve",                            778, NULL },+  { "Racute",                            722, NULL },+  { "partialdiff",                       494, NULL },+  { "uacute",                            556, NULL },+  { "braceleft",                         394, NULL },+  { "Thorn",                             611, NULL },+  { "zcaron",                            444, NULL },+  { "scommaaccent",                      389, NULL },+  { "ccedilla",                          444, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            556, NULL },+  { "Ocircumflex",                       778, NULL },+  { "Oacute",                            778, NULL },+  { "scedilla",                          389, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            500, NULL },+  { "racute",                            444, NULL },+  { "Tcaron",                            667, NULL },+  { "Eogonek",                           667, NULL },+  { "thorn",                             556, NULL },+  { "degree",                            400, NULL },+  { "registered",                        747, NULL },+  { "radical",                           549, NULL },+  { "Aring",                             722, NULL },+  { "percent",                          1000, NULL },+  { "six",                               500, NULL },+  { "paragraph",                         540, NULL },+  { "dcaron",                            672, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               500, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            389, NULL },+  { "Lacute",                            667, NULL },+  { "ocircumflex",                       500, NULL },+  { "oacute",                            500, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      667, NULL },+  { "tcaron",                            416, NULL },+  { "eogonek",                           444, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     778, NULL },+  { "asciicircum",                       581, NULL },+  { "aring",                             500, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           556, NULL },+  { "bracketright",                      333, NULL },+  { "Iacute",                            389, NULL },+  { "ampersand",                         833, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            278, NULL },+  { "Ncaron",                            722, NULL },+  { "plus",                              570, NULL },+  { "uring",                             556, NULL },+  { "quotesinglbase",                    333, NULL },+  { "lcommaaccent",                      278, NULL },+  { "Yacute",                            722, NULL },+  { "ohungarumlaut",                     500, NULL },+  { "threesuperior",                     300, NULL },+  { "acute",                             333, NULL },+  { "section",                           500, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      500, NULL },+  { "ncaron",                            556, NULL },+  { "florin",                            500, NULL },+  { "yacute",                            500, NULL },+  { "Rcommaaccent",                      722, NULL },+  { "fi",                                556, NULL },+  { "fl",                                556, NULL },+  { "Acircumflex",                       722, NULL },+  { "Cacute",                            722, NULL },+  { "Icircumflex",                       389, NULL },+  { "guillemotleft",                     500, NULL },+  { "germandbls",                        556, NULL },+  { "Amacron",                           722, NULL },+  { "seven",                             500, NULL },+  { "Sacute",                            556, NULL },+  { "ordmasculine",                      330, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          500, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           389, NULL },+  { "rcommaaccent",                      444, NULL },+  { "Zdotaccent",                        667, NULL },+  { "acircumflex",                       500, NULL },+  { "cacute",                            444, NULL },+  { "Ecaron",                            667, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        394, NULL },+  { "quotedblright",                     500, NULL },+  { "amacron",                           500, NULL },+  { "sacute",                            389, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              500, NULL },+  { "currency",                          500, NULL },+  { "logicalnot",                        570, NULL },+  { "zdotaccent",                        444, NULL },+  { "Atilde",                            722, NULL },+  { "breve",                             333, NULL },+  { "bar",                               220, NULL },+  { "fraction",                          167, NULL },+  { "less",                              570, NULL },+  { "ecaron",                            444, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            333, NULL },+  { "period",                            250, NULL },+  { "Rcaron",                            722, NULL },+  { "Kcommaaccent",                      778, NULL },+  { "greater",                           570, NULL },+  { "atilde",                            500, NULL },+  { "brokenbar",                         220, NULL },+  { "quoteleft",                         333, NULL },+  { "Edotaccent",                        667, NULL },+  { "onesuperior",                       300, NULL }+};++static BuiltinFontWidth timesBoldItalicWidthsTab[] = {+  { "Ntilde",                            722, NULL },+  { "rcaron",                            389, NULL },+  { "kcommaaccent",                      500, NULL },+  { "Ncommaaccent",                      722, NULL },+  { "Zacute",                            611, NULL },+  { "comma",                             250, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         570, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        444, NULL },+  { "asciitilde",                        570, NULL },+  { "colon",                             333, NULL },+  { "onehalf",                           750, NULL },+  { "dollar",                            500, NULL },+  { "Lcaron",                            611, NULL },+  { "ntilde",                            556, NULL },+  { "Aogonek",                           667, NULL },+  { "ncommaaccent",                      556, NULL },+  { "minus",                             606, NULL },+  { "Iogonek",                           389, NULL },+  { "zacute",                            389, NULL },+  { "yen",                               500, NULL },+  { "space",                             250, NULL },+  { "Omacron",                           722, NULL },+  { "questiondown",                      500, NULL },+  { "emdash",                           1000, NULL },+  { "Agrave",                            667, NULL },+  { "three",                             500, NULL },+  { "numbersign",                        500, NULL },+  { "lcaron",                            382, NULL },+  { "A",                                 667, NULL },+  { "B",                                 667, NULL },+  { "C",                                 667, NULL },+  { "aogonek",                           500, NULL },+  { "D",                                 722, NULL },+  { "E",                                 667, NULL },+  { "onequarter",                        750, NULL },+  { "F",                                 667, NULL },+  { "G",                                 722, NULL },+  { "H",                                 778, NULL },+  { "I",                                 389, NULL },+  { "J",                                 500, NULL },+  { "K",                                 667, NULL },+  { "iogonek",                           278, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 611, NULL },+  { "periodcentered",                    250, NULL },+  { "M",                                 889, NULL },+  { "N",                                 722, NULL },+  { "omacron",                           500, NULL },+  { "Tcommaaccent",                      611, NULL },+  { "O",                                 722, NULL },+  { "P",                                 611, NULL },+  { "Q",                                 722, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 667, NULL },+  { "Aacute",                            667, NULL },+  { "caron",                             333, NULL },+  { "S",                                 556, NULL },+  { "T",                                 611, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            500, NULL },+  { "V",                                 667, NULL },+  { "W",                                 889, NULL },+  { "X",                                 667, NULL },+  { "question",                          500, NULL },+  { "equal",                             570, NULL },+  { "Y",                                 611, NULL },+  { "Z",                                 611, NULL },+  { "four",                              500, NULL },+  { "a",                                 500, NULL },+  { "Gcommaaccent",                      722, NULL },+  { "b",                                 500, NULL },+  { "c",                                 444, NULL },+  { "d",                                 500, NULL },+  { "e",                                 444, NULL },+  { "f",                                 333, NULL },+  { "g",                                 500, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 556, NULL },+  { "i",                                 278, NULL },+  { "Oslash",                            722, NULL },+  { "dagger",                            500, NULL },+  { "j",                                 278, NULL },+  { "k",                                 500, NULL },+  { "l",                                 278, NULL },+  { "m",                                 778, NULL },+  { "n",                                 556, NULL },+  { "tcommaaccent",                      278, NULL },+  { "o",                                 500, NULL },+  { "ordfeminine",                       266, NULL },+  { "ring",                              333, NULL },+  { "p",                                 500, NULL },+  { "q",                                 500, NULL },+  { "uhungarumlaut",                     556, NULL },+  { "r",                                 389, NULL },+  { "twosuperior",                       300, NULL },+  { "aacute",                            500, NULL },+  { "s",                                 389, NULL },+  { "OE",                                944, NULL },+  { "t",                                 278, NULL },+  { "divide",                            570, NULL },+  { "u",                                 556, NULL },+  { "Ccaron",                            667, NULL },+  { "v",                                 444, NULL },+  { "w",                                 667, NULL },+  { "x",                                 500, NULL },+  { "y",                                 444, NULL },+  { "z",                                 389, NULL },+  { "Gbreve",                            722, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        389, NULL },+  { "Nacute",                            722, NULL },+  { "quotedbl",                          555, NULL },+  { "gcommaaccent",                      500, NULL },+  { "mu",                                576, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            556, NULL },+  { "Lslash",                            611, NULL },+  { "semicolon",                         333, NULL },+  { "oslash",                            500, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           494, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            444, NULL },+  { "Ecircumflex",                       667, NULL },+  { "gbreve",                            500, NULL },+  { "trademark",                        1000, NULL },+  { "daggerdbl",                         500, NULL },+  { "nacute",                            556, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            722, NULL },+  { "Emacron",                           667, NULL },+  { "ellipsis",                         1000, NULL },+  { "scaron",                            389, NULL },+  { "AE",                                944, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            278, NULL },+  { "quotedblleft",                      500, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       278, NULL },+  { "eight",                             500, NULL },+  { "exclamdown",                        389, NULL },+  { "endash",                            500, NULL },+  { "oe",                                722, NULL },+  { "Abreve",                            667, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       444, NULL },+  { "Adieresis",                         667, NULL },+  { "copyright",                         747, NULL },+  { "Egrave",                            667, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         667, NULL },+  { "otilde",                            500, NULL },+  { "Idieresis",                         389, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               500, NULL },+  { "emacron",                           444, NULL },+  { "Odieresis",                         722, NULL },+  { "ucircumflex",                       556, NULL },+  { "bracketleft",                       333, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        333, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         611, NULL },+  { "umacron",                           556, NULL },+  { "abreve",                            500, NULL },+  { "Eacute",                            667, NULL },+  { "adieresis",                         500, NULL },+  { "egrave",                            444, NULL },+  { "edieresis",                         444, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                722, NULL },+  { "asterisk",                          500, NULL },+  { "odieresis",                         500, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            556, NULL },+  { "nine",                              500, NULL },+  { "five",                              500, NULL },+  { "udieresis",                         556, NULL },+  { "Zcaron",                            611, NULL },+  { "Scommaaccent",                      556, NULL },+  { "threequarters",                     750, NULL },+  { "guillemotright",                    500, NULL },+  { "Ccedilla",                          667, NULL },+  { "ydieresis",                         444, NULL },+  { "tilde",                             333, NULL },+  { "at",                                832, NULL },+  { "eacute",                            444, NULL },+  { "underscore",                        500, NULL },+  { "Euro",                              500, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          570, NULL },+  { "zero",                              500, NULL },+  { "eth",                               500, NULL },+  { "Scedilla",                          556, NULL },+  { "Ograve",                            722, NULL },+  { "Racute",                            667, NULL },+  { "partialdiff",                       494, NULL },+  { "uacute",                            556, NULL },+  { "braceleft",                         348, NULL },+  { "Thorn",                             611, NULL },+  { "zcaron",                            389, NULL },+  { "scommaaccent",                      389, NULL },+  { "ccedilla",                          444, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            500, NULL },+  { "Ocircumflex",                       722, NULL },+  { "Oacute",                            722, NULL },+  { "scedilla",                          389, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            500, NULL },+  { "racute",                            389, NULL },+  { "Tcaron",                            611, NULL },+  { "Eogonek",                           667, NULL },+  { "thorn",                             500, NULL },+  { "degree",                            400, NULL },+  { "registered",                        747, NULL },+  { "radical",                           549, NULL },+  { "Aring",                             667, NULL },+  { "percent",                           833, NULL },+  { "six",                               500, NULL },+  { "paragraph",                         500, NULL },+  { "dcaron",                            608, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               500, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            389, NULL },+  { "Lacute",                            611, NULL },+  { "ocircumflex",                       500, NULL },+  { "oacute",                            500, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      611, NULL },+  { "tcaron",                            366, NULL },+  { "eogonek",                           444, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     722, NULL },+  { "asciicircum",                       570, NULL },+  { "aring",                             500, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           556, NULL },+  { "bracketright",                      333, NULL },+  { "Iacute",                            389, NULL },+  { "ampersand",                         778, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            278, NULL },+  { "Ncaron",                            722, NULL },+  { "plus",                              570, NULL },+  { "uring",                             556, NULL },+  { "quotesinglbase",                    333, NULL },+  { "lcommaaccent",                      278, NULL },+  { "Yacute",                            611, NULL },+  { "ohungarumlaut",                     500, NULL },+  { "threesuperior",                     300, NULL },+  { "acute",                             333, NULL },+  { "section",                           500, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      500, NULL },+  { "ncaron",                            556, NULL },+  { "florin",                            500, NULL },+  { "yacute",                            444, NULL },+  { "Rcommaaccent",                      667, NULL },+  { "fi",                                556, NULL },+  { "fl",                                556, NULL },+  { "Acircumflex",                       667, NULL },+  { "Cacute",                            667, NULL },+  { "Icircumflex",                       389, NULL },+  { "guillemotleft",                     500, NULL },+  { "germandbls",                        500, NULL },+  { "Amacron",                           667, NULL },+  { "seven",                             500, NULL },+  { "Sacute",                            556, NULL },+  { "ordmasculine",                      300, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          500, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           389, NULL },+  { "rcommaaccent",                      389, NULL },+  { "Zdotaccent",                        611, NULL },+  { "acircumflex",                       500, NULL },+  { "cacute",                            444, NULL },+  { "Ecaron",                            667, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        348, NULL },+  { "quotedblright",                     500, NULL },+  { "amacron",                           500, NULL },+  { "sacute",                            389, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              500, NULL },+  { "currency",                          500, NULL },+  { "logicalnot",                        606, NULL },+  { "zdotaccent",                        389, NULL },+  { "Atilde",                            667, NULL },+  { "breve",                             333, NULL },+  { "bar",                               220, NULL },+  { "fraction",                          167, NULL },+  { "less",                              570, NULL },+  { "ecaron",                            444, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            389, NULL },+  { "period",                            250, NULL },+  { "Rcaron",                            667, NULL },+  { "Kcommaaccent",                      667, NULL },+  { "greater",                           570, NULL },+  { "atilde",                            500, NULL },+  { "brokenbar",                         220, NULL },+  { "quoteleft",                         333, NULL },+  { "Edotaccent",                        667, NULL },+  { "onesuperior",                       300, NULL }+};++static BuiltinFontWidth timesItalicWidthsTab[] = {+  { "Ntilde",                            667, NULL },+  { "rcaron",                            389, NULL },+  { "kcommaaccent",                      444, NULL },+  { "Ncommaaccent",                      667, NULL },+  { "Zacute",                            556, NULL },+  { "comma",                             250, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         675, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        444, NULL },+  { "asciitilde",                        541, NULL },+  { "colon",                             333, NULL },+  { "onehalf",                           750, NULL },+  { "dollar",                            500, NULL },+  { "Lcaron",                            611, NULL },+  { "ntilde",                            500, NULL },+  { "Aogonek",                           611, NULL },+  { "ncommaaccent",                      500, NULL },+  { "minus",                             675, NULL },+  { "Iogonek",                           333, NULL },+  { "zacute",                            389, NULL },+  { "yen",                               500, NULL },+  { "space",                             250, NULL },+  { "Omacron",                           722, NULL },+  { "questiondown",                      500, NULL },+  { "emdash",                            889, NULL },+  { "Agrave",                            611, NULL },+  { "three",                             500, NULL },+  { "numbersign",                        500, NULL },+  { "lcaron",                            300, NULL },+  { "A",                                 611, NULL },+  { "B",                                 611, NULL },+  { "C",                                 667, NULL },+  { "aogonek",                           500, NULL },+  { "D",                                 722, NULL },+  { "E",                                 611, NULL },+  { "onequarter",                        750, NULL },+  { "F",                                 611, NULL },+  { "G",                                 722, NULL },+  { "H",                                 722, NULL },+  { "I",                                 333, NULL },+  { "J",                                 444, NULL },+  { "K",                                 667, NULL },+  { "iogonek",                           278, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 556, NULL },+  { "periodcentered",                    250, NULL },+  { "M",                                 833, NULL },+  { "N",                                 667, NULL },+  { "omacron",                           500, NULL },+  { "Tcommaaccent",                      556, NULL },+  { "O",                                 722, NULL },+  { "P",                                 611, NULL },+  { "Q",                                 722, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 611, NULL },+  { "Aacute",                            611, NULL },+  { "caron",                             333, NULL },+  { "S",                                 500, NULL },+  { "T",                                 556, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            500, NULL },+  { "V",                                 611, NULL },+  { "W",                                 833, NULL },+  { "X",                                 611, NULL },+  { "question",                          500, NULL },+  { "equal",                             675, NULL },+  { "Y",                                 556, NULL },+  { "Z",                                 556, NULL },+  { "four",                              500, NULL },+  { "a",                                 500, NULL },+  { "Gcommaaccent",                      722, NULL },+  { "b",                                 500, NULL },+  { "c",                                 444, NULL },+  { "d",                                 500, NULL },+  { "e",                                 444, NULL },+  { "f",                                 278, NULL },+  { "g",                                 500, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 500, NULL },+  { "i",                                 278, NULL },+  { "Oslash",                            722, NULL },+  { "dagger",                            500, NULL },+  { "j",                                 278, NULL },+  { "k",                                 444, NULL },+  { "l",                                 278, NULL },+  { "m",                                 722, NULL },+  { "n",                                 500, NULL },+  { "tcommaaccent",                      278, NULL },+  { "o",                                 500, NULL },+  { "ordfeminine",                       276, NULL },+  { "ring",                              333, NULL },+  { "p",                                 500, NULL },+  { "q",                                 500, NULL },+  { "uhungarumlaut",                     500, NULL },+  { "r",                                 389, NULL },+  { "twosuperior",                       300, NULL },+  { "aacute",                            500, NULL },+  { "s",                                 389, NULL },+  { "OE",                                944, NULL },+  { "t",                                 278, NULL },+  { "divide",                            675, NULL },+  { "u",                                 500, NULL },+  { "Ccaron",                            667, NULL },+  { "v",                                 444, NULL },+  { "w",                                 667, NULL },+  { "x",                                 444, NULL },+  { "y",                                 444, NULL },+  { "z",                                 389, NULL },+  { "Gbreve",                            722, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        333, NULL },+  { "Nacute",                            667, NULL },+  { "quotedbl",                          420, NULL },+  { "gcommaaccent",                      500, NULL },+  { "mu",                                500, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            500, NULL },+  { "Lslash",                            556, NULL },+  { "semicolon",                         333, NULL },+  { "oslash",                            500, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           471, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            444, NULL },+  { "Ecircumflex",                       611, NULL },+  { "gbreve",                            500, NULL },+  { "trademark",                         980, NULL },+  { "daggerdbl",                         500, NULL },+  { "nacute",                            500, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            722, NULL },+  { "Emacron",                           611, NULL },+  { "ellipsis",                          889, NULL },+  { "scaron",                            389, NULL },+  { "AE",                                889, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            278, NULL },+  { "quotedblleft",                      556, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       214, NULL },+  { "eight",                             500, NULL },+  { "exclamdown",                        389, NULL },+  { "endash",                            500, NULL },+  { "oe",                                667, NULL },+  { "Abreve",                            611, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       444, NULL },+  { "Adieresis",                         611, NULL },+  { "copyright",                         760, NULL },+  { "Egrave",                            611, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         611, NULL },+  { "otilde",                            500, NULL },+  { "Idieresis",                         333, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               500, NULL },+  { "emacron",                           444, NULL },+  { "Odieresis",                         722, NULL },+  { "ucircumflex",                       500, NULL },+  { "bracketleft",                       389, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        333, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         556, NULL },+  { "umacron",                           500, NULL },+  { "abreve",                            500, NULL },+  { "Eacute",                            611, NULL },+  { "adieresis",                         500, NULL },+  { "egrave",                            444, NULL },+  { "edieresis",                         444, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                667, NULL },+  { "asterisk",                          500, NULL },+  { "odieresis",                         500, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            500, NULL },+  { "nine",                              500, NULL },+  { "five",                              500, NULL },+  { "udieresis",                         500, NULL },+  { "Zcaron",                            556, NULL },+  { "Scommaaccent",                      500, NULL },+  { "threequarters",                     750, NULL },+  { "guillemotright",                    500, NULL },+  { "Ccedilla",                          667, NULL },+  { "ydieresis",                         444, NULL },+  { "tilde",                             333, NULL },+  { "at",                                920, NULL },+  { "eacute",                            444, NULL },+  { "underscore",                        500, NULL },+  { "Euro",                              500, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          675, NULL },+  { "zero",                              500, NULL },+  { "eth",                               500, NULL },+  { "Scedilla",                          500, NULL },+  { "Ograve",                            722, NULL },+  { "Racute",                            611, NULL },+  { "partialdiff",                       476, NULL },+  { "uacute",                            500, NULL },+  { "braceleft",                         400, NULL },+  { "Thorn",                             611, NULL },+  { "zcaron",                            389, NULL },+  { "scommaaccent",                      389, NULL },+  { "ccedilla",                          444, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            500, NULL },+  { "Ocircumflex",                       722, NULL },+  { "Oacute",                            722, NULL },+  { "scedilla",                          389, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            500, NULL },+  { "racute",                            389, NULL },+  { "Tcaron",                            556, NULL },+  { "Eogonek",                           611, NULL },+  { "thorn",                             500, NULL },+  { "degree",                            400, NULL },+  { "registered",                        760, NULL },+  { "radical",                           453, NULL },+  { "Aring",                             611, NULL },+  { "percent",                           833, NULL },+  { "six",                               500, NULL },+  { "paragraph",                         523, NULL },+  { "dcaron",                            544, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               500, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            333, NULL },+  { "Lacute",                            556, NULL },+  { "ocircumflex",                       500, NULL },+  { "oacute",                            500, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      556, NULL },+  { "tcaron",                            300, NULL },+  { "eogonek",                           444, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     722, NULL },+  { "asciicircum",                       422, NULL },+  { "aring",                             500, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           500, NULL },+  { "bracketright",                      389, NULL },+  { "Iacute",                            333, NULL },+  { "ampersand",                         778, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            278, NULL },+  { "Ncaron",                            667, NULL },+  { "plus",                              675, NULL },+  { "uring",                             500, NULL },+  { "quotesinglbase",                    333, NULL },+  { "lcommaaccent",                      278, NULL },+  { "Yacute",                            556, NULL },+  { "ohungarumlaut",                     500, NULL },+  { "threesuperior",                     300, NULL },+  { "acute",                             333, NULL },+  { "section",                           500, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      556, NULL },+  { "ncaron",                            500, NULL },+  { "florin",                            500, NULL },+  { "yacute",                            444, NULL },+  { "Rcommaaccent",                      611, NULL },+  { "fi",                                500, NULL },+  { "fl",                                500, NULL },+  { "Acircumflex",                       611, NULL },+  { "Cacute",                            667, NULL },+  { "Icircumflex",                       333, NULL },+  { "guillemotleft",                     500, NULL },+  { "germandbls",                        500, NULL },+  { "Amacron",                           611, NULL },+  { "seven",                             500, NULL },+  { "Sacute",                            500, NULL },+  { "ordmasculine",                      310, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          500, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           333, NULL },+  { "rcommaaccent",                      389, NULL },+  { "Zdotaccent",                        556, NULL },+  { "acircumflex",                       500, NULL },+  { "cacute",                            444, NULL },+  { "Ecaron",                            611, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        400, NULL },+  { "quotedblright",                     556, NULL },+  { "amacron",                           500, NULL },+  { "sacute",                            389, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              500, NULL },+  { "currency",                          500, NULL },+  { "logicalnot",                        675, NULL },+  { "zdotaccent",                        389, NULL },+  { "Atilde",                            611, NULL },+  { "breve",                             333, NULL },+  { "bar",                               275, NULL },+  { "fraction",                          167, NULL },+  { "less",                              675, NULL },+  { "ecaron",                            444, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            333, NULL },+  { "period",                            250, NULL },+  { "Rcaron",                            611, NULL },+  { "Kcommaaccent",                      667, NULL },+  { "greater",                           675, NULL },+  { "atilde",                            500, NULL },+  { "brokenbar",                         275, NULL },+  { "quoteleft",                         333, NULL },+  { "Edotaccent",                        611, NULL },+  { "onesuperior",                       300, NULL }+};++static BuiltinFontWidth timesRomanWidthsTab[] = {+  { "Ntilde",                            722, NULL },+  { "rcaron",                            333, NULL },+  { "kcommaaccent",                      500, NULL },+  { "Ncommaaccent",                      722, NULL },+  { "Zacute",                            611, NULL },+  { "comma",                             250, NULL },+  { "cedilla",                           333, NULL },+  { "plusminus",                         564, NULL },+  { "circumflex",                        333, NULL },+  { "dotaccent",                         333, NULL },+  { "edotaccent",                        444, NULL },+  { "asciitilde",                        541, NULL },+  { "colon",                             278, NULL },+  { "onehalf",                           750, NULL },+  { "dollar",                            500, NULL },+  { "Lcaron",                            611, NULL },+  { "ntilde",                            500, NULL },+  { "Aogonek",                           722, NULL },+  { "ncommaaccent",                      500, NULL },+  { "minus",                             564, NULL },+  { "Iogonek",                           333, NULL },+  { "zacute",                            444, NULL },+  { "yen",                               500, NULL },+  { "space",                             250, NULL },+  { "Omacron",                           722, NULL },+  { "questiondown",                      444, NULL },+  { "emdash",                           1000, NULL },+  { "Agrave",                            722, NULL },+  { "three",                             500, NULL },+  { "numbersign",                        500, NULL },+  { "lcaron",                            344, NULL },+  { "A",                                 722, NULL },+  { "B",                                 667, NULL },+  { "C",                                 667, NULL },+  { "aogonek",                           444, NULL },+  { "D",                                 722, NULL },+  { "E",                                 611, NULL },+  { "onequarter",                        750, NULL },+  { "F",                                 556, NULL },+  { "G",                                 722, NULL },+  { "H",                                 722, NULL },+  { "I",                                 333, NULL },+  { "J",                                 389, NULL },+  { "K",                                 722, NULL },+  { "iogonek",                           278, NULL },+  { "backslash",                         278, NULL },+  { "L",                                 611, NULL },+  { "periodcentered",                    250, NULL },+  { "M",                                 889, NULL },+  { "N",                                 722, NULL },+  { "omacron",                           500, NULL },+  { "Tcommaaccent",                      611, NULL },+  { "O",                                 722, NULL },+  { "P",                                 556, NULL },+  { "Q",                                 722, NULL },+  { "Uhungarumlaut",                     722, NULL },+  { "R",                                 667, NULL },+  { "Aacute",                            722, NULL },+  { "caron",                             333, NULL },+  { "S",                                 556, NULL },+  { "T",                                 611, NULL },+  { "U",                                 722, NULL },+  { "agrave",                            444, NULL },+  { "V",                                 722, NULL },+  { "W",                                 944, NULL },+  { "X",                                 722, NULL },+  { "question",                          444, NULL },+  { "equal",                             564, NULL },+  { "Y",                                 722, NULL },+  { "Z",                                 611, NULL },+  { "four",                              500, NULL },+  { "a",                                 444, NULL },+  { "Gcommaaccent",                      722, NULL },+  { "b",                                 500, NULL },+  { "c",                                 444, NULL },+  { "d",                                 500, NULL },+  { "e",                                 444, NULL },+  { "f",                                 333, NULL },+  { "g",                                 500, NULL },+  { "bullet",                            350, NULL },+  { "h",                                 500, NULL },+  { "i",                                 278, NULL },+  { "Oslash",                            722, NULL },+  { "dagger",                            500, NULL },+  { "j",                                 278, NULL },+  { "k",                                 500, NULL },+  { "l",                                 278, NULL },+  { "m",                                 778, NULL },+  { "n",                                 500, NULL },+  { "tcommaaccent",                      278, NULL },+  { "o",                                 500, NULL },+  { "ordfeminine",                       276, NULL },+  { "ring",                              333, NULL },+  { "p",                                 500, NULL },+  { "q",                                 500, NULL },+  { "uhungarumlaut",                     500, NULL },+  { "r",                                 333, NULL },+  { "twosuperior",                       300, NULL },+  { "aacute",                            444, NULL },+  { "s",                                 389, NULL },+  { "OE",                                889, NULL },+  { "t",                                 278, NULL },+  { "divide",                            564, NULL },+  { "u",                                 500, NULL },+  { "Ccaron",                            667, NULL },+  { "v",                                 500, NULL },+  { "w",                                 722, NULL },+  { "x",                                 500, NULL },+  { "y",                                 500, NULL },+  { "z",                                 444, NULL },+  { "Gbreve",                            722, NULL },+  { "commaaccent",                       250, NULL },+  { "hungarumlaut",                      333, NULL },+  { "Idotaccent",                        333, NULL },+  { "Nacute",                            722, NULL },+  { "quotedbl",                          408, NULL },+  { "gcommaaccent",                      500, NULL },+  { "mu",                                500, NULL },+  { "greaterequal",                      549, NULL },+  { "Scaron",                            556, NULL },+  { "Lslash",                            611, NULL },+  { "semicolon",                         278, NULL },+  { "oslash",                            500, NULL },+  { "lessequal",                         549, NULL },+  { "lozenge",                           471, NULL },+  { "parenright",                        333, NULL },+  { "ccaron",                            444, NULL },+  { "Ecircumflex",                       611, NULL },+  { "gbreve",                            500, NULL },+  { "trademark",                         980, NULL },+  { "daggerdbl",                         500, NULL },+  { "nacute",                            500, NULL },+  { "macron",                            333, NULL },+  { "Otilde",                            722, NULL },+  { "Emacron",                           611, NULL },+  { "ellipsis",                         1000, NULL },+  { "scaron",                            389, NULL },+  { "AE",                                889, NULL },+  { "Ucircumflex",                       722, NULL },+  { "lslash",                            278, NULL },+  { "quotedblleft",                      444, NULL },+  { "guilsinglright",                    333, NULL },+  { "hyphen",                            333, NULL },+  { "quotesingle",                       180, NULL },+  { "eight",                             500, NULL },+  { "exclamdown",                        333, NULL },+  { "endash",                            500, NULL },+  { "oe",                                722, NULL },+  { "Abreve",                            722, NULL },+  { "Umacron",                           722, NULL },+  { "ecircumflex",                       444, NULL },+  { "Adieresis",                         722, NULL },+  { "copyright",                         760, NULL },+  { "Egrave",                            611, NULL },+  { "slash",                             278, NULL },+  { "Edieresis",                         611, NULL },+  { "otilde",                            500, NULL },+  { "Idieresis",                         333, NULL },+  { "parenleft",                         333, NULL },+  { "one",                               500, NULL },+  { "emacron",                           444, NULL },+  { "Odieresis",                         722, NULL },+  { "ucircumflex",                       500, NULL },+  { "bracketleft",                       333, NULL },+  { "Ugrave",                            722, NULL },+  { "quoteright",                        333, NULL },+  { "Udieresis",                         722, NULL },+  { "perthousand",                      1000, NULL },+  { "Ydieresis",                         722, NULL },+  { "umacron",                           500, NULL },+  { "abreve",                            444, NULL },+  { "Eacute",                            611, NULL },+  { "adieresis",                         444, NULL },+  { "egrave",                            444, NULL },+  { "edieresis",                         444, NULL },+  { "idieresis",                         278, NULL },+  { "Eth",                               722, NULL },+  { "ae",                                667, NULL },+  { "asterisk",                          500, NULL },+  { "odieresis",                         500, NULL },+  { "Uacute",                            722, NULL },+  { "ugrave",                            500, NULL },+  { "nine",                              500, NULL },+  { "five",                              500, NULL },+  { "udieresis",                         500, NULL },+  { "Zcaron",                            611, NULL },+  { "Scommaaccent",                      556, NULL },+  { "threequarters",                     750, NULL },+  { "guillemotright",                    500, NULL },+  { "Ccedilla",                          667, NULL },+  { "ydieresis",                         500, NULL },+  { "tilde",                             333, NULL },+  { "at",                                921, NULL },+  { "eacute",                            444, NULL },+  { "underscore",                        500, NULL },+  { "Euro",                              500, NULL },+  { "Dcroat",                            722, NULL },+  { "multiply",                          564, NULL },+  { "zero",                              500, NULL },+  { "eth",                               500, NULL },+  { "Scedilla",                          556, NULL },+  { "Ograve",                            722, NULL },+  { "Racute",                            667, NULL },+  { "partialdiff",                       476, NULL },+  { "uacute",                            500, NULL },+  { "braceleft",                         480, NULL },+  { "Thorn",                             556, NULL },+  { "zcaron",                            444, NULL },+  { "scommaaccent",                      389, NULL },+  { "ccedilla",                          444, NULL },+  { "Dcaron",                            722, NULL },+  { "dcroat",                            500, NULL },+  { "Ocircumflex",                       722, NULL },+  { "Oacute",                            722, NULL },+  { "scedilla",                          389, NULL },+  { "ogonek",                            333, NULL },+  { "ograve",                            500, NULL },+  { "racute",                            333, NULL },+  { "Tcaron",                            611, NULL },+  { "Eogonek",                           611, NULL },+  { "thorn",                             500, NULL },+  { "degree",                            400, NULL },+  { "registered",                        760, NULL },+  { "radical",                           453, NULL },+  { "Aring",                             722, NULL },+  { "percent",                           833, NULL },+  { "six",                               500, NULL },+  { "paragraph",                         453, NULL },+  { "dcaron",                            588, NULL },+  { "Uogonek",                           722, NULL },+  { "two",                               500, NULL },+  { "summation",                         600, NULL },+  { "Igrave",                            333, NULL },+  { "Lacute",                            611, NULL },+  { "ocircumflex",                       500, NULL },+  { "oacute",                            500, NULL },+  { "Uring",                             722, NULL },+  { "Lcommaaccent",                      611, NULL },+  { "tcaron",                            326, NULL },+  { "eogonek",                           444, NULL },+  { "Delta",                             612, NULL },+  { "Ohungarumlaut",                     722, NULL },+  { "asciicircum",                       469, NULL },+  { "aring",                             444, NULL },+  { "grave",                             333, NULL },+  { "uogonek",                           500, NULL },+  { "bracketright",                      333, NULL },+  { "Iacute",                            333, NULL },+  { "ampersand",                         778, NULL },+  { "igrave",                            278, NULL },+  { "lacute",                            278, NULL },+  { "Ncaron",                            722, NULL },+  { "plus",                              564, NULL },+  { "uring",                             500, NULL },+  { "quotesinglbase",                    333, NULL },+  { "lcommaaccent",                      278, NULL },+  { "Yacute",                            722, NULL },+  { "ohungarumlaut",                     500, NULL },+  { "threesuperior",                     300, NULL },+  { "acute",                             333, NULL },+  { "section",                           500, NULL },+  { "dieresis",                          333, NULL },+  { "iacute",                            278, NULL },+  { "quotedblbase",                      444, NULL },+  { "ncaron",                            500, NULL },+  { "florin",                            500, NULL },+  { "yacute",                            500, NULL },+  { "Rcommaaccent",                      667, NULL },+  { "fi",                                556, NULL },+  { "fl",                                556, NULL },+  { "Acircumflex",                       722, NULL },+  { "Cacute",                            667, NULL },+  { "Icircumflex",                       333, NULL },+  { "guillemotleft",                     500, NULL },+  { "germandbls",                        500, NULL },+  { "Amacron",                           722, NULL },+  { "seven",                             500, NULL },+  { "Sacute",                            556, NULL },+  { "ordmasculine",                      310, NULL },+  { "dotlessi",                          278, NULL },+  { "sterling",                          500, NULL },+  { "notequal",                          549, NULL },+  { "Imacron",                           333, NULL },+  { "rcommaaccent",                      333, NULL },+  { "Zdotaccent",                        611, NULL },+  { "acircumflex",                       444, NULL },+  { "cacute",                            444, NULL },+  { "Ecaron",                            611, NULL },+  { "icircumflex",                       278, NULL },+  { "braceright",                        480, NULL },+  { "quotedblright",                     444, NULL },+  { "amacron",                           444, NULL },+  { "sacute",                            389, NULL },+  { "imacron",                           278, NULL },+  { "cent",                              500, NULL },+  { "currency",                          500, NULL },+  { "logicalnot",                        564, NULL },+  { "zdotaccent",                        444, NULL },+  { "Atilde",                            722, NULL },+  { "breve",                             333, NULL },+  { "bar",                               200, NULL },+  { "fraction",                          167, NULL },+  { "less",                              564, NULL },+  { "ecaron",                            444, NULL },+  { "guilsinglleft",                     333, NULL },+  { "exclam",                            333, NULL },+  { "period",                            250, NULL },+  { "Rcaron",                            667, NULL },+  { "Kcommaaccent",                      722, NULL },+  { "greater",                           564, NULL },+  { "atilde",                            444, NULL },+  { "brokenbar",                         200, NULL },+  { "quoteleft",                         333, NULL },+  { "Edotaccent",                        611, NULL },+  { "onesuperior",                       300, NULL }+};++static BuiltinFontWidth zapfDingbatsWidthsTab[] = {+  { "a81",                               438, NULL },+  { "a82",                               138, NULL },+  { "a83",                               277, NULL },+  { "a84",                               415, NULL },+  { "a85",                               509, NULL },+  { "a86",                               410, NULL },+  { "a87",                               234, NULL },+  { "a88",                               234, NULL },+  { "a89",                               390, NULL },+  { "a140",                              788, NULL },+  { "a141",                              788, NULL },+  { "a142",                              788, NULL },+  { "a143",                              788, NULL },+  { "a144",                              788, NULL },+  { "a145",                              788, NULL },+  { "a146",                              788, NULL },+  { "a147",                              788, NULL },+  { "a148",                              788, NULL },+  { "a149",                              788, NULL },+  { "a90",                               390, NULL },+  { "a91",                               276, NULL },+  { "a92",                               276, NULL },+  { "space",                             278, NULL },+  { "a93",                               317, NULL },+  { "a94",                               317, NULL },+  { "a95",                               334, NULL },+  { "a96",                               334, NULL },+  { "a97",                               392, NULL },+  { "a98",                               392, NULL },+  { "a99",                               668, NULL },+  { "a150",                              788, NULL },+  { "a151",                              788, NULL },+  { "a152",                              788, NULL },+  { "a153",                              788, NULL },+  { "a154",                              788, NULL },+  { "a155",                              788, NULL },+  { "a156",                              788, NULL },+  { "a157",                              788, NULL },+  { "a158",                              788, NULL },+  { "a159",                              788, NULL },+  { "a160",                              894, NULL },+  { "a161",                              838, NULL },+  { "a162",                              924, NULL },+  { "a163",                             1016, NULL },+  { "a164",                              458, NULL },+  { "a165",                              924, NULL },+  { "a166",                              918, NULL },+  { "a167",                              927, NULL },+  { "a168",                              928, NULL },+  { "a169",                              928, NULL },+  { "a170",                              834, NULL },+  { "a171",                              873, NULL },+  { "a172",                              828, NULL },+  { "a173",                              924, NULL },+  { "a174",                              917, NULL },+  { "a175",                              930, NULL },+  { "a176",                              931, NULL },+  { "a177",                              463, NULL },+  { "a178",                              883, NULL },+  { "a179",                              836, NULL },+  { "a180",                              867, NULL },+  { "a181",                              696, NULL },+  { "a182",                              874, NULL },+  { "a183",                              760, NULL },+  { "a184",                              946, NULL },+  { "a185",                              865, NULL },+  { "a186",                              967, NULL },+  { "a187",                              831, NULL },+  { "a188",                              873, NULL },+  { "a189",                              927, NULL },+  { "a1",                                974, NULL },+  { "a2",                                961, NULL },+  { "a3",                                980, NULL },+  { "a4",                                719, NULL },+  { "a5",                                789, NULL },+  { "a6",                                494, NULL },+  { "a7",                                552, NULL },+  { "a8",                                537, NULL },+  { "a9",                                577, NULL },+  { "a190",                              970, NULL },+  { "a191",                              918, NULL },+  { "a192",                              748, NULL },+  { "a193",                              836, NULL },+  { "a194",                              771, NULL },+  { "a195",                              888, NULL },+  { "a196",                              748, NULL },+  { "a197",                              771, NULL },+  { "a198",                              888, NULL },+  { "a199",                              867, NULL },+  { "a10",                               692, NULL },+  { "a11",                               960, NULL },+  { "a12",                               939, NULL },+  { "a13",                               549, NULL },+  { "a14",                               855, NULL },+  { "a15",                               911, NULL },+  { "a16",                               933, NULL },+  { "a17",                               945, NULL },+  { "a18",                               974, NULL },+  { "a19",                               755, NULL },+  { "a20",                               846, NULL },+  { "a21",                               762, NULL },+  { "a22",                               761, NULL },+  { "a23",                               571, NULL },+  { "a24",                               677, NULL },+  { "a25",                               763, NULL },+  { "a26",                               760, NULL },+  { "a27",                               759, NULL },+  { "a28",                               754, NULL },+  { "a29",                               786, NULL },+  { "a30",                               788, NULL },+  { "a31",                               788, NULL },+  { "a32",                               790, NULL },+  { "a33",                               793, NULL },+  { "a34",                               794, NULL },+  { "a35",                               816, NULL },+  { "a36",                               823, NULL },+  { "a37",                               789, NULL },+  { "a38",                               841, NULL },+  { "a39",                               823, NULL },+  { "a40",                               833, NULL },+  { "a41",                               816, NULL },+  { "a42",                               831, NULL },+  { "a43",                               923, NULL },+  { "a44",                               744, NULL },+  { "a45",                               723, NULL },+  { "a46",                               749, NULL },+  { "a47",                               790, NULL },+  { "a48",                               792, NULL },+  { "a49",                               695, NULL },+  { "a100",                              668, NULL },+  { "a101",                              732, NULL },+  { "a102",                              544, NULL },+  { "a103",                              544, NULL },+  { "a104",                              910, NULL },+  { "a105",                              911, NULL },+  { "a106",                              667, NULL },+  { "a107",                              760, NULL },+  { "a108",                              760, NULL },+  { "a109",                              626, NULL },+  { "a50",                               776, NULL },+  { "a51",                               768, NULL },+  { "a52",                               792, NULL },+  { "a53",                               759, NULL },+  { "a54",                               707, NULL },+  { "a55",                               708, NULL },+  { "a56",                               682, NULL },+  { "a57",                               701, NULL },+  { "a58",                               826, NULL },+  { "a59",                               815, NULL },+  { "a110",                              694, NULL },+  { "a111",                              595, NULL },+  { "a112",                              776, NULL },+  { "a117",                              690, NULL },+  { "a118",                              791, NULL },+  { "a119",                              790, NULL },+  { "a60",                               789, NULL },+  { "a61",                               789, NULL },+  { "a62",                               707, NULL },+  { "a63",                               687, NULL },+  { "a64",                               696, NULL },+  { "a65",                               689, NULL },+  { "a66",                               786, NULL },+  { "a67",                               787, NULL },+  { "a68",                               713, NULL },+  { "a69",                               791, NULL },+  { "a200",                              696, NULL },+  { "a201",                              874, NULL },+  { "a120",                              788, NULL },+  { "a121",                              788, NULL },+  { "a202",                              974, NULL },+  { "a122",                              788, NULL },+  { "a203",                              762, NULL },+  { "a123",                              788, NULL },+  { "a204",                              759, NULL },+  { "a124",                              788, NULL },+  { "a205",                              509, NULL },+  { "a125",                              788, NULL },+  { "a206",                              410, NULL },+  { "a126",                              788, NULL },+  { "a127",                              788, NULL },+  { "a128",                              788, NULL },+  { "a129",                              788, NULL },+  { "a70",                               785, NULL },+  { "a71",                               791, NULL },+  { "a72",                               873, NULL },+  { "a73",                               761, NULL },+  { "a74",                               762, NULL },+  { "a75",                               759, NULL },+  { "a76",                               892, NULL },+  { "a77",                               892, NULL },+  { "a78",                               788, NULL },+  { "a79",                               784, NULL },+  { "a130",                              788, NULL },+  { "a131",                              788, NULL },+  { "a132",                              788, NULL },+  { "a133",                              788, NULL },+  { "a134",                              788, NULL },+  { "a135",                              788, NULL },+  { "a136",                              788, NULL },+  { "a137",                              788, NULL },+  { "a138",                              788, NULL },+  { "a139",                              788, NULL }+};++BuiltinFont builtinFonts[] = {+  { "Courier",               standardEncoding,            629, -157, { -23, -250,  715,  805}, NULL },+  { "Courier-Bold",          standardEncoding,            629, -157, {-113, -250,  749,  801}, NULL },+  { "Courier-BoldOblique",   standardEncoding,            629, -157, { -57, -250,  869,  801}, NULL },+  { "Courier-Oblique",       standardEncoding,            629, -157, { -27, -250,  849,  805}, NULL },+  { "Helvetica",             standardEncoding,            718, -207, {-166, -225, 1000,  931}, NULL },+  { "Helvetica-Bold",        standardEncoding,            718, -207, {-170, -228, 1003,  962}, NULL },+  { "Helvetica-BoldOblique", standardEncoding,            718, -207, {-174, -228, 1114,  962}, NULL },+  { "Helvetica-Oblique",     standardEncoding,            718, -207, {-170, -225, 1116,  931}, NULL },+  { "Symbol",                symbolEncoding,             1010, -293, {-180, -293, 1090, 1010}, NULL },+  { "Times-Bold",            standardEncoding,            683, -217, {-168, -218, 1000,  935}, NULL },+  { "Times-BoldItalic",      standardEncoding,            683, -217, {-200, -218,  996,  921}, NULL },+  { "Times-Italic",          standardEncoding,            683, -217, {-169, -217, 1010,  883}, NULL },+  { "Times-Roman",           standardEncoding,            683, -217, {-168, -218, 1000,  898}, NULL },+  { "ZapfDingbats",          zapfDingbatsEncoding,        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;+  }+}
+ xpdf/BuiltinFontTables.h view
@@ -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
+ xpdf/CMap.cc view
@@ -0,0 +1,408 @@+//========================================================================+//+// 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 "gfile.h"+#include "GString.h"+#include "Error.h"+#include "GlobalParams.h"+#include "PSTokenizer.h"+#include "CMap.h"++//------------------------------------------------------------------------++struct CMapVectorEntry {+  GBool isVector;+  union {+    CMapVectorEntry *vector;+    CID cid;+  };+};++//------------------------------------------------------------------------++static int getCharFromFile(void *data) {+  return fgetc((FILE *)data);+}++//------------------------------------------------------------------------++CMap *CMap::parse(CMapCache *cache, GString *collectionA,+		  GString *cMapNameA) {+  FILE *f;+  CMap *cmap;+  PSTokenizer *pst;+  char tok1[256], tok2[256], tok3[256];+  int n1, n2, n3;+  Guint start, end, code;++  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(-1, "Couldn't find '%s' CMap file for '%s' collection",+	  cMapNameA->getCString(), collectionA->getCString());+    return NULL;+  }++  cmap = new CMap(collectionA->copy(), cMapNameA->copy());++  pst = new PSTokenizer(&getCharFromFile, f);+  pst->getToken(tok1, sizeof(tok1), &n1);+  while (pst->getToken(tok2, sizeof(tok2), &n2)) {+    if (!strcmp(tok2, "usecmap")) {+      if (tok1[0] == '/') {+	cmap->useCMap(cache, tok1 + 1);+      }+      pst->getToken(tok1, sizeof(tok1), &n1);+    } else if (!strcmp(tok1, "/WMode")) {+      cmap->wMode = atoi(tok2);+      pst->getToken(tok1, sizeof(tok1), &n1);+    } else if (!strcmp(tok2, "begincodespacerange")) {+      while (pst->getToken(tok1, sizeof(tok1), &n1)) {+	if (!strcmp(tok1, "endcodespacerange")) {+	  break;+	}+	if (!pst->getToken(tok2, sizeof(tok2), &n2) ||+	    !strcmp(tok2, "endcodespacerange")) {+	  error(-1, "Illegal entry in codespacerange 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;+	  cmap->addCodeSpace(cmap->vector, start, end, n1);+	}+      }+      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(-1, "Illegal entry in cidchar block in CMap");+	  break;+	}+	if (!(tok1[0] == '<' && tok1[n1 - 1] == '>' &&+	      n1 >= 4 && (n1 & 1) == 0)) {+	  error(-1, "Illegal entry in cidchar block in CMap");+	  continue;+	}+	tok1[n1 - 1] = '\0';+	if (sscanf(tok1 + 1, "%x", &code) != 1) {+	  error(-1, "Illegal entry in cidchar block in CMap");+	  continue;+	}+	n1 = (n1 - 2) / 2;+	cmap->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(-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;+	  cmap->addCIDs(start, end, n1, (CID)atoi(tok3));+	}+      }+      pst->getToken(tok1, sizeof(tok1), &n1);+    } else {+      strcpy(tok1, tok2);+    }+  }+  delete pst;++  fclose(f);++  return cmap;+}++CMap::CMap(GString *collectionA, GString *cMapNameA) {+  int i;++  collection = collectionA;+  cMapName = cMapNameA;+  wMode = 0;+  vector = (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));+  for (i = 0; i < 256; ++i) {+    vector[i].isVector = gFalse;+    vector[i].cid = 0;+  }+  refCnt = 1;+#if MULTITHREADED+  gInitMutex(&mutex);+#endif+}++CMap::CMap(GString *collectionA, GString *cMapNameA, int wModeA) {+  collection = collectionA;+  cMapName = cMapNameA;+  wMode = wModeA;+  vector = NULL;+  refCnt = 1;+#if MULTITHREADED+  gInitMutex(&mutex);+#endif+}++void CMap::useCMap(CMapCache *cache, char *useName) {+  GString *useNameStr;+  CMap *subCMap;++  useNameStr = new GString(useName);+  subCMap = cache->getCMap(collection, useNameStr);+  delete useNameStr;+  if (!subCMap) {+    return;+  }+  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(-1, "Collision in usecmap");+      } else {+	dest[i].cid = src[i].cid;+      }+    }+  }+}++void CMap::addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,+			Guint nBytes) {+  Guint start2, end2;+  int startByte, endByte, i, j;++  if (nBytes > 1) {+    startByte = (start >> (8 * (nBytes - 1))) & 0xff;+    endByte = (end >> (8 * (nBytes - 1))) & 0xff;+    start2 = start & ((1 << (8 * (nBytes - 1))) - 1);+    end2 = end & ((1 << (8 * (nBytes - 1))) - 1);+    for (i = startByte; i <= endByte; ++i) {+      if (!vec[i].isVector) {+	vec[i].isVector = gTrue;+	vec[i].vector =+	  (CMapVectorEntry *)gmallocn(256, sizeof(CMapVectorEntry));+	for (j = 0; j < 256; ++j) {+	  vec[i].vector[j].isVector = gFalse;+	  vec[i].vector[j].cid = 0;+	}+      }+      addCodeSpace(vec[i].vector, start2, end2, nBytes - 1);+    }+  }+}++void CMap::addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID) {+  CMapVectorEntry *vec;+  CID cid;+  int byte;+  Guint i;++  vec = vector;+  for (i = nBytes - 1; i >= 1; --i) {+    byte = (start >> (8 * i)) & 0xff;+    if (!vec[byte].isVector) {+      error(-1, "Invalid CID (%0*x - %0*x) in CMap",+	    2*nBytes, start, 2*nBytes, end);+      return;+    }+    vec = vec[byte].vector;+  }+  cid = firstCID;+  for (byte = (int)(start & 0xff); byte <= (int)(end & 0xff); ++byte) {+    if (vec[byte].isVector) {+      error(-1, "Invalid CID (%0*x - %0*x) in CMap",+	    2*nBytes, start, 2*nBytes, end);+    } else {+      vec[byte].cid = cid;+    }+    ++cid;+  }+}++CMap::~CMap() {+  delete collection;+  delete cMapName;+  if (vector) {+    freeCMapVector(vector);+  }+#if MULTITHREADED+  gDestroyMutex(&mutex);+#endif+}++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+  gLockMutex(&mutex);+#endif+  ++refCnt;+#if MULTITHREADED+  gUnlockMutex(&mutex);+#endif+}++void CMap::decRefCnt() {+  GBool done;++#if MULTITHREADED+  gLockMutex(&mutex);+#endif+  done = --refCnt == 0;+#if MULTITHREADED+  gUnlockMutex(&mutex);+#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, int *nUsed) {+  CMapVectorEntry *vec;+  int n, i;++  if (!(vec = vector)) {+    // identity CMap+    *nUsed = 2;+    if (len < 2) {+      return 0;+    }+    return ((s[0] & 0xff) << 8) + (s[1] & 0xff);+  }+  n = 0;+  while (1) {+    if (n >= len) {+      *nUsed = n;+      return 0;+    }+    i = s[n++] & 0xff;+    if (!vec[i].isVector) {+      *nUsed = n;+      return vec[i].cid;+    }+    vec = vec[i].vector;+  }+}++//------------------------------------------------------------------------++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;+}
+ xpdf/CMap.h view
@@ -0,0 +1,102 @@+//========================================================================+//+// 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;+struct CMapVectorEntry;+class CMapCache;++//------------------------------------------------------------------------++class CMap {+public:++  // 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);++  ~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 *<nUsed> to the number of+  // bytes used by the char code.+  CID getCID(char *s, int len, int *nUsed);++  // Return the writing mode (0=horizontal, 1=vertical).+  int getWMode() { return wMode; }++private:++  CMap(GString *collectionA, GString *cMapNameA);+  CMap(GString *collectionA, GString *cMapNameA, int wModeA);+  void useCMap(CMapCache *cache, char *useName);+  void copyVector(CMapVectorEntry *dest, CMapVectorEntry *src);+  void addCodeSpace(CMapVectorEntry *vec, Guint start, Guint end,+		    Guint nBytes);+  void addCIDs(Guint start, Guint end, Guint nBytes, CID firstCID);+  void freeCMapVector(CMapVectorEntry *vec);++  GString *collection;+  GString *cMapName;+  int wMode;			// writing mode (0=horizontal, 1=vertical)+  CMapVectorEntry *vector;	// vector for first byte (NULL for+				//   identity CMap)+  int refCnt;+#if MULTITHREADED+  GMutex mutex;+#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
+ xpdf/Catalog.cc view
@@ -0,0 +1,374 @@+//========================================================================+//+// Catalog.cc+//+// Copyright 1996-2007 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stddef.h>+#include "gmem.h"+#include "Object.h"+#include "XRef.h"+#include "Array.h"+#include "Dict.h"+#include "Page.h"+#include "Error.h"+#include "Link.h"+#include "Catalog.h"++//------------------------------------------------------------------------+// Catalog+//------------------------------------------------------------------------++Catalog::Catalog(XRef *xrefA) {+  Object catDict, pagesDict, pagesDictRef;+  Object obj, obj2;+  char *alreadyRead;+  int numPages0;+  int i;++  ok = gTrue;+  xref = xrefA;+  pages = NULL;+  pageRefs = NULL;+  numPages = pagesSize = 0;+  baseURI = NULL;++  xref->getCatalog(&catDict);+  if (!catDict.isDict()) {+    error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());+    goto err1;+  }++  // read page tree+  catDict.dictLookup("Pages", &pagesDict);+  // This should really be isDict("Pages"), but I've seen at least one+  // PDF file where the /Type entry is missing.+  if (!pagesDict.isDict()) {+    error(-1, "Top-level pages object is wrong type (%s)",+	  pagesDict.getTypeName());+    goto err2;+  }+  pagesDict.dictLookup("Count", &obj);+  // some PDF files actually use real numbers here ("/Count 9.0")+  if (!obj.isNum()) {+    error(-1, "Page count in top-level pages object is wrong type (%s)",+	  obj.getTypeName());+    goto err3;+  }+  pagesSize = numPages0 = (int)obj.getNum();+  obj.free();+  pages = (Page **)gmallocn(pagesSize, sizeof(Page *));+  pageRefs = (Ref *)gmallocn(pagesSize, sizeof(Ref));+  for (i = 0; i < pagesSize; ++i) {+    pages[i] = NULL;+    pageRefs[i].num = -1;+    pageRefs[i].gen = -1;+  }+  alreadyRead = (char *)gmalloc(xref->getNumObjects());+  memset(alreadyRead, 0, xref->getNumObjects());+  if (catDict.dictLookupNF("Pages", &pagesDictRef)->isRef() &&+      pagesDictRef.getRefNum() >= 0 &&+      pagesDictRef.getRefNum() < xref->getNumObjects()) {+    alreadyRead[pagesDictRef.getRefNum()] = 1;+  }+  pagesDictRef.free();+  numPages = readPageTree(pagesDict.getDict(), NULL, 0, alreadyRead);+  gfree(alreadyRead);+  if (numPages != numPages0) {+    error(-1, "Page count in top-level pages object is incorrect");+  }+  pagesDict.free();++  // 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();++  // 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);++  catDict.free();+  return;++ err3:+  obj.free();+ err2:+  pagesDict.free();+ err1:+  catDict.free();+  dests.initNull();+  nameTree.initNull();+  ok = gFalse;+}++Catalog::~Catalog() {+  int i;++  if (pages) {+    for (i = 0; i < pagesSize; ++i) {+      if (pages[i]) {+	delete pages[i];+      }+    }+    gfree(pages);+    gfree(pageRefs);+  }+  dests.free();+  nameTree.free();+  if (baseURI) {+    delete baseURI;+  }+  metadata.free();+  structTreeRoot.free();+  outline.free();+  acroForm.free();+}++GString *Catalog::readMetadata() {+  GString *s;+  Dict *dict;+  Object obj;+  int c;++  if (!metadata.isStream()) {+    return NULL;+  }+  dict = metadata.streamGetDict();+  if (!dict->lookup("Subtype", &obj)->isName("XML")) {+    error(-1, "Unknown Metadata type: '%s'",+	  obj.isName() ? obj.getName() : "???");+  }+  obj.free();+  s = new GString();+  metadata.streamReset();+  while ((c = metadata.streamGetChar()) != EOF) {+    s->append(c);+  }+  metadata.streamClose();+  return s;+}++int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start,+			  char *alreadyRead) {+  Object kids;+  Object kid;+  Object kidRef;+  PageAttrs *attrs1, *attrs2;+  Page *page;+  int i, j;++  attrs1 = new PageAttrs(attrs, pagesDict);+  pagesDict->lookup("Kids", &kids);+  if (!kids.isArray()) {+    error(-1, "Kids object (page %d) is wrong type (%s)",+	  start+1, kids.getTypeName());+    goto err1;+  }+  for (i = 0; i < kids.arrayGetLength(); ++i) {+    kids.arrayGetNF(i, &kidRef);+    if (kidRef.isRef() &&+	kidRef.getRefNum() >= 0 &&+	kidRef.getRefNum() < xref->getNumObjects()) {+      if (alreadyRead[kidRef.getRefNum()]) {+	error(-1, "Loop in Pages tree");+	kidRef.free();+	continue;+      }+      alreadyRead[kidRef.getRefNum()] = 1;+    }+    kids.arrayGet(i, &kid);+    if (kid.isDict("Page")) {+      attrs2 = new PageAttrs(attrs1, kid.getDict());+      page = new Page(xref, start+1, kid.getDict(), attrs2);+      if (!page->isOk()) {+	++start;+	goto err3;+      }+      if (start >= pagesSize) {+	pagesSize += 32;+	pages = (Page **)greallocn(pages, pagesSize, sizeof(Page *));+	pageRefs = (Ref *)greallocn(pageRefs, pagesSize, sizeof(Ref));+	for (j = pagesSize - 32; j < pagesSize; ++j) {+	  pages[j] = NULL;+	  pageRefs[j].num = -1;+	  pageRefs[j].gen = -1;+	}+      }+      pages[start] = page;+      if (kidRef.isRef()) {+	pageRefs[start].num = kidRef.getRefNum();+	pageRefs[start].gen = kidRef.getRefGen();+      }+      ++start;+    // This should really be isDict("Pages"), but I've seen at least one+    // PDF file where the /Type entry is missing.+    } else if (kid.isDict()) {+      if ((start = readPageTree(kid.getDict(), attrs1, start, alreadyRead))+	  < 0)+	goto err2;+    } else {+      error(-1, "Kid object (page %d) is wrong type (%s)",+	    start+1, kid.getTypeName());+    }+    kid.free();+    kidRef.free();+  }+  delete attrs1;+  kids.free();+  return start;++ err3:+  delete page;+ err2:+  kid.free();+ err1:+  kids.free();+  delete attrs1;+  ok = gFalse;+  return -1;+}++int Catalog::findPage(int num, int gen) {+  int i;++  for (i = 0; i < numPages; ++i) {+    if (pageRefs[i].num == num && pageRefs[i].gen == gen)+      return i + 1;+  }+  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(-1, "Bad named destination value");+    obj2.free();+  } else {+    error(-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;+}
+ xpdf/Catalog.h view
@@ -0,0 +1,97 @@+//========================================================================+//+// Catalog.h+//+// Copyright 1996-2007 Glyph & Cog, LLC+//+//========================================================================++#ifndef CATALOG_H+#define CATALOG_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++class XRef;+class Object;+class Page;+class PageAttrs;+struct Ref;+class LinkDest;++//------------------------------------------------------------------------+// Catalog+//------------------------------------------------------------------------++class Catalog {+public:++  // Constructor.+  Catalog(XRef *xrefA);++  // Destructor.+  ~Catalog();++  // Is catalog valid?+  GBool isOk() { return ok; }++  // Get number of pages.+  int getNumPages() { return numPages; }++  // Get a page.+  Page *getPage(int i) { return pages[i-1]; }++  // Get the reference for a page object.+  Ref *getPageRef(int i) { return &pageRefs[i-1]; }++  // 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; }++private:++  XRef *xref;			// the xref table for this PDF file+  Page **pages;			// array of pages+  Ref *pageRefs;		// object ID for each page+  int numPages;			// number of pages+  int pagesSize;		// size of pages array+  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 ok;			// true if catalog is valid++  int readPageTree(Dict *pages, PageAttrs *attrs, int start,+		   char *alreadyRead);+  Object *findDestInTree(Object *tree, GString *name, Object *obj);+};++#endif
+ xpdf/CharCodeToUnicode.cc view
@@ -0,0 +1,540 @@+//========================================================================+//+// 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 "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;+};++//------------------------------------------------------------------------++static int getCharFromString(void *data) {+  char *p;+  int c;++  p = *(char **)data;+  if (*p) {+    c = *p++;+    *(char **)data = p;+  } else {+    c = EOF;+  }+  return c;+}++static int getCharFromFile(void *data) {+  return fgetc((FILE *)data);+}++//------------------------------------------------------------------------++CharCodeToUnicode *CharCodeToUnicode::parseCIDToUnicode(GString *fileName,+							GString *collection) {+  FILE *f;+  Unicode *mapA;+  CharCode size, mapLenA;+  char buf[64];+  Unicode u;+  CharCodeToUnicode *ctu;++  if (!(f = fopen(fileName->getCString(), "r"))) {+    error(-1, "Couldn't open cidToUnicode file '%s'",+	  fileName->getCString());+    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(-1, "Bad line (%d) in cidToUnicode file '%s'",+	    (int)(mapLenA + 1), fileName->getCString());+      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 = fopen(fileName->getCString(), "r"))) {+    error(-1, "Couldn't open unicodeToUnicode file '%s'",+	  fileName->getCString());+    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")) ||+	sscanf(tok, "%x", &u0) != 1) {+      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",+	    line, fileName->getCString());+      continue;+    }+    n = 0;+    while (n < maxUnicodeString) {+      if (!(tok = strtok(NULL, " \t\r\n"))) {+	break;+      }+      if (sscanf(tok, "%x", &uBuf[n]) != 1) {+	error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",+	      line, fileName->getCString());+	break;+      }+      ++n;+    }+    if (n < 1) {+      error(-1, "Bad line (%d) in unicodeToUnicode file '%s'",+	    line, fileName->getCString());+      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;+  char *p;++  ctu = new CharCodeToUnicode(NULL);+  p = buf->getCString();+  ctu->parseCMap1(&getCharFromString, &p, nBits);+  return ctu;+}++void CharCodeToUnicode::mergeCMap(GString *buf, int nBits) {+  char *p;++  p = buf->getCString();+  parseCMap1(&getCharFromString, &p, nBits);+}++void CharCodeToUnicode::parseCMap1(int (*getCharFunc)(void *), void *data,+				   int nBits) {+  PSTokenizer *pst;+  char tok1[256], tok2[256], tok3[256];+  int nDigits, n1, n2, n3;+  CharCode i;+  CharCode code1, code2;+  GString *name;+  FILE *f;++  nDigits = nBits / 4;+  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))) {+	  parseCMap1(&getCharFromFile, f, nBits);+	  fclose(f);+	} else {+	  error(-1, "Couldn't find ToUnicode CMap file for '%s'",+		name->getCString());+	}+	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(-1, "Illegal entry in bfchar block in ToUnicode CMap");+	  break;+	}+	if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&+	      tok2[0] == '<' && tok2[n2 - 1] == '>')) {+	  error(-1, "Illegal entry in bfchar block in ToUnicode CMap");+	  continue;+	}+	tok1[n1 - 1] = tok2[n2 - 1] = '\0';+	if (sscanf(tok1 + 1, "%x", &code1) != 1) {+	  error(-1, "Illegal entry in bfchar block in ToUnicode CMap");+	  continue;+	}+	addMapping(code1, tok2 + 1, n2 - 2, 0);+      }+      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(-1, "Illegal entry in bfrange block in ToUnicode CMap");+	  break;+	}+	if (!(n1 == 2 + nDigits && tok1[0] == '<' && tok1[n1 - 1] == '>' &&+	      n2 == 2 + nDigits && tok2[0] == '<' && tok2[n2 - 1] == '>')) {+	  error(-1, "Illegal entry in bfrange block in ToUnicode CMap");+	  continue;+	}+	tok1[n1 - 1] = tok2[n2 - 1] = '\0';+	if (sscanf(tok1 + 1, "%x", &code1) != 1 ||+	    sscanf(tok2 + 1, "%x", &code2) != 1) {+	  error(-1, "Illegal entry in bfrange block in ToUnicode CMap");+	  continue;+	}+	if (!strcmp(tok3, "[")) {+	  i = 0;+	  while (pst->getToken(tok1, sizeof(tok1), &n1) &&+		 code1 + i <= code2) {+	    if (!strcmp(tok1, "]")) {+	      break;+	    }+	    if (tok1[0] == '<' && tok1[n1 - 1] == '>') {+	      tok1[n1 - 1] = '\0';+	      addMapping(code1 + i, tok1 + 1, n1 - 2, 0);+	    } else {+	      error(-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);+	  }++	} else {+	  error(-1, "Illegal entry in bfrange block in ToUnicode CMap");+	}+      }+      pst->getToken(tok1, sizeof(tok1), &n1);+    } else {+      strcpy(tok1, tok2);+    }+  }+  delete pst;+}++void CharCodeToUnicode::addMapping(CharCode code, char *uStr, int n,+				   int offset) {+  CharCode oldLen, i;+  Unicode u;+  char uHex[5];+  int j;++  if (code >= mapLen) {+    oldLen = mapLen;+    mapLen = (code + 256) & ~255;+    map = (Unicode *)greallocn(map, mapLen, sizeof(Unicode));+    for (i = oldLen; i < mapLen; ++i) {+      map[i] = 0;+    }+  }+  if (n <= 4) {+    if (sscanf(uStr, "%x", &u) != 1) {+      error(-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;+    sMap[sMapLen].len = n / 4;+    for (j = 0; j < sMap[sMapLen].len && j < maxUnicodeString; ++j) {+      strncpy(uHex, uStr + j*4, 4);+      uHex[4] = '\0';+      if (sscanf(uHex, "%x", &sMap[sMapLen].u[j]) != 1) {+	error(-1, "Illegal entry in ToUnicode CMap");+      }+    }+    sMap[sMapLen].u[sMap[sMapLen].len - 1] += offset;+    ++sMapLen;+  }+}++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;+#if MULTITHREADED+  gInitMutex(&mutex);+#endif+}++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;+#if MULTITHREADED+  gInitMutex(&mutex);+#endif+}++CharCodeToUnicode::~CharCodeToUnicode() {+  if (tag) {+    delete tag;+  }+  gfree(map);+  if (sMap) {+    gfree(sMap);+  }+#if MULTITHREADED+  gDestroyMutex(&mutex);+#endif+}++void CharCodeToUnicode::incRefCnt() {+#if MULTITHREADED+  gLockMutex(&mutex);+#endif+  ++refCnt;+#if MULTITHREADED+  gUnlockMutex(&mutex);+#endif+}++void CharCodeToUnicode::decRefCnt() {+  GBool done;++#if MULTITHREADED+  gLockMutex(&mutex);+#endif+  done = --refCnt == 0;+#if MULTITHREADED+  gUnlockMutex(&mutex);+#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 (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 (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();+}
+ xpdf/CharCodeToUnicode.h view
@@ -0,0 +1,117 @@+//========================================================================+//+// 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:++  // 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; }++private:++  void parseCMap1(int (*getCharFunc)(void *), void *data, int nBits);+  void addMapping(CharCode code, char *uStr, int n, int offset);+  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;+  int refCnt;+#if MULTITHREADED+  GMutex mutex;+#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
+ xpdf/CharTypes.h view
@@ -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
+ xpdf/CompactFontTables.h view
@@ -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
+ xpdf/CoreOutputDev.cc view
@@ -0,0 +1,61 @@+//========================================================================+//+// CoreOutputDev.cc+//+// Copyright 2004 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include "Object.h"+#include "TextOutputDev.h"+#include "CoreOutputDev.h"++//------------------------------------------------------------------------+// CoreOutputDev+//------------------------------------------------------------------------++CoreOutputDev::CoreOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,+			     GBool reverseVideoA, SplashColorPtr paperColorA,+			     GBool incrementalUpdateA,+			     CoreOutRedrawCbk redrawCbkA,+			     void *redrawCbkDataA):+  SplashOutputDev(colorModeA, bitmapRowPadA, reverseVideoA, paperColorA)+{+  incrementalUpdate = incrementalUpdateA;+  redrawCbk = redrawCbkA;+  redrawCbkData = redrawCbkDataA;+}++CoreOutputDev::~CoreOutputDev() {+}++void CoreOutputDev::endPage() {+  SplashOutputDev::endPage();+  if (!incrementalUpdate) {+    (*redrawCbk)(redrawCbkData, 0, 0, getBitmapWidth(), getBitmapHeight(),+		 gTrue);+  }+}++void CoreOutputDev::dump() {+  int x0, y0, x1, y1;++  if (incrementalUpdate) {+    getModRegion(&x0, &y0, &x1, &y1);+    clearModRegion();+    if (x1 >= x0 && y1 >= y0) {+      (*redrawCbk)(redrawCbkData, x0, y0, x1, y1, gFalse);+    }+  }+}++void CoreOutputDev::clear() {+  startDoc(NULL);+  startPage(0, NULL);+}
+ xpdf/CoreOutputDev.h view
@@ -0,0 +1,61 @@+//========================================================================+//+// CoreOutputDev.h+//+// Copyright 2004 Glyph & Cog, LLC+//+//========================================================================++#ifndef COREOUTPUTDEV_H+#define COREOUTPUTDEV_H++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashTypes.h"+#include "SplashOutputDev.h"++class TextPage;++//------------------------------------------------------------------------++typedef void (*CoreOutRedrawCbk)(void *data, int x0, int y0, int x1, int y1,+				 GBool composited);++//------------------------------------------------------------------------+// CoreOutputDev+//------------------------------------------------------------------------++class CoreOutputDev: public SplashOutputDev {+public:++  CoreOutputDev(SplashColorMode colorModeA, int bitmapRowPadA,+		GBool reverseVideoA, SplashColorPtr paperColorA,+		GBool incrementalUpdateA,+		CoreOutRedrawCbk redrawCbkA,+		void *redrawCbkDataA);++  virtual ~CoreOutputDev();++  //----- initialization and control++  // End a page.+  virtual void endPage();++  // Dump page contents to display.+  virtual void dump();++  //----- special access++  // Clear out the document (used when displaying an empty window).+  void clear();++private:++  GBool incrementalUpdate;      // incrementally update the display?+  CoreOutRedrawCbk redrawCbk;+  void *redrawCbkData;+};++#endif
+ xpdf/Decrypt.cc view
@@ -0,0 +1,776 @@+//========================================================================+//+// 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 "Decrypt.h"++static void rc4InitKey(Guchar *key, int keyLen, Guchar *state);+static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c);+static void aesKeyExpansion(DecryptAESState *s,+			    Guchar *objKey, int objKeyLen);+static void aesDecryptBlock(DecryptAESState *s, Guchar *in, GBool last);+static void md5(Guchar *msg, int msgLen, Guchar *digest);++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,+			   int permissions, GString *fileID,+			   GString *ownerPassword, GString *userPassword,+			   Guchar *fileKey, GBool encryptMetadata,+			   GBool *ownerPasswordOk) {+  Guchar test[32], test2[32];+  GString *userPassword2;+  Guchar fState[256];+  Guchar tmpKey[16];+  Guchar fx, fy;+  int len, i, j;++  // try using the supplied owner password to generate the user password+  *ownerPasswordOk = gFalse;+  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, 16, 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] = 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);+}++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] = permissions & 0xff;+  buf[65] = (permissions >> 8) & 0xff;+  buf[66] = (permissions >> 16) & 0xff;+  buf[67] = (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] = 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 *fileKey,+			     CryptAlgorithm algoA, int keyLength,+			     int objNum, int objGen):+  FilterStream(strA)+{+  int n, i;++  algo = algoA;++  // construct object key+  for (i = 0; i < keyLength; ++i) {+    objKey[i] = fileKey[i];+  }+  objKey[keyLength] = objNum & 0xff;+  objKey[keyLength + 1] = (objNum >> 8) & 0xff;+  objKey[keyLength + 2] = (objNum >> 16) & 0xff;+  objKey[keyLength + 3] = objGen & 0xff;+  objKey[keyLength + 4] = (objGen >> 8) & 0xff;+  if (algo == cryptAES) {+    objKey[keyLength + 5] = 0x73; // 's'+    objKey[keyLength + 6] = 0x41; // 'A'+    objKey[keyLength + 7] = 0x6c; // 'l'+    objKey[keyLength + 8] = 0x54; // 'T'+    n = keyLength + 9;+  } else {+    n = keyLength + 5;+  }+  md5(objKey, n, objKey);+  if ((objKeyLength = keyLength + 5) > 16) {+    objKeyLength = 16;+  }+}++DecryptStream::~DecryptStream() {+  delete str;+}++void DecryptStream::reset() {+  int i;++  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);+    for (i = 0; i < 16; ++i) {+      state.aes.cbc[i] = str->getChar();+    }+    state.aes.bufIdx = 16;+    break;+  }+}++int DecryptStream::getChar() {+  Guchar in[16];+  int c, i;++  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) {+      for (i = 0; i < 16; ++i) {+	if ((c = str->getChar()) == EOF) {+	  return EOF;+	}+	in[i] = (Guchar)c;+      }+      aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);+    }+    if (state.aes.bufIdx == 16) {+      c = EOF;+    } else {+      c = state.aes.buf[state.aes.bufIdx++];+    }+    break;+  }+  return c;+}++int DecryptStream::lookChar() {+  Guchar in[16];+  int c, i;++  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) {+      for (i = 0; i < 16; ++i) {+	if ((c = str->getChar()) == EOF) {+	  return EOF;+	}+	in[i] = c;+      }+      aesDecryptBlock(&state.aes, in, str->lookChar() == EOF);+    }+    if (state.aes.bufIdx == 16) {+      c = EOF;+    } else {+      c = state.aes.buf[state.aes.bufIdx];+    }+    break;+  }+  return c;+}++GBool DecryptStream::isBinary(GBool last) {+  return str->isBinary(last);+}++//------------------------------------------------------------------------+// RC4-compatible decryption+//------------------------------------------------------------------------++static void rc4InitKey(Guchar *key, int keyLen, Guchar *state) {+  Guchar index1, index2;+  Guchar t;+  int i;++  for (i = 0; i < 256; ++i)+    state[i] = i;+  index1 = index2 = 0;+  for (i = 0; i < 256; ++i) {+    index2 = (key[index1] + state[i] + index2) % 256;+    t = state[i];+    state[i] = state[index2];+    state[index2] = t;+    index1 = (index1 + 1) % keyLen;+  }+}++static Guchar rc4DecryptByte(Guchar *state, Guchar *x, Guchar *y, Guchar c) {+  Guchar x1, y1, tx, ty;++  x1 = *x = (*x + 1) % 256;+  y1 = *y = (state[*x] + *y) % 256;+  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 invSubBytes(Guchar *state) {+  int i;++  for (i = 0; i < 16; ++i) {+    state[i] = invSbox[state[i]];+  }+}++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;+}++// {09} \cdot s+static inline Guchar mul09(Guchar s) {+  Guchar s2, s4, s8;++  s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);+  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);+  s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1);+  return s ^ s8;+}++// {0b} \cdot s+static inline Guchar mul0b(Guchar s) {+  Guchar s2, s4, s8;++  s2 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);+  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);+  s8 = (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 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);+  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);+  s8 = (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 = (s & 0x80) ? ((s << 1) ^ 0x1b) : (s << 1);+  s4 = (s2 & 0x80) ? ((s2 << 1) ^ 0x1b) : (s2 << 1);+  s8 = (s4 & 0x80) ? ((s4 << 1) ^ 0x1b) : (s4 << 1);+  return s2 ^ s4 ^ s8;+}++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 = w[c] >> 24;+    s1 = w[c] >> 16;+    s2 = w[c] >> 8;+    s3 = 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] ^= w[c] >> 24;+    state[4+c] ^= w[c] >> 16;+    state[8+c] ^= w[c] >> 8;+    state[12+c] ^= w[c];+  }+}++static void aesKeyExpansion(DecryptAESState *s,+			    Guchar *objKey, int objKeyLen) {+  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;+  }+  for (round = 1; round <= 9; ++round) {+    invMixColumnsW(&s->w[round * 4]);+  }+}++static 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];+    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,  Gulong 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,  Gulong 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,  Gulong 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,  Gulong s, Gulong Ti) {+  return b + rotateLeft((a + (c ^ (b | ~d)) + Xk + Ti), s);+}++static void md5(Guchar *msg, int msgLen, Guchar *digest) {+  Gulong x[16];+  Gulong a, b, c, d, aa, bb, cc, dd;+  int n64;+  int i, j, k;++  // compute number of 64-byte blocks+  // (length + pad byte (0x80) + 8 bytes for length)+  n64 = (msgLen + 1 + 8 + 63) / 64;++  // initialize a, b, c, d+  a = 0x67452301;+  b = 0xefcdab89;+  c = 0x98badcfe;+  d = 0x10325476;++  // loop through blocks+  k = 0;+  for (i = 0; i < n64; ++i) {++    // grab a 64-byte block+    for (j = 0; j < 16 && k < msgLen - 3; ++j, k += 4)+      x[j] = (((((msg[k+3] << 8) + msg[k+2]) << 8) + msg[k+1]) << 8) + msg[k];+    if (i == n64 - 1) {+      if (k == msgLen - 3)+	x[j] = 0x80000000 + (((msg[k+2] << 8) + msg[k+1]) << 8) + msg[k];+      else if (k == msgLen - 2)+	x[j] = 0x800000 + (msg[k+1] << 8) + msg[k];+      else if (k == msgLen - 1)+	x[j] = 0x8000 + msg[k];+      else+	x[j] = 0x80;+      ++j;+      while (j < 16)+	x[j++] = 0;+      x[14] = msgLen << 3;+    }++    // save a, b, c, d+    aa = a;+    bb = b;+    cc = c;+    dd = 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+    a += aa;+    b += bb;+    c += cc;+    d += dd;+  }++  // break digest into bytes+  digest[0] = (Guchar)(a & 0xff);+  digest[1] = (Guchar)((a >>= 8) & 0xff);+  digest[2] = (Guchar)((a >>= 8) & 0xff);+  digest[3] = (Guchar)((a >>= 8) & 0xff);+  digest[4] = (Guchar)(b & 0xff);+  digest[5] = (Guchar)((b >>= 8) & 0xff);+  digest[6] = (Guchar)((b >>= 8) & 0xff);+  digest[7] = (Guchar)((b >>= 8) & 0xff);+  digest[8] = (Guchar)(c & 0xff);+  digest[9] = (Guchar)((c >>= 8) & 0xff);+  digest[10] = (Guchar)((c >>= 8) & 0xff);+  digest[11] = (Guchar)((c >>= 8) & 0xff);+  digest[12] = (Guchar)(d & 0xff);+  digest[13] = (Guchar)((d >>= 8) & 0xff);+  digest[14] = (Guchar)((d >>= 8) & 0xff);+  digest[15] = (Guchar)((d >>= 8) & 0xff);+}
+ xpdf/Decrypt.h view
@@ -0,0 +1,95 @@+//========================================================================+//+// 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,+			   int permissions, GString *fileID,+			   GString *ownerPassword, GString *userPassword,+			   Guchar *fileKey, GBool encryptMetadata,+			   GBool *ownerPasswordOk);++private:++  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;+};++class DecryptStream: public FilterStream {+public:++  DecryptStream(Stream *strA, Guchar *fileKey,+		CryptAlgorithm algoA, int keyLength,+		int objNum, int objGen);+  virtual ~DecryptStream();+  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:++  CryptAlgorithm algo;+  int objKeyLength;+  Guchar objKey[16 + 9];++  union {+    DecryptRC4State rc4;+    DecryptAESState aes;+  } state;+};++#endif
+ xpdf/Dict.cc view
@@ -0,0 +1,95 @@+//========================================================================+//+// 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 "Object.h"+#include "XRef.h"+#include "Dict.h"++//------------------------------------------------------------------------+// Dict+//------------------------------------------------------------------------++Dict::Dict(XRef *xrefA) {+  xref = xrefA;+  entries = NULL;+  size = length = 0;+  ref = 1;+}++Dict::~Dict() {+  int i;++  for (i = 0; i < length; ++i) {+    gfree(entries[i].key);+    entries[i].val.free();+  }+  gfree(entries);+}++void Dict::add(char *key, Object *val) {+  if (length == size) {+    if (length == 0) {+      size = 8;+    } else {+      size *= 2;+    }+    entries = (DictEntry *)greallocn(entries, size, sizeof(DictEntry));+  }+  entries[length].key = key;+  entries[length].val = *val;+  ++length;+}++inline DictEntry *Dict::find(char *key) {+  int i;++  for (i = 0; i < length; ++i) {+    if (!strcmp(key, entries[i].key))+      return &entries[i];+  }+  return NULL;+}++GBool Dict::is(char *type) {+  DictEntry *e;++  return (e = find("Type")) && e->val.isName(type);+}++Object *Dict::lookup(char *key, Object *obj) {+  DictEntry *e;++  return (e = find(key)) ? e->val.fetch(xref, obj) : obj->initNull();+}++Object *Dict::lookupNF(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);+}
+ xpdf/Dict.h view
@@ -0,0 +1,77 @@+//========================================================================+//+// Dict.h+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef DICT_H+#define DICT_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "Object.h"++//------------------------------------------------------------------------+// Dict+//------------------------------------------------------------------------++struct DictEntry {+  char *key;+  Object val;+};++class Dict {+public:++  // Constructor.+  Dict(XRef *xrefA);++  // Destructor.+  ~Dict();++  // Reference counting.+  int incRef() { return ++ref; }+  int decRef() { return --ref; }++  // 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(char *type);++  // Look up an entry and return the value.  Returns a null object+  // if <key> is not in the dictionary.+  Object *lookup(char *key, Object *obj);+  Object *lookupNF(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+  int size;			// size of <entries> array+  int length;			// number of entries in dictionary+  int ref;			// reference count++  DictEntry *find(char *key);+};++#endif
+ xpdf/Error.cc view
@@ -0,0 +1,38 @@+//========================================================================+//+// Error.cc+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdio.h>+#include <stddef.h>+#include <stdarg.h>+#include "GlobalParams.h"+#include "Error.h"++void CDECL error(int pos, char *msg, ...) {+  va_list args;++  // NB: this can be called before the globalParams object is created+  if (globalParams && globalParams->getErrQuiet()) {+    return;+  }+  if (pos >= 0) {+    fprintf(stderr, "Error (%d): ", pos);+  } else {+    fprintf(stderr, "Error: ");+  }+  va_start(args, msg);+  vfprintf(stderr, msg, args);+  va_end(args);+  fprintf(stderr, "\n");+  fflush(stderr);+}
+ xpdf/Error.h view
@@ -0,0 +1,23 @@+//========================================================================+//+// 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"++extern void CDECL error(int pos, char *msg, ...);++#endif
+ xpdf/ErrorCodes.h view
@@ -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
+ xpdf/FixedPoint.cc view
@@ -0,0 +1,118 @@+//========================================================================+//+// 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 "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) {+#if 1 //~tmp+  return ((FixPtInt64)x * y) >> fixptShift;+#else+  int ah0, ah, bh, al, bl;+  ah0 = x & fixptMaskH;+  ah = x >> fixptShift;+  al = x - ah0;+  bh = y >> fixptShift;+  bl = y - (bh << fixptShift);+  return ah0 * bh + ah * bl + al * bh + ((al * bl) >> fixptShift);+#endif+}++int FixedPoint::div(int x, int y) {+#if 1 //~tmp+  return ((FixPtInt64)x << fixptShift) / y;+#else+#endif+}++GBool FixedPoint::divCheck(FixedPoint x, FixedPoint y, FixedPoint *result) {+#if 1 //~tmp+  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 = z;+  return gTrue;+#else+#endif+}++#endif // USE_FIXEDPOINT
+ xpdf/FixedPoint.h view
@@ -0,0 +1,155 @@+//========================================================================+//+// 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(long x) { val = 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 getRaw() { return val; }++  FixedPoint operator =(FixedPoint x) { val = x.val; return *this; }++  int operator ==(FixedPoint x) { return val == x.val; }+  int operator ==(double x) { return *this == (FixedPoint)x; }+  int operator ==(int x) { return *this == (FixedPoint)x; }+  int operator ==(long x) { return *this == (FixedPoint)x; }++  int operator !=(FixedPoint x) { return val != x.val; }+  int operator !=(double x) { return *this != (FixedPoint)x; }+  int operator !=(int x) { return *this != (FixedPoint)x; }+  int operator !=(long x) { return *this != (FixedPoint)x; }++  int operator <(FixedPoint x) { return val < x.val; }+  int operator <(double x) { return *this < (FixedPoint)x; }+  int operator <(int x) { return *this < (FixedPoint)x; }+  int operator <(long x) { return *this < (FixedPoint)x; }++  int operator <=(FixedPoint x) { return val <= x.val; }+  int operator <=(double x) { return *this <= (FixedPoint)x; }+  int operator <=(int x) { return *this <= (FixedPoint)x; }+  int operator <=(long x) { return *this <= (FixedPoint)x; }++  int operator >(FixedPoint x) { return val > x.val; }+  int operator >(double x) { return *this > (FixedPoint)x; }+  int operator >(int x) { return *this > (FixedPoint)x; }+  int operator >(long x) { return *this > (FixedPoint)x; }++  int operator >=(FixedPoint x) { return val >= x.val; }+  int operator >=(double x) { return *this >= (FixedPoint)x; }+  int operator >=(int x) { return *this >= (FixedPoint)x; }+  int operator >=(long x) { 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 +(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 +=(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 -(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 -=(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 *(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 *=(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 /(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 /=(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; }++  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);++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;			// 16.16 fixed point+};++#endif // USE_FIXEDPOINT++#endif
+ xpdf/FoFiBase.cc view
@@ -0,0 +1,156 @@+//========================================================================+//+// FoFiBase.cc+//+// Copyright 1999-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdio.h>+#include "gmem.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);+  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+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+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+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+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::getUVarBE(int pos, int size, GBool *ok) {+  Guint x;+  int i;++  if (pos < 0 || 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 &&+         pos + size >= pos &&+         pos + size <= len;+}
+ xpdf/FoFiBase.h view
@@ -0,0 +1,57 @@+//========================================================================+//+// 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, 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 getUVarBE(int pos, int size, GBool *ok);++  GBool checkRegion(int pos, int size);++  Guchar *fileData;+  Guchar *file;+  int len;+  GBool freeFileData;+};++#endif
+ xpdf/FoFiEncodings.cc view
@@ -0,0 +1,994 @@+//========================================================================+//+// FoFiEncodings.cc+//+// Copyright 1999-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include "FoFiEncodings.h"++//------------------------------------------------------------------------+// Type 1 and 1C font data+//------------------------------------------------------------------------++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+};++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+//------------------------------------------------------------------------++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+};
+ xpdf/FoFiEncodings.h view
@@ -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 char *fofiType1StandardEncoding[256];+extern char *fofiType1ExpertEncoding[256];++//------------------------------------------------------------------------+// Type 1C font data+//------------------------------------------------------------------------++extern char *fofiType1CStdStrings[391];+extern Gushort fofiType1CISOAdobeCharset[229];+extern Gushort fofiType1CExpertCharset[166];+extern Gushort fofiType1CExpertSubsetCharset[87];++#endif
+ xpdf/FoFiTrueType.cc view
@@ -0,0 +1,2027 @@+//========================================================================+//+// FoFiTrueType.cc+//+// Copyright 1999-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <string.h>+#include "gtypes.h"+#include "gmem.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++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;+}++//------------------------------------------------------------------------++struct T42Table {+  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 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) {+  FoFiTrueType *ff;++  ff = new FoFiTrueType(fileA, lenA, gFalse);+  if (!ff->parsedOk) {+    delete ff;+    return NULL;+  }+  return ff;+}++FoFiTrueType *FoFiTrueType::load(char *fileName) {+  FoFiTrueType *ff;+  char *fileA;+  int lenA;++  if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {+    return NULL;+  }+  ff = new FoFiTrueType(fileA, lenA, gTrue);+  if (!ff->parsedOk) {+    delete ff;+    return NULL;+  }+  return ff;+}++FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA):+  FoFiBase(fileA, lenA, freeFileDataA)+{+  tables = NULL;+  nTables = 0;+  cmaps = NULL;+  nCmaps = 0;+  nameToGID = NULL;+  parsedOk = gFalse;++  parse();+}++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;+}++Gushort FoFiTrueType::mapCodeToGID(int i, int c) {+  Gushort 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(cmaps[i].offset + 6 + 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);+}++Gushort *FoFiTrueType::getCIDToGIDMap(int *nCIDs) {+  FoFiType1C *ff;+  Gushort *map;+  int i;++  *nCIDs = 0;+  if (!openTypeCFF) {+    return NULL;+  }+  i = seekTable("CFF ");+  if (!checkRegion(tables[i].offset, tables[i].len)) {+    return NULL;+  }+  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,+			      tables[i].len))) {+    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::convertToType42(char *psName, char **encoding,+				   Gushort *codeToGID,+				   FoFiOutputFunc outputFunc,+				   void *outputStream) {+  GString *buf;+  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, 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);++  // end the dictionary and define the font+  (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40);+}++void FoFiTrueType::convertToType1(char *psName, char **newEncoding,+				  GBool ascii, FoFiOutputFunc outputFunc,+				  void *outputStream) {+  FoFiType1C *ff;+  int i;++  if (!openTypeCFF) {+    return;+  }+  i = seekTable("CFF ");+  if (!checkRegion(tables[i].offset, tables[i].len)) {+    return;+  }+  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,+			      tables[i].len))) {+    return;+  }+  ff->convertToType1(psName, newEncoding, ascii, outputFunc, outputStream);+  delete ff;+}++void FoFiTrueType::convertToCIDType2(char *psName,+				     Gushort *cidMap, int nCIDs,+				     GBool needVerticalMetrics,+				     FoFiOutputFunc outputFunc,+				     void *outputStream) {+  GString *buf;+  Gushort cid;+  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, 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);++  // end the dictionary and define the font+  (*outputFunc)(outputStream,+		"CIDFontName currentdict end /CIDFont defineresource pop\n",+		56);+}++void FoFiTrueType::convertToCIDType0(char *psName,+				     FoFiOutputFunc outputFunc,+				     void *outputStream) {+  FoFiType1C *ff;+  int i;++  if (!openTypeCFF) {+    return;+  }+  i = seekTable("CFF ");+  if (!checkRegion(tables[i].offset, tables[i].len)) {+    return;+  }+  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,+			      tables[i].len))) {+    return;+  }+  ff->convertToCIDType0(psName, outputFunc, outputStream);+  delete ff;+}++void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs,+				  GBool needVerticalMetrics,+				  FoFiOutputFunc outputFunc,+				  void *outputStream) {+  GString *buf;+  GString *sfntsName;+  int n, i, j;++  if (openTypeCFF) {+    return;+  }++  // write the Type 42 sfnts array+  sfntsName = (new GString(psName))->append("_sfnts");+  cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics);+  delete sfntsName;++  // write the descendant Type 42 fonts+  n = cidMap ? nCIDs : nGlyphs;+  for (i = 0; i < n; i += 256) {+    (*outputFunc)(outputStream, "10 dict begin\n", 14);+    (*outputFunc)(outputStream, "/FontName /", 11);+    (*outputFunc)(outputStream, psName, 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, 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, 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, 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,+				  FoFiOutputFunc outputFunc,+				  void *outputStream) {+  FoFiType1C *ff;+  int i;++  if (!openTypeCFF) {+    return;+  }+  i = seekTable("CFF ");+  if (!checkRegion(tables[i].offset, tables[i].len)) {+    return;+  }+  if (!(ff = FoFiType1C::make((char *)file + tables[i].offset,+			      tables[i].len))) {+    return;+  }+  ff->convertToType0(psName, outputFunc, outputStream);+  delete ff;+}++void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc,+			    void *outputStream, char *name,+			    Gushort *codeToGID) {+  // this substitute cmap table maps char codes 0000-ffff directly to+  // glyphs 0000-ffff+  static char cmapTab[36] = {+    0, 0,			// table version number+    0, 1,			// number of encoding tables+    0, 1,			// platform ID+    0, 0,			// encoding ID+    0, 0, 0, 12,		// 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+    0, 0,			// startCount[0]+    0, 0,			// idDelta[0]+    0, 0			// pad to a mulitple of four bytes+  };+  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+    0, 0,			// usWeightClass+    0, 0,			// 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+    0, 0,			// usWinAscent+    0, 0,			// usWinDescent+    0, 0, 0, 0,			// ulCodePageRange1+    0, 0, 0, 0			// ulCodePageRange2+  };+  GBool missingCmap, missingName, missingPost, missingOS2;+  GBool unsortedLoca, badCmapLen, abbrevHMTX;+  int nZeroLengthTables;+  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;+  }++  // 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;+  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 (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+  nZeroLengthTables = 0;+  for (i = 0; i < nTables; ++i) {+    if (tables[i].len == 0) {+      ++nZeroLengthTables;+    }+  }++  // check for an incorrect cmap table length+  badCmapLen = gFalse;+  cmapLen = 0; // make gcc happy+  if (!missingCmap) {+    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 && !badCmapLen && !abbrevHMTX && nZeroLengthTables == 0 &&+      !name && !codeToGID) {+    (*outputFunc)(outputStream, (char *)file, len);+    goto done1;+  }++  // 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) {+    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),+	  &cmpTrueTypeLocaOffset);+    for (i = 0; i < nGlyphs; ++i) {+      locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;+    }+    locaTable[nGlyphs].len = 0;+    qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),+	  &cmpTrueTypeLocaIdx);+    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 = 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] = i + 1;+      newNameTab[6 + i*12 + 8] = i+1 == 2 ? 0 : ((2*n) >> 8); // string length+      newNameTab[6 + i*12 + 9] = i+1 == 2 ? 14 : ((2*n) & 0xff);+      newNameTab[6 + i*12 + 10] = next >> 8;		    // string offset+      newNameTab[6 + i*12 + 11] = 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) {+    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) {+      newCmapTab[44 + 2*i] = codeToGID[i] >> 8;+      newCmapTab[44 + 2*i + 1] = 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] = getU8(pos++, &ok);+    }+    newHHEATab[34] = nGlyphs >> 8;+    newHHEATab[35] = 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    ] = advWidth >> 8;+      newHMTXTab[4*i + 1] = advWidth & 0xff;+      newHMTXTab[4*i + 2] = lsb >> 8;+      newHMTXTab[4*i + 3] = lsb & 0xff;+    }+    for (; i < nGlyphs; ++i) {+      lsb = getU16BE(pos, &ok);+      pos += 2;+      newHMTXTab[4*i    ] = advWidth >> 8;+      newHMTXTab[4*i + 1] = advWidth & 0xff;+      newHMTXTab[4*i + 2] = lsb >> 8;+      newHMTXTab[4*i + 3] = 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 ++               (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) {+      newTables[j] = tables[i];+      newTables[j].origOffset = tables[i].offset;+      if (checkRegion(tables[i].offset, newTables[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);+	}+      }+      if (newTables[j].tag == cmapTag && codeToGID) {+	newTables[j].len = newCmapLen;+	newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab,+						     newCmapLen);+      } 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 (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;+  }+  qsort(newTables, nNewTables, sizeof(TrueTypeTable),+	&cmpTrueTypeTableTag);+  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] = fileChecksum >> 24;+	checksumBuf[1] = fileChecksum >> 16;+	checksumBuf[2] = fileChecksum >> 8;+	checksumBuf[3] = 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 && 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);+ done1:+  gfree(locaTable);+}++void FoFiTrueType::cvtEncoding(char **encoding,+			       FoFiOutputFunc outputFunc,+			       void *outputStream) {+  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, 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,+				  Gushort *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);++  // if there's no 'cmap' table, punt+  if (nCmaps == 0) {+    goto err;+  }++  // 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 {+      sprintf(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, strlen(name));+	buf = GString::format(" {0:d} def\n", k);+	(*outputFunc)(outputStream, buf->getCString(), buf->getLength());+	delete buf;+      }+    }+  }++ err:+  (*outputFunc)(outputStream, "end readonly def\n", 17);+}++void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc,+			    void *outputStream, GString *name,+			    GBool needVerticalMetrics) {+  Guchar headData[54];+  TrueTypeLoca *locaTable;+  Guchar *locaData;+  TrueTypeTable newTables[nT42Tables];+  Guchar tableDir[12 + nT42Tables*16];+  GBool ok;+  Guint checksum;+  int nNewTables;+  int 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;++  // 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, cmpTrueTypeLocaPos 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)+  locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca));+  i = seekTable("loca");+  pos = tables[i].offset;+  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);+    }+  }+  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),+	&cmpTrueTypeLocaOffset);+  for (i = 0; i < nGlyphs; ++i) {+    locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset;+  }+  locaTable[nGlyphs].len = 0;+  qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca),+	&cmpTrueTypeLocaIdx);+  pos = 0;+  for (i = 0; i <= nGlyphs; ++i) {+    locaTable[i].newOffset = pos;+    pos += locaTable[i].len;+    if (pos & 3) {+      pos += 4 - (pos & 3);+    }+  }++  // 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) {+	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] = advance / 256;    // max advance height+	vheaTab[11] = advance % 256;+	length = sizeof(vheaTab);+	checksum = computeTableChecksum(vheaTab, length);+      } else if (needVerticalMetrics && i == t42VmtxTable) {+	length = 4 + (nGlyphs - 1) * 4;+	vmtxTab = (Guchar *)gmalloc(length);+	vmtxTab[0] = advance / 256;+	vmtxTab[1] = 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] = 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 > 0 &&+	    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);+	  gfree(vmtxTab);+	}+      }+    }+  }++  // end the sfnts array+  (*outputFunc)(outputStream, "] def\n", 6);++  gfree(locaData);+  gfree(locaTable);+}++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() {+  Guint topTag;+  int pos, ver, i, j;++  parsedOk = gTrue;++  // look for a collection (TTC)+  topTag = getU32BE(0, &parsedOk);+  if (!parsedOk) {+    return;+  }+  if (topTag == ttcfTag) {+    pos = getU32BE(12, &parsedOk);+    if (!parsedOk) {+      return;+    }+  } else {+    pos = 0;+  }++  // check the sfnt version+  ver = getU32BE(pos, &parsedOk);+  if (!parsedOk) {+    return;+  }+  openTypeCFF = ver == 0x4f54544f; // 'OTTO'++  // read the table directory+  nTables = getU16BE(pos + 4, &parsedOk);+  if (!parsedOk) {+    return;+  }+  tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable));+  pos += 12;+  for (i = 0; i < nTables; ++i) {+    tables[i].tag = getU32BE(pos, &parsedOk);+    tables[i].checksum = getU32BE(pos + 4, &parsedOk);+    tables[i].offset = (int)getU32BE(pos + 8, &parsedOk);+    tables[i].len = (int)getU32BE(pos + 12, &parsedOk);+    if (tables[i].offset + tables[i].len < tables[i].offset ||+	tables[i].offset + tables[i].len > len) {+      parsedOk = gFalse;+    }+    pos += 16;+  }+  if (!parsedOk) {+    return;+  }++  // check for tables that are required by both the TrueType spec and+  // the Type 42 spec+  if (seekTable("head") < 0 ||+      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) {+    pos = tables[i].offset + 2;+    nCmaps = getU16BE(pos, &parsedOk);+    pos += 2;+    if (!parsedOk) {+      return;+    }+    cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap));+    for (j = 0; j < nCmaps; ++j) {+      cmaps[j].platform = getU16BE(pos, &parsedOk);+      cmaps[j].encoding = getU16BE(pos + 2, &parsedOk);+      cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk);+      pos += 8;+      cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk);+      cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk);+    }+    if (!parsedOk) {+      return;+    }+  } else {+    nCmaps = 0;+  }++  // 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 and entries are+  // in bounds)+  if (!openTypeCFF) {+    i = seekTable("loca");+    if (tables[i].len < 0) {+      parsedOk = gFalse;+      return;+    }+    if (tables[i].len < (nGlyphs + 1) * (locaFmt ? 4 : 2)) {+      nGlyphs = tables[i].len / (locaFmt ? 4 : 2) - 1;+    }+    for (j = 0; j <= nGlyphs; ++j) {+      if (locaFmt) {+	pos = (int)getU32BE(tables[i].offset + j*4, &parsedOk);+      } else {+	pos = getU16BE(tables[i].offset + j*2, &parsedOk);+      }+      if (pos < 0 || pos > len) {+	parsedOk = gFalse;+      }+    }+    if (!parsedOk) {+      return;+    }+  }++  // read the post table+  readPostTable();+}++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;+    }+    if (n > nGlyphs) {+      n = nGlyphs;+    }+    stringIdx = 0;+    stringPos = tablePos + 34 + 2*n;+    for (i = 0; i < n; ++i) {+      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) {+	    goto err;+	  }+	}+	m = getU8(stringPos, &ok);+	if (!ok || !checkRegion(stringPos + 1, m)) {+	  goto err;+	}+	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) {+	goto err;+      }+      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(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;+}
+ xpdf/FoFiTrueType.h view
@@ -0,0 +1,174 @@+//========================================================================+//+// 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.+  static FoFiTrueType *make(char *fileA, int lenA);++  // Create a FoFiTrueType object from a file on disk.+  static FoFiTrueType *load(char *fileName);++  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; }++  // 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.+  Gushort 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.)+  Gushort *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();++  // 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,+		       Gushort *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, 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, Gushort *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,+			 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, Gushort *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,+		      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.  (Not useful+  // for OpenType CFF fonts.)+  void writeTTF(FoFiOutputFunc outputFunc, void *outputStream,+		char *name = NULL, Gushort *codeToGID = NULL);++private:++  FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA);+  void cvtEncoding(char **encoding,+		   FoFiOutputFunc outputFunc,+		   void *outputStream);+  void cvtCharStrings(char **encoding,+		      Gushort *codeToGID,+		      FoFiOutputFunc outputFunc,+		      void *outputStream);+  void cvtSfnts(FoFiOutputFunc outputFunc,+		void *outputStream, GString *name,+		GBool needVerticalMetrics);+  void dumpString(Guchar *s, int length,+		  FoFiOutputFunc outputFunc,+		  void *outputStream);+  Guint computeTableChecksum(Guchar *data, int length);+  void parse();+  void readPostTable();+  int seekTable(char *tag);++  TrueTypeTable *tables;+  int nTables;+  TrueTypeCmap *cmaps;+  int nCmaps;+  int nGlyphs;+  int locaFmt;+  int bbox[4];+  GHash *nameToGID;+  GBool openTypeCFF;++  GBool parsedOk;+};++#endif
+ xpdf/FoFiType1.cc view
@@ -0,0 +1,252 @@+//========================================================================+//+// 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 "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;+  parsed = gFalse;+}++FoFiType1::~FoFiType1() {+  int i;++  if (name) {+    gfree(name);+  }+  if (encoding && encoding != 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::writeEncoded(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 && 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, 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]) {+      sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]);+      (*outputFunc)(outputStream, buf, strlen(buf));+    }+  }+  (*outputFunc)(outputStream, "readonly def\n", 13);+  +  // find the end of the encoding data+  //~ this ought to parse PostScript tokens+  if (!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 && strncmp(line2, "/Encoding", 9);+	 line2 = getNextLine(line2), ++i) ;+    if (i < 20 && line2) {+      (*outputFunc)(outputStream, line, line2 - line);+      if (!strncmp(line2, "/Encoding StandardEncoding def", 30)) {+	line = getNextLine(line2);+      } else {+	// skip "/Encoding" + one whitespace char,+	// then look for 'def' preceded by PostScript whitespace+	p = line2 + 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;+	  }+	}+      }+    }++    // copy everything after the encoding+    if (line) {+      (*outputFunc)(outputStream, line, ((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, i, j;++  for (i = 1, line = (char *)file;+       i <= 100 && line && (!name || !encoding);+       ++i) {++    // get font name+    if (!name && !strncmp(line, "/FontName", 9)) {+      strncpy(buf, line, 255);+      buf[255] = '\0';+      if ((p = strchr(buf+9, '/')) &&+	  (p = strtok(p+1, " \t\n\r"))) {+	name = copyString(p);+      }+      line = getNextLine(line);++    // get encoding+    } else if (!encoding &&+	       !strncmp(line, "/Encoding StandardEncoding def", 30)) {+      encoding = fofiType1StandardEncoding;+    } else if (!encoding &&+	       !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 = line1 - line) > 255) {+	  n = 255;+	}+	strncpy(buf, line, n);+	buf[n] = '\0';+	for (p = buf; *p == ' ' || *p == '\t'; ++p) ;+	if (!strncmp(p, "dup", 3)) {+	  for (p += 3; *p == ' ' || *p == '\t'; ++p) ;+	  for (p2 = p; *p2 >= '0' && *p2 <= '9'; ++p2) ;+	  if (*p2) {+	    c = *p2;+	    *p2 = '\0';+	    code = atoi(p);+	    *p2 = c;+	    if (code == 8 && *p2 == '#') {+	      code = 0;+	      for (++p2; *p2 >= '0' && *p2 <= '7'; ++p2) {+		code = code * 8 + (*p2 - '0');+	      }+	    }+	    if (code < 256) {+	      for (p = p2; *p == ' ' || *p == '\t'; ++p) ;+	      if (*p == '/') {+		++p;+		for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;+		*p2 = '\0';+		encoding[code] = copyString(p);+	      }+	    }+	  }+	} else {+	  if (strtok(buf, " \t") &&+	      (p = strtok(NULL, " \t\n\r")) && !strcmp(p, "def")) {+	    break;+	  }+	}+      }+      //~ check for getinterval/putinterval junk++    } else {+      line = getNextLine(line);+    }+  }++  parsed = gTrue;+}
+ xpdf/FoFiType1.h view
@@ -0,0 +1,59 @@+//========================================================================+//+// 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();++  // Write a version of the Type 1 font file with a new encoding.+  void writeEncoded(char **newEncoding,+		    FoFiOutputFunc outputFunc, void *outputStream);++private:++  FoFiType1(char *fileA, int lenA, GBool freeFileDataA);++  char *getNextLine(char *line);+  void parse();++  char *name;+  char **encoding;+  GBool parsed;+};++#endif
+ xpdf/FoFiType1C.cc view
@@ -0,0 +1,2603 @@+//========================================================================+//+// 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 "GString.h"+#include "FoFiEncodings.h"+#include "FoFiType1C.h"++//------------------------------------------------------------------------++static char hexChars[17] = "0123456789ABCDEF";++//------------------------------------------------------------------------+// 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 != fofiType1StandardEncoding &&+      encoding != 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;+}++Gushort *FoFiType1C::getCIDToGIDMap(int *nCIDs) {+  Gushort *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 = (Gushort *)gmallocn(n, sizeof(Gushort));+  memset(map, 0, n * sizeof(Gushort));+  for (i = 0; i < nGlyphs; ++i) {+    map[charset[i]] = i;+  }+  *nCIDs = n;+  return map;+}++void FoFiType1C::convertToType1(char *psName, char **newEncoding, GBool ascii,+				FoFiOutputFunc outputFunc,+				void *outputStream) {+  int psNameLen;+  Type1CEexecBuf eb;+  Type1CIndex subrIdx;+  Type1CIndexVal val;+  GString *buf;+  char buf2[256];+  char **enc;+  GBool ok;+  int i;++  if (psName) {+    psNameLen = 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, 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 (", 10);+    (*outputFunc)(outputStream, buf2, strlen(buf2));+    (*outputFunc)(outputStream, ") readonly def\n", 15);+  }+  if (topDict.noticeSID != 0) {+    getString(topDict.noticeSID, buf2, &ok);+    (*outputFunc)(outputStream, "/Notice (", 9);+    (*outputFunc)(outputStream, buf2, strlen(buf2));+    (*outputFunc)(outputStream, ") readonly def\n", 15);+  }+  if (topDict.copyrightSID != 0) {+    getString(topDict.copyrightSID, buf2, &ok);+    (*outputFunc)(outputStream, "/Copyright (", 12);+    (*outputFunc)(outputStream, buf2, strlen(buf2));+    (*outputFunc)(outputStream, ") readonly def\n", 15);+  }+  if (topDict.fullNameSID != 0) {+    getString(topDict.fullNameSID, buf2, &ok);+    (*outputFunc)(outputStream, "/FullName (", 11);+    (*outputFunc)(outputStream, buf2, strlen(buf2));+    (*outputFunc)(outputStream, ") readonly def\n", 15);+  }+  if (topDict.familyNameSID != 0) {+    getString(topDict.familyNameSID, buf2, &ok);+    (*outputFunc)(outputStream, "/FamilyName (", 13);+    (*outputFunc)(outputStream, buf2, strlen(buf2));+    (*outputFunc)(outputStream, ") readonly def\n", 15);+  }+  if (topDict.weightSID != 0) {+    getString(topDict.weightSID, buf2, &ok);+    (*outputFunc)(outputStream, "/Weight (", 9);+    (*outputFunc)(outputStream, buf2, strlen(buf2));+    (*outputFunc)(outputStream, ") readonly def\n", 15);+  }+  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 == 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 : 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) {+    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) {+    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,+				   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+  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;+  }++  // 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[gid]].subrsOffset, &subrIdx, &ok);+	if (!ok) {+	  subrIdx.pos = -1;+	}+	cvtGlyph(val.pos, val.len, charStrings,+		 &subrIdx, &privateDicts[fdSelect[gid]], gTrue);+      }+    }+  }+  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, 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, strlen(buf2));+      (*outputFunc)(outputStream, ") def\n", 6);+    }+    ok = gTrue;+    getString(topDict.orderingSID, buf2, &ok);+    if (ok) {+      (*outputFunc)(outputStream, "  /Ordering (", 13);+      (*outputFunc)(outputStream, buf2, 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) {+      (*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) {+      (*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) {+	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,+				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+  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;+  }++  // 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;+    for (j = i==0 ? 1 : 0; j < 256 && i+j < nCIDs; ++j) {+      if (cidMap[i+j] >= 0) {+	fd = fdSelect[cidMap[i+j]];+	break;+      }+    }++    // font dictionary (unencrypted section)+    (*outputFunc)(outputStream, "16 dict begin\n", 14);+    (*outputFunc)(outputStream, "/FontName /", 11);+    (*outputFunc)(outputStream, psName, 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) {+      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) {+      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, 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, 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, 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);++  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) {+  Type1CIndexVal val;+  GBool ok, dFP;+  double d, dx, dy;+  Gushort r2;+  Guchar byte;+  int pos, subrBias, start, i, k;++  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;+  }++  pos = offset;+  while (pos < offset + nBytes) {+    ok = gTrue;+    pos = getOp(pos, gTrue, &ok);+    if (!ok) {+      break;+    }+    if (!ops[nOps - 1].isNum) {+      --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;+	dFP = gFalse;+	for (k = 0; k < nOps; k += 2) {+	  // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints+	  if (ops[k+1].num < 0) {+	    d += ops[k].num + ops[k+1].num;+	    dFP |= ops[k].isFP | ops[k+1].isFP;+	    cvtNum(d, dFP, charBuf);+	    cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);+	  } else {+	    d += ops[k].num;+	    dFP |= ops[k].isFP;+	    cvtNum(d, dFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    d += ops[k+1].num;+	    dFP |= ops[k+1].isFP;+	  }+	  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;+	dFP = gFalse;+	for (k = 0; k < nOps; k += 2) {+	  // convert Type 2 edge hints (-20 or -21) to Type 1 ghost hints+	  if (ops[k+1].num < 0) {+	    d += ops[k].num + ops[k+1].num;+	    dFP |= ops[k].isFP | ops[k+1].isFP;+	    cvtNum(d, dFP, charBuf);+	    cvtNum(-ops[k+1].num, ops[k+1].isFP, charBuf);+	  } else {+	    d += ops[k].num;+	    dFP |= ops[k].isFP;+	    cvtNum(d, dFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    d += ops[k+1].num;+	    dFP |= ops[k+1].isFP;+	  }+	  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].num, ops[0].isFP, 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 < nOps; k += 2) {+	  cvtNum(ops[k].num, ops[k].isFP, charBuf);+	  cvtNum(ops[k+1].num, ops[k+1].isFP, 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].num, ops[k].isFP, 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].num, ops[k].isFP, 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].num, ops[k].isFP, charBuf);+	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	  cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	  cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);+	  cvtNum(ops[k+5].num, ops[k+5].isFP, 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 + (int)ops[nOps - 1].num;+	  --nOps;+	  ok = gTrue;+	  getIndexVal(subrIdx, k, &val, &ok);+	  if (ok) {+	    cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);+	  }+	} 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(0, gFalse, charBuf);+	  cvtNum(ops[0].num, ops[0].isFP, charBuf);+	  cvtNum(ops[1].num, ops[1].isFP, charBuf);+	  cvtNum(ops[2].num, ops[2].isFP, charBuf);+	  cvtNum(ops[3].num, ops[3].isFP, 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].num, ops[0].isFP, charBuf);+	cvtNum(ops[1].num, ops[1].isFP, 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].num, ops[0].isFP, 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 - 2; k += 6) {+	  cvtNum(ops[k].num, ops[k].isFP, charBuf);+	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	  cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	  cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);+	  cvtNum(ops[k+5].num, ops[k+5].isFP, charBuf);+	  charBuf->append((char)8);+	}+	cvtNum(ops[k].num, ops[k].isFP, charBuf);+	cvtNum(ops[k+1].num, ops[k].isFP, 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 - 6; k += 2) {+	  cvtNum(ops[k].num, ops[k].isFP, charBuf);+	  cvtNum(ops[k+1].num, ops[k].isFP, charBuf);+	  charBuf->append((char)5);+	}+	cvtNum(ops[k].num, ops[k].isFP, charBuf);+	cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);+	cvtNum(ops[k+5].num, ops[k+5].isFP, 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) {+	  cvtNum(ops[0].num, ops[0].isFP, charBuf);+	  cvtNum(ops[1].num, ops[1].isFP, charBuf);+	  cvtNum(ops[2].num, ops[2].isFP, charBuf);+	  cvtNum(ops[3].num, ops[3].isFP, charBuf);+	  cvtNum(0, gFalse, charBuf);+	  cvtNum(ops[4].num, ops[4].isFP, charBuf);+	  charBuf->append((char)8);+	  k = 5;+	} else {+	  k = 0;+	}+	for (; k < nOps; k += 4) {+	  cvtNum(0, gFalse, charBuf);+	  cvtNum(ops[k].num, ops[k].isFP, charBuf);+	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	  cvtNum(0, gFalse, charBuf);+	  cvtNum(ops[k+3].num, ops[k+3].isFP, 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) {+	  cvtNum(ops[1].num, ops[1].isFP, charBuf);+	  cvtNum(ops[0].num, ops[0].isFP, charBuf);+	  cvtNum(ops[2].num, ops[2].isFP, charBuf);+	  cvtNum(ops[3].num, ops[3].isFP, charBuf);+	  cvtNum(ops[4].num, ops[4].isFP, charBuf);+	  cvtNum(0, gFalse, charBuf);+	  charBuf->append((char)8);+	  k = 5;+	} else {+	  k = 0;+	}+	for (; k < nOps; k += 4) {+	  cvtNum(ops[k].num, ops[k].isFP, charBuf);+	  cvtNum(0, gFalse, charBuf);+	  cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	  cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	  cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	  cvtNum(0, gFalse, charBuf);+	  charBuf->append((char)8);+	}+	nOps = 0;+	openPath = gTrue;+	break;+      case 0x001d:		// callgsubr+	if (nOps >= 1) {+	  k = gsubrBias + (int)ops[nOps - 1].num;+	  --nOps;+	  ok = gTrue;+	  getIndexVal(&gsubrIdx, k, &val, &ok);+	  if (ok) {+	    cvtGlyph(val.pos, val.len, charBuf, subrIdx, pDict, gFalse);+	  }+	} 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 < nOps && k != nOps-5; k += 4) {+	  if (k % 8 == 0) {+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	    charBuf->append((char)30);+	  } else {+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	    charBuf->append((char)31);+	  }+	}+	if (k == nOps-5) {+	  if (k % 8 == 0) {+	    cvtNum(0, gFalse, charBuf);+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);+	  } else {+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(0, gFalse, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, 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 < nOps && k != nOps-5; k += 4) {+	  if (k % 8 == 0) {+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	    charBuf->append((char)31);+	  } else {+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	    charBuf->append((char)30);+	  }+	}+	if (k == nOps-5) {+	  if (k % 8 == 0) {+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(0, gFalse, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	  } else {+	    cvtNum(0, gFalse, charBuf);+	    cvtNum(ops[k].num, ops[k].isFP, charBuf);+	    cvtNum(ops[k+1].num, ops[k+1].isFP, charBuf);+	    cvtNum(ops[k+2].num, ops[k+2].isFP, charBuf);+	    cvtNum(ops[k+3].num, ops[k+3].isFP, charBuf);+	    cvtNum(ops[k+4].num, ops[k+4].isFP, charBuf);+	  }+	  charBuf->append((char)8);+	}+	nOps = 0;+	openPath = gTrue;+	break;+      case 0x0c00:		// dotsection (should be Type 1 only?)+	// ignored+	nOps = 0;+	break;+      case 0x0c03:		// and+      case 0x0c04:		// or+      case 0x0c05:		// not+      case 0x0c08:		// store+      case 0x0c09:		// abs+      case 0x0c0a:		// add+      case 0x0c0b:		// sub+      case 0x0c0c:		// div+      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) {+	  //~ error(-1, "Wrong number of args (%d) to Type 2 hflex", nOps);+	}+	cvtNum(ops[0].num, ops[0].isFP, charBuf);+	cvtNum(0, gFalse, charBuf);+	cvtNum(ops[1].num, ops[1].isFP, charBuf);+	cvtNum(ops[2].num, ops[2].isFP, charBuf);+	cvtNum(ops[3].num, ops[3].isFP, charBuf);+	cvtNum(0, gFalse, charBuf);+	charBuf->append((char)8);+	cvtNum(ops[4].num, ops[4].isFP, charBuf);+	cvtNum(0, gFalse, charBuf);+	cvtNum(ops[5].num, ops[5].isFP, charBuf);+	cvtNum(-ops[2].num, ops[2].isFP, charBuf);+	cvtNum(ops[6].num, ops[6].isFP, charBuf);+	cvtNum(0, gFalse, charBuf);+	charBuf->append((char)8);+	nOps = 0;+	openPath = gTrue;+	break;+      case 0x0c23:		// flex+	if (nOps != 13) {+	  //~ error(-1, "Wrong number of args (%d) to Type 2 flex", nOps);+	}+	cvtNum(ops[0].num, ops[0].isFP, charBuf);+	cvtNum(ops[1].num, ops[1].isFP, charBuf);+	cvtNum(ops[2].num, ops[2].isFP, charBuf);+	cvtNum(ops[3].num, ops[3].isFP, charBuf);+	cvtNum(ops[4].num, ops[4].isFP, charBuf);+	cvtNum(ops[5].num, ops[5].isFP, charBuf);+	charBuf->append((char)8);+	cvtNum(ops[6].num, ops[6].isFP, charBuf);+	cvtNum(ops[7].num, ops[7].isFP, charBuf);+	cvtNum(ops[8].num, ops[8].isFP, charBuf);+	cvtNum(ops[9].num, ops[9].isFP, charBuf);+	cvtNum(ops[10].num, ops[10].isFP, charBuf);+	cvtNum(ops[11].num, ops[11].isFP, charBuf);+	charBuf->append((char)8);+	nOps = 0;+	openPath = gTrue;+	break;+      case 0x0c24:		// hflex1+	if (nOps != 9) {+	  //~ error(-1, "Wrong number of args (%d) to Type 2 hflex1", nOps);+	}+	cvtNum(ops[0].num, ops[0].isFP, charBuf);+	cvtNum(ops[1].num, ops[1].isFP, charBuf);+	cvtNum(ops[2].num, ops[2].isFP, charBuf);+	cvtNum(ops[3].num, ops[3].isFP, charBuf);+	cvtNum(ops[4].num, ops[4].isFP, charBuf);+	cvtNum(0, gFalse, charBuf);+	charBuf->append((char)8);+	cvtNum(ops[5].num, ops[5].isFP, charBuf);+	cvtNum(0, gFalse, charBuf);+	cvtNum(ops[6].num, ops[6].isFP, charBuf);+	cvtNum(ops[7].num, ops[7].isFP, charBuf);+	cvtNum(ops[8].num, ops[8].isFP, charBuf);+	cvtNum(-(ops[1].num + ops[3].num + ops[7].num),+	       ops[1].isFP | ops[3].isFP | ops[7].isFP, charBuf);+	charBuf->append((char)8);+	nOps = 0;+	openPath = gTrue;+	break;+      case 0x0c25:		// flex1+	if (nOps != 11) {+	  //~ error(-1, "Wrong number of args (%d) to Type 2 flex1", nOps);+	}+	cvtNum(ops[0].num, ops[0].isFP, charBuf);+	cvtNum(ops[1].num, ops[1].isFP, charBuf);+	cvtNum(ops[2].num, ops[2].isFP, charBuf);+	cvtNum(ops[3].num, ops[3].isFP, charBuf);+	cvtNum(ops[4].num, ops[4].isFP, charBuf);+	cvtNum(ops[5].num, ops[5].isFP, charBuf);+	charBuf->append((char)8);+	cvtNum(ops[6].num, ops[6].isFP, charBuf);+	cvtNum(ops[7].num, ops[7].isFP, charBuf);+	cvtNum(ops[8].num, ops[8].isFP, charBuf);+	cvtNum(ops[9].num, ops[9].isFP, charBuf);+	dx = ops[0].num + ops[2].num + ops[4].num + ops[6].num + ops[8].num;+	dy = ops[1].num + ops[3].num + ops[5].num + ops[7].num + ops[9].num;+	if (fabs(dx) > fabs(dy)) {+	  cvtNum(ops[10].num, ops[10].isFP, charBuf);+	  cvtNum(-dy, ops[1].isFP | ops[3].isFP | ops[5].isFP |+		      ops[7].isFP | ops[9].isFP, charBuf);+	} else {+	  cvtNum(-dx, ops[0].isFP | ops[2].isFP | ops[4].isFP |+		      ops[6].isFP | ops[8].isFP, charBuf);+	  cvtNum(ops[10].num, ops[10].isFP, charBuf);+	}+	charBuf->append((char)8);+	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 = charBuf->getChar(i) ^ (r2 >> 8);+      charBuf->setChar(i, byte);+      r2 = (byte + r2) * 52845 + 22719;+    }+  }+}++void FoFiType1C::cvtGlyphWidth(GBool useOp, GString *charBuf,+			       Type1CPrivateDict *pDict) {+  double w;+  GBool wFP;+  int i;++  if (useOp) {+    w = pDict->nominalWidthX + ops[0].num;+    wFP = pDict->nominalWidthXFP | ops[0].isFP;+    for (i = 1; i < nOps; ++i) {+      ops[i-1] = ops[i];+    }+    --nOps;+  } else {+    w = pDict->defaultWidthX;+    wFP = pDict->defaultWidthXFP;+  }+  cvtNum(0, gFalse, charBuf);+  cvtNum(w, wFP, charBuf);+  charBuf->append((char)13);+}++void FoFiType1C::cvtNum(double x, GBool isFP, GString *charBuf) {+  Guchar buf[12];+  int y, n;++  n = 0;+  if (isFP) {+    if (x >= -32768 && x < 32768) {+      y = (int)(x * 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");+    }+  } else {+    y = (int)x;+    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;+    }+  }+  charBuf->append((char *)buf, n);+}++void FoFiType1C::eexecWrite(Type1CEexecBuf *eb, char *s) {+  Guchar *p;+  Guchar x;++  for (p = (Guchar *)s; *p; ++p) {+    x = *p ^ (eb->r1 >> 8);+    eb->r1 = (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] ^ (eb->r1 >> 8);+    eb->r1 = (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);+    }+  }+}++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 {+    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].isNum) {+      --nOps; // drop the operator+      if (topDict.firstOp < 0) {+	topDict.firstOp = ops[nOps].op;+      }+      switch (ops[nOps].op) {+      case 0x0000: topDict.versionSID = (int)ops[0].num; break;+      case 0x0001: topDict.noticeSID = (int)ops[0].num; break;+      case 0x0c00: topDict.copyrightSID = (int)ops[0].num; break;+      case 0x0002: topDict.fullNameSID = (int)ops[0].num; break;+      case 0x0003: topDict.familyNameSID = (int)ops[0].num; break;+      case 0x0004: topDict.weightSID = (int)ops[0].num; break;+      case 0x0c01: topDict.isFixedPitch = (int)ops[0].num; break;+      case 0x0c02: topDict.italicAngle = ops[0].num; break;+      case 0x0c03: topDict.underlinePosition = ops[0].num; break;+      case 0x0c04: topDict.underlineThickness = ops[0].num; break;+      case 0x0c05: topDict.paintType = (int)ops[0].num; break;+      case 0x0c06: topDict.charstringType = (int)ops[0].num; break;+      case 0x0c07: topDict.fontMatrix[0] = ops[0].num;+	           topDict.fontMatrix[1] = ops[1].num;+	           topDict.fontMatrix[2] = ops[2].num;+	           topDict.fontMatrix[3] = ops[3].num;+	           topDict.fontMatrix[4] = ops[4].num;+	           topDict.fontMatrix[5] = ops[5].num;+		   topDict.hasFontMatrix = gTrue; break;+      case 0x000d: topDict.uniqueID = (int)ops[0].num; break;+      case 0x0005: topDict.fontBBox[0] = ops[0].num;+	           topDict.fontBBox[1] = ops[1].num;+	           topDict.fontBBox[2] = ops[2].num;+	           topDict.fontBBox[3] = ops[3].num; break;+      case 0x0c08: topDict.strokeWidth = ops[0].num; break;+      case 0x000f: topDict.charsetOffset = (int)ops[0].num; break;+      case 0x0010: topDict.encodingOffset = (int)ops[0].num; break;+      case 0x0011: topDict.charStringsOffset = (int)ops[0].num; break;+      case 0x0012: topDict.privateSize = (int)ops[0].num;+	           topDict.privateOffset = (int)ops[1].num; break;+      case 0x0c1e: topDict.registrySID = (int)ops[0].num;+	           topDict.orderingSID = (int)ops[1].num;+		   topDict.supplement = (int)ops[2].num; break;+      case 0x0c24: topDict.fdArrayOffset = (int)ops[0].num; break;+      case 0x0c25: topDict.fdSelectOffset = (int)ops[0].num; 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;+  pSize = pOffset = 0;+  pos = offset;+  nOps = 0;+  while (pos < offset + length) {+    pos = getOp(pos, gFalse, &parsedOk);+    if (!parsedOk) {+      return;+    }+    if (!ops[nOps - 1].isNum) {+      if (ops[nOps - 1].op == 0x0012) {+	if (nOps < 3) {+	  parsedOk = gFalse;+	  return;+	}+	pSize = (int)ops[0].num;+	pOffset = (int)ops[1].num;+	break;+      } else if (ops[nOps - 1].op == 0x0c07) {+	fontMatrix[0] = ops[0].num;+	fontMatrix[1] = ops[1].num;+	fontMatrix[2] = ops[2].num;+	fontMatrix[3] = ops[3].num;+	fontMatrix[4] = ops[4].num;+	fontMatrix[5] = ops[5].num;+	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->defaultWidthXFP = gFalse;+  pDict->nominalWidthX = 0;+  pDict->nominalWidthXFP = gFalse;++  // 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].isNum) {+      --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].num;+	break;+      case 0x0c0a:+	pDict->blueShift = (int)ops[0].num;+	break;+      case 0x0c0b:+	pDict->blueFuzz = (int)ops[0].num;+	break;+      case 0x000a:+	pDict->stdHW = ops[0].num;+	pDict->hasStdHW = gTrue;+	break;+      case 0x000b:+	pDict->stdVW = ops[0].num;+	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].num != 0;+	pDict->hasForceBold = gTrue;+	break;+      case 0x0c0f:+	pDict->forceBoldThreshold = ops[0].num;+	break;+      case 0x0c11:+	pDict->languageGroup = (int)ops[0].num;+	break;+      case 0x0c12:+	pDict->expansionFactor = ops[0].num;+	break;+      case 0x0c13:+	pDict->initialRandomSeed = (int)ops[0].num;+	break;+      case 0x0013:+	pDict->subrsOffset = offset + (int)ops[0].num;+	break;+      case 0x0014:+	pDict->defaultWidthX = ops[0].num;+	pDict->defaultWidthXFP = ops[0].isFP;+	break;+      case 0x0015:+	pDict->nominalWidthX = ops[0].num;+	pDict->nominalWidthXFP = ops[0].isFP;+	break;+      }+      nOps = 0;+    }+  }+}++void FoFiType1C::readFDSelect() {+  int fdSelectFmt, pos, nRanges, gid0, gid1, fd, i, j;++  fdSelect = (Guchar *)gmalloc(nGlyphs);+  if (topDict.fdSelectOffset == 0) {+    for (i = 0; i < nGlyphs; ++i) {+      fdSelect[i] = 0;+    }+  } else {+    pos = topDict.fdSelectOffset;+    fdSelectFmt = getU8(pos++, &parsedOk);+    if (!parsedOk) {+      return;+    }+    if (fdSelectFmt == 0) {+      if (!checkRegion(pos, nGlyphs)) {+	parsedOk = gFalse;+	return;+      }+      memcpy(fdSelect, file + pos, nGlyphs);+    } 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) {+	  //~ error(-1, "Bad FDSelect table in CID font");+	  parsedOk = gFalse;+	  return;+	}+	for (j = gid0; j < gid1; ++j) {+	  fdSelect[j] = fd;+	}+	gid0 = gid1;+      }+    } else {+      //~ error(-1, "Unknown FDSelect table format in CID font");+      for (i = 0; i < nGlyphs; ++i) {+	fdSelect[i] = 0;+      }+    }+  }+}++void FoFiType1C::buildEncoding() {+  char buf[256];+  int nCodes, nRanges, encFormat;+  int pos, c, sid, nLeft, nSups, i, j;++  if (topDict.encodingOffset == 0) {+    encoding = fofiType1StandardEncoding;++  } else if (topDict.encodingOffset == 1) {+    encoding = 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;+  } else if (topDict.charsetOffset == 1) {+    charset = fofiType1CExpertCharset;+  } else if (topDict.charsetOffset == 2) {+    charset = fofiType1CExpertSubsetCharset;+  } 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);+  op.isNum = gTrue;+  op.isFP = gFalse;++  if (b0 == 28) {+    x = getU8(pos++, ok);+    x = (x << 8) | getU8(pos++, ok);+    if (x & 0x8000) {+      x |= ~0xffff;+    }+    op.num = 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.num = 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.num = atof(buf);+    op.isFP = gTrue;++  } else if (b0 >= 32 && b0 <= 246) {+    op.num = b0 - 139;++  } else if (b0 >= 247 && b0 <= 250) {+    op.num = ((b0 - 247) << 8) + getU8(pos++, ok) + 108;++  } else if (b0 >= 251 && b0 <= 254) {+    op.num = -((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.num = (double)x / 65536.0;+    op.isFP = gTrue;++  } else if (b0 == 12) {+    op.isNum = gFalse;+    op.op = 0x0c00 + getU8(pos++, ok);++  } else {+    op.isNum = gFalse;+    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 += (int)ops[i].num;+    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].num;+    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) {+    *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 < 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;+}
+ xpdf/FoFiType1C.h view
@@ -0,0 +1,233 @@+//========================================================================+//+// 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 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 defaultWidthXFP;+  double nominalWidthX;+  GBool nominalWidthXFP;+};++struct Type1COp {+  GBool isNum;			// true -> number, false -> operator+  GBool isFP;			// true -> floating point number, false -> int+  union {+    double num;			// if num is true+    int op;			// if num is false+  };+};++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();++  // Return the mapping from CIDs to GIDs, and return the number of+  // CIDs in *<nCIDs>.  This is only useful for CID fonts.+  Gushort *getCIDToGIDMap(int *nCIDs);++  // 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, 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.+  void convertToCIDType0(char *psName,+			 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.+  void convertToType0(char *psName,+		      FoFiOutputFunc outputFunc, void *outputStream);++private:++  FoFiType1C(char *fileA, int lenA, GBool freeFileDataA);+  void eexecCvtGlyph(Type1CEexecBuf *eb, char *glyphName,+		     int offset, int nBytes,+		     Type1CIndex *subrIdx,+		     Type1CPrivateDict *pDict);+  void cvtGlyph(int offset, int nBytes, GString *charBuf,+		Type1CIndex *subrIdx, Type1CPrivateDict *pDict,+		GBool top);+  void cvtGlyphWidth(GBool useOp, GString *charBuf,+		     Type1CPrivateDict *pDict);+  void cvtNum(double x, GBool isFP, GString *charBuf);+  void eexecWrite(Type1CEexecBuf *eb, char *s);+  void eexecWriteCharstring(Type1CEexecBuf *eb, Guchar *s, int n);+  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
+ xpdf/FontEncodingTables.cc view
@@ -0,0 +1,1824 @@+//========================================================================+//+// FontEncodingTables.cc+//+// Copyright 2001-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>+#include <stdlib.h>+#include "FontEncodingTables.h"++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"+};++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+};++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"+};++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+};++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"+};++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+};++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+};
+ xpdf/FontEncodingTables.h view
@@ -0,0 +1,20 @@+//========================================================================+//+// FontEncodingTables.h+//+// Copyright 2001-2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef FONTENCODINGTABLES_H+#define FONTENCODINGTABLES_H++extern char *macRomanEncoding[];+extern char *macExpertEncoding[];+extern char *winAnsiEncoding[];+extern char *standardEncoding[];+extern char *expertEncoding[];+extern char *symbolEncoding[];+extern char *zapfDingbatsEncoding[];++#endif
+ xpdf/Function.cc view
@@ -0,0 +1,1573 @@+//========================================================================+//+// 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 "Object.h"+#include "Dict.h"+#include "Stream.h"+#include "Error.h"+#include "Function.h"++//------------------------------------------------------------------------+// Function+//------------------------------------------------------------------------++Function::Function() {+}++Function::~Function() {+}++Function *Function::parse(Object *funcObj) {+  Function *func;+  Dict *dict;+  int funcType;+  Object obj1;++  if (funcObj->isStream()) {+    dict = funcObj->streamGetDict();+  } else if (funcObj->isDict()) {+    dict = funcObj->getDict();+  } else if (funcObj->isName("Identity")) {+    return new IdentityFunction();+  } else {+    error(-1, "Expected function dictionary or stream");+    return NULL;+  }++  if (!dict->lookup("FunctionType", &obj1)->isInt()) {+    error(-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);+  } else if (funcType == 4) {+    func = new PostScriptFunction(funcObj, dict);+  } else {+    error(-1, "Unimplemented function type (%d)", funcType);+    return NULL;+  }+  if (!func->isOk()) {+    delete func;+    return NULL;+  }++  return func;+}++GBool Function::init(Dict *dict) {+  Object obj1, obj2;+  int i;++  //----- Domain+  if (!dict->lookup("Domain", &obj1)->isArray()) {+    error(-1, "Function is missing domain");+    goto err2;+  }+  m = obj1.arrayGetLength() / 2;+  if (m > funcMaxInputs) {+    error(-1, "Functions with more than %d inputs are unsupported",+	  funcMaxInputs);+    goto err2;+  }+  for (i = 0; i < m; ++i) {+    obj1.arrayGet(2*i, &obj2);+    if (!obj2.isNum()) {+      error(-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(-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(-1, "Functions with more than %d outputs are unsupported",+	    funcMaxOutputs);+      goto err2;+    }+    for (i = 0; i < n; ++i) {+      obj1.arrayGet(2*i, &obj2);+      if (!obj2.isNum()) {+	error(-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(-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 i;++  // fill these in with arbitrary values just in case they get used+  // somewhere+  m = funcMaxInputs;+  n = funcMaxOutputs;+  for (i = 0; i < funcMaxInputs; ++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 < funcMaxOutputs; ++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;+  int i;++  samples = NULL;+  sBuf = NULL;+  ok = gFalse;++  //----- initialize the generic stuff+  if (!init(dict)) {+    goto err1;+  }+  if (!hasRange) {+    error(-1, "Type 0 function is missing range");+    goto err1;+  }+  if (m > sampledFuncMaxInputs) {+    error(-1, "Sampled functions with more than %d inputs are unsupported",+	  sampledFuncMaxInputs);+    goto err1;+  }++  //----- buffer+  sBuf = (double *)gmallocn(1 << m, sizeof(double));++  //----- get the stream+  if (!funcObj->isStream()) {+    error(-1, "Type 0 function isn't a stream");+    goto err1;+  }+  str = funcObj->getStream();++  //----- Size+  if (!dict->lookup("Size", &obj1)->isArray() ||+      obj1.arrayGetLength() != m) {+    error(-1, "Function has missing or invalid size array");+    goto err2;+  }+  for (i = 0; i < m; ++i) {+    obj1.arrayGet(i, &obj2);+    if (!obj2.isInt()) {+      error(-1, "Illegal value in function size array");+      goto err3;+    }+    sampleSize[i] = obj2.getInt();+    obj2.free();+  }+  obj1.free();+  idxMul[0] = n;+  for (i = 1; i < m; ++i) {+    idxMul[i] = idxMul[i-1] * sampleSize[i-1];+  }++  //----- BitsPerSample+  if (!dict->lookup("BitsPerSample", &obj1)->isInt()) {+    error(-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(-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(-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(-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(-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)+    nSamples *= sampleSize[i];+  samples = (double *)gmallocn(nSamples, sizeof(double));+  buf = 0;+  bits = 0;+  bitMask = (1 << sampleBits) - 1;+  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();++  ok = gTrue;+  return;++ err3:+  obj2.free();+ err2:+  obj1.free();+ err1:+  return;+}++SampledFunction::~SampledFunction() {+  if (samples) {+    gfree(samples);+  }+  if (sBuf) {+    gfree(sBuf);+  }+}++SampledFunction::SampledFunction(SampledFunction *func) {+  memcpy(this, func, sizeof(SampledFunction));+  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][2];+  double efrac0[funcMaxInputs];+  double efrac1[funcMaxInputs];+  int i, j, k, idx, t;++  // 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 = 0;+    } else if (x > sampleSize[i] - 1) {+      x = sampleSize[i] - 1;+    }+    e[i][0] = (int)x;+    if ((e[i][1] = e[i][0] + 1) >= sampleSize[i]) {+      // this happens if in[i] = domain[i][1]+      e[i][1] = e[i][0];+    }+    efrac1[i] = x - e[i][0];+    efrac0[i] = 1 - efrac1[i];+  }++  // 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) {+      idx = i;+      for (k = 0, t = j; k < m; ++k, t >>= 1) {+	idx += idxMul[k] * (e[k][t & 1]);+      }+      sBuf[j] = samples[idx];+    }++    // 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];+    }+  }+}++//------------------------------------------------------------------------+// 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(-1, "Exponential function with more than one input");+    goto err1;+  }++  //----- C0+  if (dict->lookup("C0", &obj1)->isArray()) {+    if (hasRange && obj1.arrayGetLength() != n) {+      error(-1, "Function's C0 array is wrong length");+      goto err2;+    }+    n = obj1.arrayGetLength();+    for (i = 0; i < n; ++i) {+      obj1.arrayGet(i, &obj2);+      if (!obj2.isNum()) {+	error(-1, "Illegal value in function C0 array");+	goto err3;+      }+      c0[i] = obj2.getNum();+      obj2.free();+    }+  } else {+    if (hasRange && n != 1) {+      error(-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(-1, "Function's C1 array is wrong length");+      goto err2;+    }+    for (i = 0; i < n; ++i) {+      obj1.arrayGet(i, &obj2);+      if (!obj2.isNum()) {+	error(-1, "Illegal value in function C1 array");+	goto err3;+      }+      c1[i] = obj2.getNum();+      obj2.free();+    }+  } else {+    if (n != 1) {+      error(-1, "Function's C1 array is wrong length");+      goto err2;+    }+    c1[0] = 1;+  }+  obj1.free();++  //----- N (exponent)+  if (!dict->lookup("N", &obj1)->isNum()) {+    error(-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(this, 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) {+  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(-1, "Stitching function with more than one input");+    goto err1;+  }++  //----- Functions+  if (!dict->lookup("Functions", &obj1)->isArray()) {+    error(-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)))) {+      goto err2;+    }+    if (i > 0 && (funcs[i]->getInputSize() != 1 ||+		  funcs[i]->getOutputSize() != funcs[0]->getOutputSize())) {+      error(-1, "Incompatible subfunctions in stitching function");+      goto err2;+    }+    obj2.free();+  }+  obj1.free();++  //----- Bounds+  if (!dict->lookup("Bounds", &obj1)->isArray() ||+      obj1.arrayGetLength() != k - 1) {+    error(-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(-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(-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(-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;++  k = func->k;+  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+//------------------------------------------------------------------------++enum PSOp {+  psOpAbs,+  psOpAdd,+  psOpAnd,+  psOpAtan,+  psOpBitshift,+  psOpCeiling,+  psOpCopy,+  psOpCos,+  psOpCvi,+  psOpCvr,+  psOpDiv,+  psOpDup,+  psOpEq,+  psOpExch,+  psOpExp,+  psOpFalse,+  psOpFloor,+  psOpGe,+  psOpGt,+  psOpIdiv,+  psOpIndex,+  psOpLe,+  psOpLn,+  psOpLog,+  psOpLt,+  psOpMod,+  psOpMul,+  psOpNe,+  psOpNeg,+  psOpNot,+  psOpOr,+  psOpPop,+  psOpRoll,+  psOpRound,+  psOpSin,+  psOpSqrt,+  psOpSub,+  psOpTrue,+  psOpTruncate,+  psOpXor,+  psOpIf,+  psOpIfelse,+  psOpReturn+};++// Note: 'if' and 'ifelse' are parsed separately.+// The rest are listed here in alphabetical order.+// The index in this table is equivalent to the entry in PSOp.+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"+};++#define nPSOps (sizeof(psOpNames) / sizeof(char *))++enum PSObjectType {+  psBool,+  psInt,+  psReal,+  psOperator,+  psBlock+};++// In the code array, 'if'/'ifelse' operators take up three slots+// plus space for the code in the subclause(s).+//+//         +---------------------------------++//         | psOperator: psOpIf / psOpIfelse |+//         +---------------------------------++//         | psBlock: ptr=<A>                |+//         +---------------------------------++//         | psBlock: ptr=<B>                |+//         +---------------------------------++//         | if clause                       |+//         | ...                             |+//         | psOperator: psOpReturn          |+//         +---------------------------------++//     <A> | else clause                     |+//         | ...                             |+//         | psOperator: psOpReturn          |+//         +---------------------------------++//     <B> | ...                             |+//+// For 'if', pointer <A> is present in the code stream but unused.++struct PSObject {+  PSObjectType type;+  union {+    GBool booln;		// boolean (stack only)+    int intg;			// integer (stack and code)+    double real;		// real (stack and code)+    PSOp op;			// operator (code only)+    int blk;			// if/ifelse block pointer (code only)+  };+};++#define psStackSize 100++class PSStack {+public:++  PSStack() { sp = psStackSize; }+  void pushBool(GBool booln);+  void pushInt(int intg);+  void pushReal(double real);+  GBool popBool();+  int popInt();+  double popNum();+  GBool empty() { return sp == psStackSize; }+  GBool topIsInt() { return sp < psStackSize && stack[sp].type == psInt; }+  GBool topTwoAreInts()+    { return sp < psStackSize - 1 &&+	     stack[sp].type == psInt &&+             stack[sp+1].type == psInt; }+  GBool topIsReal() { return sp < psStackSize && stack[sp].type == psReal; }+  GBool topTwoAreNums()+    { return sp < psStackSize - 1 &&+	     (stack[sp].type == psInt || stack[sp].type == psReal) &&+	     (stack[sp+1].type == psInt || stack[sp+1].type == psReal); }+  void copy(int n);+  void roll(int n, int j);+  void index(int i);+  void pop();++private:++  GBool checkOverflow(int n = 1);+  GBool checkUnderflow();+  GBool checkType(PSObjectType t1, PSObjectType t2);++  PSObject stack[psStackSize];+  int sp;+};++GBool PSStack::checkOverflow(int n) {+  if (sp - n < 0) {+    error(-1, "Stack overflow in PostScript function");+    return gFalse;+  }+  return gTrue;+}++GBool PSStack::checkUnderflow() {+  if (sp == psStackSize) {+    error(-1, "Stack underflow in PostScript function");+    return gFalse;+  }+  return gTrue;+}++GBool PSStack::checkType(PSObjectType t1, PSObjectType t2) {+  if (stack[sp].type != t1 && stack[sp].type != t2) {+    error(-1, "Type mismatch in PostScript function");+    return gFalse;+  }+  return gTrue;+}++void PSStack::pushBool(GBool booln) {+  if (checkOverflow()) {+    stack[--sp].type = psBool;+    stack[sp].booln = booln;+  }+}++void PSStack::pushInt(int intg) {+  if (checkOverflow()) {+    stack[--sp].type = psInt;+    stack[sp].intg = intg;+  }+}++void PSStack::pushReal(double real) {+  if (checkOverflow()) {+    stack[--sp].type = psReal;+    stack[sp].real = real;+  }+}++GBool PSStack::popBool() {+  if (checkUnderflow() && checkType(psBool, psBool)) {+    return stack[sp++].booln;+  }+  return gFalse;+}++int PSStack::popInt() {+  if (checkUnderflow() && checkType(psInt, psInt)) {+    return stack[sp++].intg;+  }+  return 0;+}++double PSStack::popNum() {+  double ret;++  if (checkUnderflow() && checkType(psInt, psReal)) {+    ret = (stack[sp].type == psInt) ? (double)stack[sp].intg : stack[sp].real;+    ++sp;+    return ret;+  }+  return 0;+}++void PSStack::copy(int n) {+  int i;++  if (sp + n > psStackSize) {+    error(-1, "Stack underflow in PostScript function");+    return;+  }+  if (!checkOverflow(n)) {+    return;+  }+  for (i = sp + n - 1; i >= sp; --i) {+    stack[i - n] = stack[i];+  }+  sp -= n;+}++void PSStack::roll(int n, int j) {+  PSObject obj;+  int i, k;++  if (j >= 0) {+    j %= n;+  } else {+    j = -j % n;+    if (j != 0) {+      j = n - j;+    }+  }+  if (n <= 0 || j == 0) {+    return;+  }+  for (i = 0; i < j; ++i) {+    obj = stack[sp];+    for (k = sp; k < sp + n - 1; ++k) {+      stack[k] = stack[k+1];+    }+    stack[sp + n - 1] = obj;+  }+}++void PSStack::index(int i) {+  if (!checkOverflow()) {+    return;+  }+  --sp;+  stack[sp] = stack[sp + 1 + i];+}++void PSStack::pop() {+  if (!checkUnderflow()) {+    return;+  }+  ++sp;+}++PostScriptFunction::PostScriptFunction(Object *funcObj, Dict *dict) {+  Stream *str;+  int codePtr;+  GString *tok;++  code = NULL;+  codeSize = 0;+  ok = gFalse;++  //----- initialize the generic stuff+  if (!init(dict)) {+    goto err1;+  }+  if (!hasRange) {+    error(-1, "Type 4 function is missing range");+    goto err1;+  }++  //----- get the stream+  if (!funcObj->isStream()) {+    error(-1, "Type 4 function isn't a stream");+    goto err1;+  }+  str = funcObj->getStream();++  //----- parse the function+  codeString = new GString();+  str->reset();+  if (!(tok = getToken(str)) || tok->cmp("{")) {+    error(-1, "Expected '{' at start of PostScript function");+    if (tok) {+      delete tok;+    }+    goto err1;+  }+  delete tok;+  codePtr = 0;+  if (!parseCode(str, &codePtr)) {+    goto err2;+  }+  str->close();++  ok = gTrue;++ err2:+  str->close();+ err1:+  return;+}++PostScriptFunction::PostScriptFunction(PostScriptFunction *func) {+  memcpy(this, func, sizeof(PostScriptFunction));+  code = (PSObject *)gmallocn(codeSize, sizeof(PSObject));+  memcpy(code, func->code, codeSize * sizeof(PSObject));+  codeString = func->codeString->copy();+}++PostScriptFunction::~PostScriptFunction() {+  gfree(code);+  delete codeString;+}++void PostScriptFunction::transform(double *in, double *out) {+  PSStack *stack;+  int i;++  stack = new PSStack();+  for (i = 0; i < m; ++i) {+    //~ may need to check for integers here+    stack->pushReal(in[i]);+  }+  exec(stack, 0);+  for (i = n - 1; i >= 0; --i) {+    out[i] = stack->popNum();+    if (out[i] < range[i][0]) {+      out[i] = range[i][0];+    } else if (out[i] > range[i][1]) {+      out[i] = range[i][1];+    }+  }+  // if (!stack->empty()) {+  //   error(-1, "Extra values on stack at end of PostScript function");+  // }+  delete stack;+}++GBool PostScriptFunction::parseCode(Stream *str, int *codePtr) {+  GString *tok;+  char *p;+  GBool isReal;+  int opPtr, elsePtr;+  int a, b, mid, cmp;++  while (1) {+    if (!(tok = getToken(str))) {+      error(-1, "Unexpected end of PostScript function stream");+      return gFalse;+    }+    p = tok->getCString();+    if (isdigit(*p) || *p == '.' || *p == '-') {+      isReal = gFalse;+      for (++p; *p; ++p) {+	if (*p == '.') {+	  isReal = gTrue;+	  break;+	}+      }+      resizeCode(*codePtr);+      if (isReal) {+	code[*codePtr].type = psReal;+	code[*codePtr].real = atof(tok->getCString());+      } else {+	code[*codePtr].type = psInt;+	code[*codePtr].intg = atoi(tok->getCString());+      }+      ++*codePtr;+      delete tok;+    } else if (!tok->cmp("{")) {+      delete tok;+      opPtr = *codePtr;+      *codePtr += 3;+      resizeCode(opPtr + 2);+      if (!parseCode(str, codePtr)) {+	return gFalse;+      }+      if (!(tok = getToken(str))) {+	error(-1, "Unexpected end of PostScript function stream");+	return gFalse;+      }+      if (!tok->cmp("{")) {+	elsePtr = *codePtr;+	if (!parseCode(str, codePtr)) {+	  return gFalse;+	}+	delete tok;+	if (!(tok = getToken(str))) {+	  error(-1, "Unexpected end of PostScript function stream");+	  return gFalse;+	}+      } else {+	elsePtr = -1;+      }+      if (!tok->cmp("if")) {+	if (elsePtr >= 0) {+	  error(-1, "Got 'if' operator with two blocks in PostScript function");+	  return gFalse;+	}+	code[opPtr].type = psOperator;+	code[opPtr].op = psOpIf;+	code[opPtr+2].type = psBlock;+	code[opPtr+2].blk = *codePtr;+      } else if (!tok->cmp("ifelse")) {+	if (elsePtr < 0) {+	  error(-1, "Got 'ifelse' operator with one blocks in PostScript function");+	  return gFalse;+	}+	code[opPtr].type = psOperator;+	code[opPtr].op = psOpIfelse;+	code[opPtr+1].type = psBlock;+	code[opPtr+1].blk = elsePtr;+	code[opPtr+2].type = psBlock;+	code[opPtr+2].blk = *codePtr;+      } else {+	error(-1, "Expected if/ifelse operator in PostScript function");+	delete tok;+	return gFalse;+      }+      delete tok;+    } else if (!tok->cmp("}")) {+      delete tok;+      resizeCode(*codePtr);+      code[*codePtr].type = psOperator;+      code[*codePtr].op = psOpReturn;+      ++*codePtr;+      break;+    } else {+      a = -1;+      b = nPSOps;+      // 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(-1, "Unknown operator '%s' in PostScript function",+	      tok->getCString());+	delete tok;+	return gFalse;+      }+      delete tok;+      resizeCode(*codePtr);+      code[*codePtr].type = psOperator;+      code[*codePtr].op = (PSOp)a;+      ++*codePtr;+    }+  }+  return gTrue;+}++GString *PostScriptFunction::getToken(Stream *str) {+  GString *s;+  int c;+  GBool comment;++  s = new GString();+  comment = gFalse;+  while (1) {+    if ((c = str->getChar()) == EOF) {+      break;+    }+    codeString->append(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(c);+    }+  } else {+    while (1) {+      s->append((char)c);+      c = str->lookChar();+      if (c == EOF || !isalnum(c)) {+	break;+      }+      str->getChar();+      codeString->append(c);+    }+  }+  return s;+}++void PostScriptFunction::resizeCode(int newSize) {+  if (newSize >= codeSize) {+    codeSize += 64;+    code = (PSObject *)greallocn(code, codeSize, sizeof(PSObject));+  }+}++void PostScriptFunction::exec(PSStack *stack, int codePtr) {+  int i1, i2;+  double r1, r2;+  GBool b1, b2;++  while (1) {+    switch (code[codePtr].type) {+    case psInt:+      stack->pushInt(code[codePtr++].intg);+      break;+    case psReal:+      stack->pushReal(code[codePtr++].real);+      break;+    case psOperator:+      switch (code[codePtr++].op) {+      case psOpAbs:+	if (stack->topIsInt()) {+	  stack->pushInt(abs(stack->popInt()));+	} else {+	  stack->pushReal(fabs(stack->popNum()));+	}+	break;+      case psOpAdd:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushInt(i1 + i2);+	} else {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushReal(r1 + r2);+	}+	break;+      case psOpAnd:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushInt(i1 & i2);+	} else {+	  b2 = stack->popBool();+	  b1 = stack->popBool();+	  stack->pushBool(b1 && b2);+	}+	break;+      case psOpAtan:+	r2 = stack->popNum();+	r1 = stack->popNum();+	stack->pushReal(atan2(r1, r2));+	break;+      case psOpBitshift:+	i2 = stack->popInt();+	i1 = stack->popInt();+	if (i2 > 0) {+	  stack->pushInt(i1 << i2);+	} else if (i2 < 0) {+	  stack->pushInt((int)((Guint)i1 >> i2));+	} else {+	  stack->pushInt(i1);+	}+	break;+      case psOpCeiling:+	if (!stack->topIsInt()) {+	  stack->pushReal(ceil(stack->popNum()));+	}+	break;+      case psOpCopy:+	stack->copy(stack->popInt());+	break;+      case psOpCos:+	stack->pushReal(cos(stack->popNum()));+	break;+      case psOpCvi:+	if (!stack->topIsInt()) {+	  stack->pushInt((int)stack->popNum());+	}+	break;+      case psOpCvr:+	if (!stack->topIsReal()) {+	  stack->pushReal(stack->popNum());+	}+	break;+      case psOpDiv:+	r2 = stack->popNum();+	r1 = stack->popNum();+	stack->pushReal(r1 / r2);+	break;+      case psOpDup:+	stack->copy(1);+	break;+      case psOpEq:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushBool(i1 == i2);+	} else if (stack->topTwoAreNums()) {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushBool(r1 == r2);+	} else {+	  b2 = stack->popBool();+	  b1 = stack->popBool();+	  stack->pushBool(b1 == b2);+	}+	break;+      case psOpExch:+	stack->roll(2, 1);+	break;+      case psOpExp:+	r2 = stack->popNum();+	r1 = stack->popNum();+	stack->pushReal(pow(r1, r2));+	break;+      case psOpFalse:+	stack->pushBool(gFalse);+	break;+      case psOpFloor:+	if (!stack->topIsInt()) {+	  stack->pushReal(floor(stack->popNum()));+	}+	break;+      case psOpGe:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushBool(i1 >= i2);+	} else {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushBool(r1 >= r2);+	}+	break;+      case psOpGt:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushBool(i1 > i2);+	} else {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushBool(r1 > r2);+	}+	break;+      case psOpIdiv:+	i2 = stack->popInt();+	i1 = stack->popInt();+	stack->pushInt(i1 / i2);+	break;+      case psOpIndex:+	stack->index(stack->popInt());+	break;+      case psOpLe:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushBool(i1 <= i2);+	} else {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushBool(r1 <= r2);+	}+	break;+      case psOpLn:+	stack->pushReal(log(stack->popNum()));+	break;+      case psOpLog:+	stack->pushReal(log10(stack->popNum()));+	break;+      case psOpLt:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushBool(i1 < i2);+	} else {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushBool(r1 < r2);+	}+	break;+      case psOpMod:+	i2 = stack->popInt();+	i1 = stack->popInt();+	stack->pushInt(i1 % i2);+	break;+      case psOpMul:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  //~ should check for out-of-range, and push a real instead+	  stack->pushInt(i1 * i2);+	} else {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushReal(r1 * r2);+	}+	break;+      case psOpNe:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushBool(i1 != i2);+	} else if (stack->topTwoAreNums()) {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushBool(r1 != r2);+	} else {+	  b2 = stack->popBool();+	  b1 = stack->popBool();+	  stack->pushBool(b1 != b2);+	}+	break;+      case psOpNeg:+	if (stack->topIsInt()) {+	  stack->pushInt(-stack->popInt());+	} else {+	  stack->pushReal(-stack->popNum());+	}+	break;+      case psOpNot:+	if (stack->topIsInt()) {+	  stack->pushInt(~stack->popInt());+	} else {+	  stack->pushBool(!stack->popBool());+	}+	break;+      case psOpOr:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushInt(i1 | i2);+	} else {+	  b2 = stack->popBool();+	  b1 = stack->popBool();+	  stack->pushBool(b1 || b2);+	}+	break;+      case psOpPop:+	stack->pop();+	break;+      case psOpRoll:+	i2 = stack->popInt();+	i1 = stack->popInt();+	stack->roll(i1, i2);+	break;+      case psOpRound:+	if (!stack->topIsInt()) {+	  r1 = stack->popNum();+	  stack->pushReal((r1 >= 0) ? floor(r1 + 0.5) : ceil(r1 - 0.5));+	}+	break;+      case psOpSin:+	stack->pushReal(sin(stack->popNum()));+	break;+      case psOpSqrt:+	stack->pushReal(sqrt(stack->popNum()));+	break;+      case psOpSub:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushInt(i1 - i2);+	} else {+	  r2 = stack->popNum();+	  r1 = stack->popNum();+	  stack->pushReal(r1 - r2);+	}+	break;+      case psOpTrue:+	stack->pushBool(gTrue);+	break;+      case psOpTruncate:+	if (!stack->topIsInt()) {+	  r1 = stack->popNum();+	  stack->pushReal((r1 >= 0) ? floor(r1) : ceil(r1));+	}+	break;+      case psOpXor:+	if (stack->topTwoAreInts()) {+	  i2 = stack->popInt();+	  i1 = stack->popInt();+	  stack->pushInt(i1 ^ i2);+	} else {+	  b2 = stack->popBool();+	  b1 = stack->popBool();+	  stack->pushBool(b1 ^ b2);+	}+	break;+      case psOpIf:+	b1 = stack->popBool();+	if (b1) {+	  exec(stack, codePtr + 2);+	}+	codePtr = code[codePtr + 1].blk;+	break;+      case psOpIfelse:+	b1 = stack->popBool();+	if (b1) {+	  exec(stack, codePtr + 2);+	} else {+	  exec(stack, code[codePtr].blk);+	}+	codePtr = code[codePtr + 1].blk;+	break;+      case psOpReturn:+	return;+      }+      break;+    default:+      error(-1, "Internal: bad object in PostScript function code");+      break;+    }+  }+}
+ xpdf/Function.h view
@@ -0,0 +1,229 @@+//========================================================================+//+// 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 Dict;+class Stream;+struct PSObject;+class PSStack;++//------------------------------------------------------------------------+// Function+//------------------------------------------------------------------------++#define funcMaxInputs        32+#define funcMaxOutputs       32+#define sampledFuncMaxInputs 16++class Function {+public:++  Function();++  virtual ~Function();++  // Construct a function.  Returns NULL if unsuccessful.+  static Function *parse(Object *funcObj);++  // 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();+  virtual ~IdentityFunction();+  virtual Function *copy() { return new IdentityFunction(); }+  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 idxMul[funcMaxInputs];	// sample array index multipliers+  double *samples;		// the samples+  int nSamples;			// size of the samples array+  double *sBuf;			// buffer for the transform function+  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);+  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(Stream *str, int *codePtr);+  GString *getToken(Stream *str);+  void resizeCode(int newSize);+  void exec(PSStack *stack, int codePtr);++  GString *codeString;+  PSObject *code;+  int codeSize;+  GBool ok;+};++#endif
+ xpdf/GHash.cc view
@@ -0,0 +1,380 @@+//========================================================================+//+// GHash.cc+//+// Copyright 2001-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include "gmem.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;+    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;+    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(char *key) {+  GHashBucket *p;+  int h;++  if (!(p = find(key, &h))) {+    return NULL;+  }+  return p->val.p;+}++int GHash::lookupInt(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(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(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(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) {+  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(char *key) {+  char *p;+  unsigned int h;++  h = 0;+  for (p = key; *p; ++p) {+    h = 17 * h + (int)(*p & 0xff);+  }+  return (int)(h % size);+}
+ xpdf/GHash.h view
@@ -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(char *key);+  int lookupInt(char *key);+  void *remove(GString *key);+  int removeInt(GString *key);+  void *remove(char *key);+  int removeInt(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(char *key, int *h);+  int hash(GString *key);+  int hash(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
+ xpdf/GList.cc view
@@ -0,0 +1,97 @@+//========================================================================+//+// 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 "GList.h"++//------------------------------------------------------------------------+// GList+//------------------------------------------------------------------------++GList::GList() {+  size = 8;+  data = (void **)gmallocn(size, sizeof(void*));+  length = 0;+  inc = 0;+}++GList::GList(int sizeA) {+  size = sizeA;+  data = (void **)gmallocn(size, sizeof(void*));+  length = 0;+  inc = 0;+}++GList::~GList() {+  gfree(data);+}++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 < 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::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*));+}
+ xpdf/GList.h view
@@ -0,0 +1,96 @@+//========================================================================+//+// 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; }++  //----- ordered list support++  // Return the <i>th element.+  // Assumes 0 <= i < length.+  void *get(int i) { return data[i]; }++  // 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));++  //----- 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
+ xpdf/GMutex.h view
@@ -0,0 +1,49 @@+//========================================================================+//+// GMutex.h+//+// Portable mutex macros.+//+// Copyright 2002-2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef GMUTEX_H+#define GMUTEX_H++// Usage:+//+// GMutex m;+// gInitMutex(&m);+// ...+// gLockMutex(&m);+//   ... critical section ...+// gUnlockMutex(&m);+// ...+// gDestroyMutex(&m);++#ifdef WIN32++#include <windows.h>++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++#include <pthread.h>++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++#endif
+ xpdf/GString.cc view
@@ -0,0 +1,718 @@+//========================================================================+//+// 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 "gmem.h"+#include "GString.h"++//------------------------------------------------------------------------++union GStringFormatArg {+  int i;+  Guint ui;+  long l;+  Gulong ul;+  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,+  fmtDouble,+  fmtDoubleTrim,+  fmtChar,+  fmtString,+  fmtGString,+  fmtSpace+};++static char *formatStrings[] = {+  "d", "x", "o", "b", "ud", "ux", "uo", "ub",+  "ld", "lx", "lo", "lb", "uld", "ulx", "ulo", "ulb",+  "f", "g",+  "c",+  "s",+  "t",+  "w",+  NULL+};++//------------------------------------------------------------------------++static inline int size(int len) {+  int delta;+  for (delta = 8; delta < len && delta < 0x100000; delta <<= 1) ;+  // this is ((len + 1) + (delta - 1)) & ~(delta - 1)+  return (len + delta) & ~(delta - 1);+}++inline void GString::resize(int length1) {+  char *s1;++  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 = 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;+  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+  char *p;+  int len;++  formatInt(x, buf, sizeof(buf), gFalse, 0, 10, &p, &len);+  return new GString(p, len);+}++GString *GString::format(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(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) {+  resize(length + 1);+  s[length++] = c;+  s[length] = '\0';+  return this;+}++GString *GString::append(GString *str) {+  int n = str->getLength();++  resize(length + n);+  memcpy(s + length, str->getCString(), n + 1);+  length += n;+  return this;+}++GString *GString::append(const char *str) {+  int n = strlen(str);++  resize(length + n);+  memcpy(s + length, str, n + 1);+  length += n;+  return this;+}++GString *GString::append(const char *str, int lengthA) {+  resize(length + lengthA);+  memcpy(s + length, str, lengthA);+  length += lengthA;+  s[length] = '\0';+  return this;+}++GString *GString::appendf(char *fmt, ...) {+  va_list argList;++  va_start(argList, fmt);+  appendfv(fmt, argList);+  va_end(argList);+  return this;+}++GString *GString::appendfv(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;+  char *p0, *p1, *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 (*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;+	  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;+	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:+	  str = arg.s;+	  len = strlen(str);+	  reverseAlign = !reverseAlign;+	  break;+	case fmtGString:+	  str = arg.gs->getCString();+	  len = arg.gs->getLength();+	  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, p1 - p0);+      p0 = p1;+    }+  }++  gfree(args);+  return this;+}++void GString::formatInt(long x, char *buf, int bufSize,+			GBool zeroFill, int width, int base,+			char **p, int *len) {+  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;+}++void GString::formatUInt(Gulong x, char *buf, int bufSize,+			 GBool zeroFill, int width, int base,+			 char **p, int *len) {+  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, char **p, int *len) {+  GBool neg, started;+  double x2;+  int d, i, j;++  if ((neg = x < 0)) {+    x = -x;+  }+  x = floor(x * pow(10, 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] = '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] = '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;++  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;++  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 = strlen(str);+  int j;++  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;++  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 (n > 0) {+    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]))+      s[i] = toupper(s[i]);+  }+  return this;+}++GString *GString::lowerCase() {+  int i;++  for (i = 0; i < length; ++i) {+    if (isupper(s[i]))+      s[i] = tolower(s[i]);+  }+  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 - *p2;+    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 - *p2;+    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 - *p2;+    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 - *p2;+    if (x != 0) {+      return x;+    }+  }+  if (i == n) {+    return 0;+  }+  if (i < n1) {+    return 1;+  }+  if (*p2) {+    return -1;+  }+  return 0;+}
+ xpdf/GString.h view
@@ -0,0 +1,136 @@+//========================================================================+//+// 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 <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+  //     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(char *fmt, ...);+  static GString *formatv(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(char *fmt, ...);+  GString *appendfv(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);+  static void formatInt(long x, char *buf, int bufSize,+			GBool zeroFill, int width, int base,+			char **p, int *len);+  static void formatUInt(Gulong x, char *buf, int bufSize,+			 GBool zeroFill, int width, int base,+			 char **p, int *len);+  static void formatDouble(double x, char *buf, int bufSize, int prec,+			   GBool trim, char **p, int *len);+};++#endif
+ xpdf/Gfx.cc view
@@ -0,0 +1,4181 @@+//========================================================================+//+// Gfx.cc+//+// Copyright 1996-2003 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 "GlobalParams.h"+#include "CharTypes.h"+#include "Object.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 "Error.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))++// Max number of splits along the t axis for an axial shading fill.+#define axialMaxSplits 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))++//------------------------------------------------------------------------+// 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},+          &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},+          &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) {++    // 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);++  } else {+    fonts = NULL;+    xObjDict.initNull();+    colorSpaceDict.initNull();+    patternDict.initNull();+    shadingDict.initNull();+    gStateDict.initNull();+  }++  next = nextA;+}++GfxResources::~GfxResources() {+  if (fonts) {+    delete fonts;+  }+  xObjDict.free();+  colorSpaceDict.free();+  patternDict.free();+  shadingDict.free();+  gStateDict.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(-1, "Unknown font tag '%s'", name);+  return NULL;+}++GBool GfxResources::lookupXObject(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(-1, "XObject '%s' is unknown", name);+  return gFalse;+}++GBool GfxResources::lookupXObjectNF(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(-1, "XObject '%s' is unknown", name);+  return gFalse;+}++void GfxResources::lookupColorSpace(char *name, Object *obj) {+  GfxResources *resPtr;++  for (resPtr = this; resPtr; resPtr = resPtr->next) {+    if (resPtr->colorSpaceDict.isDict()) {+      if (!resPtr->colorSpaceDict.dictLookup(name, obj)->isNull()) {+	return;+      }+      obj->free();+    }+  }+  obj->initNull();+}++GfxPattern *GfxResources::lookupPattern(char *name) {+  GfxResources *resPtr;+  GfxPattern *pattern;+  Object obj;++  for (resPtr = this; resPtr; resPtr = resPtr->next) {+    if (resPtr->patternDict.isDict()) {+      if (!resPtr->patternDict.dictLookup(name, &obj)->isNull()) {+	pattern = GfxPattern::parse(&obj);+	obj.free();+	return pattern;+      }+      obj.free();+    }+  }+  error(-1, "Unknown pattern '%s'", name);+  return NULL;+}++GfxShading *GfxResources::lookupShading(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(-1, "Unknown shading '%s'", name);+  return NULL;+}++GBool GfxResources::lookupGState(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(-1, "ExtGState '%s' is unknown", name);+  return gFalse;+}++//------------------------------------------------------------------------+// Gfx+//------------------------------------------------------------------------++Gfx::Gfx(XRef *xrefA, OutputDev *outA, int pageNum, Dict *resDict,+	 double hDPI, double vDPI, PDFRectangle *box,+	 PDFRectangle *cropBox, int rotate,+	 GBool (*abortCheckCbkA)(void *data),+	 void *abortCheckCbkDataA) {+  int i;++  xref = xrefA;+  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;+  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(XRef *xrefA, OutputDev *outA, Dict *resDict,+	 PDFRectangle *box, PDFRectangle *cropBox,+	 GBool (*abortCheckCbkA)(void *data),+	 void *abortCheckCbkDataA) {+  int i;++  xref = xrefA;+  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;+  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() {+  while (state->hasSaves()) {+    restoreState();+  }+  if (!subPage) {+    out->endPage();+  }+  while (res) {+    popResources();+  }+  if (state) {+    delete state;+  }+}++void Gfx::display(Object *obj, GBool topLevel) {+  Object obj2;+  int i;++  if (obj->isArray()) {+    for (i = 0; i < obj->arrayGetLength(); ++i) {+      obj->arrayGet(i, &obj2);+      if (!obj2.isStream()) {+	error(-1, "Weird page contents");+	obj2.free();+	return;+      }+      obj2.free();+    }+  } else if (!obj->isStream()) {+    error(-1, "Weird page contents");+    return;+  }+  parser = new Parser(xref, new Lexer(xref, obj), gFalse);+  go(topLevel);+  delete parser;+  parser = NULL;+}++void Gfx::go(GBool topLevel) {+  Object obj;+  Object args[maxArgs];+  int numArgs, i;+  int lastAbortCheck;++  // scan a sequence of objects+  updateLevel = lastAbortCheck = 0;+  numArgs = 0;+  parser->getObj(&obj);+  while (!obj.isEOF()) {++    // 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);+      }+      execOp(&obj, args, numArgs);+      obj.free();+      for (i = 0; i < numArgs; ++i)+	args[i].free();+      numArgs = 0;++      // periodically update display+      if (++updateLevel >= 20000) {+	out->dump();+	updateLevel = 0;+      }++      // check for an abort+      if (abortCheckCbk) {+	if (updateLevel - lastAbortCheck > 10) {+	  if ((*abortCheckCbk)(abortCheckCbkData)) {+	    break;+	  }+	  lastAbortCheck = updateLevel;+	}+      }++    // got an argument - save it+    } else if (numArgs < maxArgs) {+      args[numArgs++] = obj;++    // too many arguments - something is wrong+    } else {+      error(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+    parser->getObj(&obj);+  }+  obj.free();++  // args at end with no command+  if (numArgs > 0) {+    error(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();+  }++  // update display+  if (topLevel && updateLevel > 0) {+    out->dump();+  }+}++void 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)+      error(getPos(), "Unknown operator '%s'", name);+    return;+  }++  // type check args+  argPtr = args;+  if (op->numArgs >= 0) {+    if (numArgs < op->numArgs) {+      error(getPos(), "Too few (%d) args to '%s' operator", numArgs, name);+      return;+    }+    if (numArgs > op->numArgs) {+#if 0+      error(getPos(), "Too many (%d) args to '%s' operator", numArgs, name);+#endif+      argPtr += numArgs - op->numArgs;+      numArgs = op->numArgs;+    }+  } else {+    if (numArgs > -op->numArgs) {+      error(getPos(), "Too many (%d) args to '%s' operator",+	    numArgs, name);+      return;+    }+  }+  for (i = 0; i < numArgs; ++i) {+    if (!checkArg(&argPtr[i], op->tchk[i])) {+      error(getPos(), "Arg #%d to '%s' operator is wrong type (%s)",+	    i, name, argPtr[i].getTypeName());+      return;+    }+  }++  // do it+  (this->*op->func)(argPtr, numArgs);+}++Operator *Gfx::findOp(char *name) {+  int a, b, m, cmp;++  a = -1;+  b = numOps;+  // 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;+}++int 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) {+  state->setLineJoin(args[0].getInt());+  out->updateLineJoin(state);+}++void Gfx::opSetLineCap(Object args[], int numArgs) {+  state->setLineCap(args[0].getInt());+  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, obj4, obj5;+  GfxBlendMode mode;+  GBool haveFillOP;+  Function *funcs[4];+  GfxColor backdropColor;+  GBool haveBackdropColor;+  GfxColorSpace *blendingColorSpace;+  GBool alpha, isolated, knockout;+  int i;++  if (!res->lookupGState(args[0].getName(), &obj1)) {+    return;+  }+  if (!obj1.isDict()) {+    error(getPos(), "ExtGState '%s' is wrong type", args[0].getName());+    obj1.free();+    return;+  }+  if (printCommands) {+    printf("  gfx state dict: ");+    obj1.print();+    printf("\n");+  }++  // 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(getPos(), "Invalid blend mode in ExtGState");+    }+  }+  obj2.free();+  if (obj1.dictLookup("ca", &obj2)->isNum()) {+    state->setFillOpacity(obj2.getNum());+    out->updateFillOpacity(state);+  }+  obj2.free();+  if (obj1.dictLookup("CA", &obj2)->isNum()) {+    state->setStrokeOpacity(obj2.getNum());+    out->updateStrokeOpacity(state);+  }+  obj2.free();++  // fill/stroke overprint+  if ((haveFillOP = (obj1.dictLookup("op", &obj2)->isBool()))) {+    state->setFillOverprint(obj2.getBool());+    out->updateFillOverprint(state);+  }+  obj2.free();+  if (obj1.dictLookup("OP", &obj2)->isBool()) {+    state->setStrokeOverprint(obj2.getBool());+    out->updateStrokeOverprint(state);+    if (!haveFillOP) {+      state->setFillOverprint(obj2.getBool());+      out->updateFillOverprint(state);+    }+  }+  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.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);+      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))) {+      funcs[1] = funcs[2] = funcs[3] = NULL;+      state->setTransfer(funcs);+      out->updateTransfer(state);+    }+  } else if (!obj2.isNull()) {+    error(getPos(), "Invalid transfer function in ExtGState");+  }+  obj2.free();++  // soft mask+  if (!obj1.dictLookup("SMask", &obj2)->isNull()) {+    if (obj2.isName("None")) {+      out->clearSoftMask(state);+    } else if (obj2.isDict()) {+      if (obj2.dictLookup("S", &obj3)->isName("Alpha")) {+	alpha = gTrue;+      } else { // "Luminosity"+	alpha = gFalse;+      }+      obj3.free();+      funcs[0] = NULL;+      if (!obj2.dictLookup("TR", &obj3)->isNull()) {+	funcs[0] = Function::parse(&obj3);+	if (funcs[0]->getInputSize() != 1 ||+	    funcs[0]->getOutputSize() != 1) {+	  error(getPos(),+		"Invalid transfer function in soft mask in ExtGState");+	  delete funcs[0];+	  funcs[0] = NULL;+	}+      }+      obj3.free();+      if ((haveBackdropColor = obj2.dictLookup("BC", &obj3)->isArray())) {+	for (i = 0; i < gfxColorMaxComps; ++i) {+	  backdropColor.c[i] = 0;+	}+	for (i = 0; i < obj3.arrayGetLength() && i < gfxColorMaxComps; ++i) {+	  obj3.arrayGet(i, &obj4);+	  if (obj4.isNum()) {+	    backdropColor.c[i] = dblToCol(obj4.getNum());+	  }+	  obj4.free();+	}+      }+      obj3.free();+      if (obj2.dictLookup("G", &obj3)->isStream()) {+	if (obj3.streamGetDict()->lookup("Group", &obj4)->isDict()) {+	  blendingColorSpace = NULL;+	  isolated = knockout = gFalse;+	  if (!obj4.dictLookup("CS", &obj5)->isNull()) {+	    blendingColorSpace = GfxColorSpace::parse(&obj5);+	  }+	  obj5.free();+	  if (obj4.dictLookup("I", &obj5)->isBool()) {+	    isolated = obj5.getBool();+	  }+	  obj5.free();+	  if (obj4.dictLookup("K", &obj5)->isBool()) {+	    knockout = obj5.getBool();+	  }+	  obj5.free();+	  if (!haveBackdropColor) {+	    if (blendingColorSpace) {+	      blendingColorSpace->getDefaultColor(&backdropColor);+	    } else {+	      //~ need to get the parent or default color space (?)+	      for (i = 0; i < gfxColorMaxComps; ++i) {+		backdropColor.c[i] = 0;+	      }+	    }+	  }+	  doSoftMask(&obj3, alpha, blendingColorSpace,+		     isolated, knockout, funcs[0], &backdropColor);+	  if (funcs[0]) {+	    delete funcs[0];+	  }+	} else {+	  error(getPos(), "Invalid soft mask in ExtGState - missing group");+	}+	obj4.free();+      } else {+	error(getPos(), "Invalid soft mask in ExtGState - missing group");+      }+      obj3.free();+    } else if (!obj2.isNull()) {+      error(getPos(), "Invalid soft mask in ExtGState");+    }+  }+  obj2.free();++  obj1.free();+}++void Gfx::doSoftMask(Object *str, GBool alpha,+		     GfxColorSpace *blendingColorSpace,+		     GBool isolated, GBool knockout,+		     Function *transferFunc, GfxColor *backdropColor) {+  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(getPos(), "Unknown form type");+  }+  obj1.free();++  // get bounding box+  dict->lookup("BBox", &obj1);+  if (!obj1.isArray()) {+    obj1.free();+    error(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;+  doForm1(str, resDict, m, bbox, gTrue, gTrue,+	  blendingColorSpace, isolated, knockout,+	  alpha, transferFunc, backdropColor);+  --formDepth;++  if (blendingColorSpace) {+    delete blendingColorSpace;+  }+  obj1.free();+}++void Gfx::opSetRenderingIntent(Object args[], int numArgs) {+}++//------------------------------------------------------------------------+// color operators+//------------------------------------------------------------------------++void Gfx::opSetFillGray(Object args[], int numArgs) {+  GfxColor color;++  state->setFillPattern(NULL);+  state->setFillColorSpace(new GfxDeviceGrayColorSpace());+  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;++  state->setStrokePattern(NULL);+  state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());+  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;++  state->setFillPattern(NULL);+  state->setFillColorSpace(new GfxDeviceCMYKColorSpace());+  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;++  state->setStrokePattern(NULL);+  state->setStrokeColorSpace(new GfxDeviceCMYKColorSpace());+  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;++  state->setFillPattern(NULL);+  state->setFillColorSpace(new GfxDeviceRGBColorSpace());+  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;++  state->setStrokePattern(NULL);+  state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());+  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;++  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(getPos(), "Bad color space (fill)");+  }+}++void Gfx::opSetStrokeColorSpace(Object args[], int numArgs) {+  Object obj;+  GfxColorSpace *colorSpace;+  GfxColor color;++  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(getPos(), "Bad color space (stroke)");+  }+}++void Gfx::opSetFillColor(Object args[], int numArgs) {+  GfxColor color;+  int i;++  if (numArgs != state->getFillColorSpace()->getNComps()) {+    error(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);+}++void Gfx::opSetStrokeColor(Object args[], int numArgs) {+  GfxColor color;+  int i;++  if (numArgs != state->getStrokeColorSpace()->getNComps()) {+    error(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->getFillColorSpace()->getMode() == csPattern) {+    if (numArgs > 1) {+      if (!((GfxPatternColorSpace *)state->getFillColorSpace())->getUnder() ||+	  numArgs - 1 != ((GfxPatternColorSpace *)state->getFillColorSpace())+	                     ->getUnder()->getNComps()) {+	error(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 (args[numArgs-1].isName() &&+	(pattern = res->lookupPattern(args[numArgs-1].getName()))) {+      state->setFillPattern(pattern);+    }++  } else {+    if (numArgs != state->getFillColorSpace()->getNComps()) {+      error(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->getStrokeColorSpace()->getMode() == csPattern) {+    if (numArgs > 1) {+      if (!((GfxPatternColorSpace *)state->getStrokeColorSpace())+	       ->getUnder() ||+	  numArgs - 1 != ((GfxPatternColorSpace *)state->getStrokeColorSpace())+	                     ->getUnder()->getNComps()) {+	error(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 (args[numArgs-1].isName() &&+	(pattern = res->lookupPattern(args[numArgs-1].getName()))) {+      state->setStrokePattern(pattern);+    }++  } else {+    if (numArgs != state->getStrokeColorSpace()->getNComps()) {+      error(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(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(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(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(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(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(getPos(), "No path in stroke");+    return;+  }+  if (state->isPath()) {+    if (state->getStrokeColorSpace()->getMode() == csPattern) {+      doPatternStroke();+    } else {+      out->stroke(state);+    }+  }+  doEndPath();+}++void Gfx::opCloseStroke(Object args[], int numArgs) {+  if (!state->isCurPt()) {+    //error(getPos(), "No path in closepath/stroke");+    return;+  }+  if (state->isPath()) {+    state->closePath();+    if (state->getStrokeColorSpace()->getMode() == csPattern) {+      doPatternStroke();+    } else {+      out->stroke(state);+    }+  }+  doEndPath();+}++void Gfx::opFill(Object args[], int numArgs) {+  if (!state->isCurPt()) {+    //error(getPos(), "No path in fill");+    return;+  }+  if (state->isPath()) {+    if (state->getFillColorSpace()->getMode() == csPattern) {+      doPatternFill(gFalse);+    } else {+      out->fill(state);+    }+  }+  doEndPath();+}++void Gfx::opEOFill(Object args[], int numArgs) {+  if (!state->isCurPt()) {+    //error(getPos(), "No path in eofill");+    return;+  }+  if (state->isPath()) {+    if (state->getFillColorSpace()->getMode() == csPattern) {+      doPatternFill(gTrue);+    } else {+      out->eoFill(state);+    }+  }+  doEndPath();+}++void Gfx::opFillStroke(Object args[], int numArgs) {+  if (!state->isCurPt()) {+    //error(getPos(), "No path in fill/stroke");+    return;+  }+  if (state->isPath()) {+    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(getPos(), "No path in closepath/fill/stroke");+    return;+  }+  if (state->isPath()) {+    state->closePath();+    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(getPos(), "No path in eofill/stroke");+    return;+  }+  if (state->isPath()) {+    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(getPos(), "No path in closepath/eofill/stroke");+    return;+  }+  if (state->isPath()) {+    state->closePath();+    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);+    break;+  case 2:+    doShadingPatternFill((GfxShadingPattern *)pattern, gFalse, eoFill);+    break;+  default:+    error(getPos(), "Unimplemented pattern type (%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);+    break;+  case 2:+    doShadingPatternFill((GfxShadingPattern *)pattern, gTrue, gFalse);+    break;+  default:+    error(getPos(), "Unimplemented pattern type (%d) in stroke",+	  pattern->getType());+    break;+  }+}++void Gfx::doTilingPatternFill(GfxTilingPattern *tPat,+			      GBool stroke, GBool eoFill) {+  GfxPatternColorSpace *patCS;+  GfxColorSpace *cs;+  GfxPath *savedPath;+  double xMin, yMin, xMax, yMax, x, y, x1, y1;+  double cxMin, cyMin, cxMax, cyMax;+  int xi0, yi0, xi1, yi1, xi, yi;+  double *ctm, *btm, *ptm;+  double m[6], ictm[6], m1[6], imb[6];+  double det;+  double xstep, ystep;+  int 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 = 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;+  // 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 = 1 / (m1[0] * m1[3] - m1[1] * m1[2]);+  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+  savedPath = state->getPath()->copy();+  saveState();++  // set underlying color space (for uncolored tiling patterns); set+  // various other parameters (stroke color, line width) to match+  // Adobe's behavior+  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());+  } else {+    state->setFillColorSpace(new GfxDeviceGrayColorSpace());+    out->updateFillColorSpace(state);+    state->setStrokeColorSpace(new GfxDeviceGrayColorSpace());+    out->updateStrokeColorSpace(state);+  }+  state->setFillPattern(NULL);+  out->updateFillColor(state);+  state->setStrokePattern(NULL);+  out->updateStrokeColor(state);+  if (!stroke) {+    state->setLineWidth(0);+    out->updateLineWidth(state);+  }++  // clip to current path+  if (stroke) {+    state->clipToStrokePath();+    out->clipToStrokePath(state);+  } else {+    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 (?)+  xstep = fabs(tPat->getXStep());+  ystep = fabs(tPat->getYStep());+  xi0 = (int)ceil((xMin - tPat->getBBox()[2]) / xstep);+  xi1 = (int)floor((xMax - tPat->getBBox()[0]) / xstep) + 1;+  yi0 = (int)ceil((yMin - tPat->getBBox()[3]) / ystep);+  yi1 = (int)floor((yMax - tPat->getBBox()[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, tPat->getContentStream(),+			   tPat->getPaintType(), tPat->getResDict(),+			   m1, tPat->getBBox(),+			   xi0, yi0, xi1, yi1, xstep, ystep);+  } else {+    for (yi = yi0; yi < yi1; ++yi) {+      for (xi = xi0; xi < xi1; ++xi) {+	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];+	doForm1(tPat->getContentStream(), tPat->getResDict(),+		m1, tPat->getBBox());+      }+    }+  }++  // restore graphics state+ err:+  restoreState();+  state->setPath(savedPath);+}++void Gfx::doShadingPatternFill(GfxShadingPattern *sPat,+			       GBool stroke, GBool eoFill) {+  GfxShading *shading;+  GfxPath *savedPath;+  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+  savedPath = state->getPath()->copy();+  saveState();++  // 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->setPath(savedPath->copy());+  }++  // clip to current path+  if (stroke) {+    state->clipToStrokePath();+    out->clipToStrokePath(state);+  } else {+    state->clip();+    if (eoFill) {+      out->eoClip(state);+    } else {+      out->clip(state);+    }+  }++  // 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);+    out->fill(state);+  }+  state->clearPath();++  // construct a (pattern space) -> (current space) transform matrix+  ctm = state->getCTM();+  btm = baseMatrix;+  ptm = sPat->getMatrix();+  // iCTM = invert 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;+  // 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]);++#if 1 //~tmp: turn off anti-aliasing temporarily+  GBool vaa = out->getVectorAntialias();+  if (vaa) {+    out->setVectorAntialias(gFalse);+  }+#endif++  // do shading type-specific operations+  switch (shading->getType()) {+  case 1:+    doFunctionShFill((GfxFunctionShading *)shading);+    break;+  case 2:+    doAxialShFill((GfxAxialShading *)shading);+    break;+  case 3:+    doRadialShFill((GfxRadialShading *)shading);+    break;+  case 4:+  case 5:+    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);+    break;+  case 6:+  case 7:+    doPatchMeshShFill((GfxPatchMeshShading *)shading);+    break;+  }++#if 1 //~tmp: turn off anti-aliasing temporarily+  if (vaa) {+    out->setVectorAntialias(gTrue);+  }+#endif++  // restore graphics state+  restoreState();+  state->setPath(savedPath);+}++void Gfx::opShFill(Object args[], int numArgs) {+  GfxShading *shading;+  GfxPath *savedPath;+  double xMin, yMin, xMax, yMax;++  if (!(shading = res->lookupShading(args[0].getName()))) {+    return;+  }++  // save current graphics state+  savedPath = state->getPath()->copy();+  saveState();++  // 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);++#if 1 //~tmp: turn off anti-aliasing temporarily+  GBool vaa = out->getVectorAntialias();+  if (vaa) {+    out->setVectorAntialias(gFalse);+  }+#endif++  // do shading type-specific operations+  switch (shading->getType()) {+  case 1:+    doFunctionShFill((GfxFunctionShading *)shading);+    break;+  case 2:+    doAxialShFill((GfxAxialShading *)shading);+    break;+  case 3:+    doRadialShFill((GfxRadialShading *)shading);+    break;+  case 4:+  case 5:+    doGouraudTriangleShFill((GfxGouraudTriangleShading *)shading);+    break;+  case 6:+  case 7:+    doPatchMeshShFill((GfxPatchMeshShading *)shading);+    break;+  }++#if 1 //~tmp: turn off anti-aliasing temporarily+  if (vaa) {+    out->setVectorAntialias(gTrue);+  }+#endif++  // restore graphics state+  restoreState();+  state->setPath(savedPath);++  delete shading;+}++void Gfx::doFunctionShFill(GfxFunctionShading *shading) {+  double x0, y0, x1, y1;+  GfxColor colors[4];++  if (out->useShadedFills() &&+      out->functionShadedFill(state, shading)) {+    return;+  }++  shading->getDomain(&x0, &y0, &x1, &y1);+  shading->getColor(x0, y0, &colors[0]);+  shading->getColor(x0, y1, &colors[1]);+  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 dxZero, dyZero;+  double tMin, tMax, t, tx, ty;+  double s[4], sMin, sMax, tmp;+  double ux0, uy0, ux1, uy1, vx0, vy0, vx1, vy1;+  double t0, t1, tt;+  double ta[axialMaxSplits + 1];+  int next[axialMaxSplits + 1];+  GfxColor color0, color1;+  int nComps;+  int i, j, k, kk;++  if (out->useShadedFills() &&+      out->axialShadedFill(state, shading)) {+    return;+  }++  // 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;+  dxZero = fabs(dx) < 0.01;+  dyZero = fabs(dy) < 0.01;+  if (dxZero && dyZero) {+    tMin = tMax = 0;+  } 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();++  // 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).  In the general case, there are four+  // intersection points:+  //+  //     s0 = (xMin - tx) / -dy+  //     s1 = (xMax - tx) / -dy+  //     s2 = (yMin - ty) / dx+  //     s3 = (yMax - ty) / dx+  //+  // and we want the middle two s values.+  //+  // In the case where dx = 0, take s0 and s1; in the case where dy =+  // 0, take s2 and s3.+  //+  // 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.++  // set up: require at least one split to avoid problems when the two+  // ends of the t axis have the same color+  nComps = shading->getColorSpace()->getNComps();+  ta[0] = tMin;+  next[0] = axialMaxSplits / 2;+  ta[axialMaxSplits / 2] = 0.5 * (tMin + tMax);+  next[axialMaxSplits / 2] = axialMaxSplits;+  ta[axialMaxSplits] = tMax;++  // compute the color at t = tMin+  if (tMin < 0) {+    tt = t0;+  } else if (tMin > 1) {+    tt = t1;+  } else {+    tt = t0 + (t1 - t0) * tMin;+  }+  shading->getColor(tt, &color0);++  // 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 (dxZero && dyZero) {+    sMin = sMax = 0;+  } else if (dxZero) {+    sMin = (xMin - tx) / -dy;+    sMax = (xMax - tx) / -dy;+    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }+  } else if (dyZero) {+    sMin = (yMin - ty) / dx;+    sMax = (yMax - ty) / dx;+    if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }+  } else {+    s[0] = (yMin - ty) / dx;+    s[1] = (yMax - ty) / dx;+    s[2] = (xMin - tx) / -dy;+    s[3] = (xMax - tx) / -dy;+    for (j = 0; j < 3; ++j) {+      kk = j;+      for (k = j + 1; k < 4; ++k) {+	if (s[k] < s[kk]) {+	  kk = k;+	}+      }+      tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;+    }+    sMin = s[1];+    sMax = s[2];+  }+  ux0 = tx - sMin * dy;+  uy0 = ty + sMin * dx;+  vx0 = tx - sMax * dy;+  vy0 = ty + sMax * dx;++  i = 0;+  while (i < axialMaxSplits) {++    // bisect until color difference is small enough or we hit the+    // bisection limit+    j = next[i];+    while (j > i + 1) {+      if (ta[j] < 0) {+	tt = t0;+      } else if (ta[j] > 1) {+	tt = t1;+      } else {+	tt = t0 + (t1 - t0) * ta[j];+      }+      shading->getColor(tt, &color1);+      for (k = 0; k < nComps; ++k) {+	if (abs(color1.c[k] - color0.c[k]) > axialColorDelta) {+	  break;+	}+      }+      if (k == nComps) {+	break;+      }+      k = (i + j) / 2;+      ta[k] = 0.5 * (ta[i] + ta[j]);+      next[i] = k;+      next[k] = j;+      j = k;+    }++    // use the average of the colors of the two sides of the region+    for (k = 0; k < nComps; ++k) {+      color0.c[k] = (color0.c[k] + color1.c[k]) / 2;+    }++    // compute the coordinates of the point on the t axis; then+    // compute the intersection of the perpendicular line with the+    // bounding box+    tx = x0 + ta[j] * dx;+    ty = y0 + ta[j] * dy;+    if (dxZero && dyZero) {+      sMin = sMax = 0;+    } else if (dxZero) {+      sMin = (xMin - tx) / -dy;+      sMax = (xMax - tx) / -dy;+      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }+    } else if (dyZero) {+      sMin = (yMin - ty) / dx;+      sMax = (yMax - ty) / dx;+      if (sMin > sMax) { tmp = sMin; sMin = sMax; sMax = tmp; }+    } else {+      s[0] = (yMin - ty) / dx;+      s[1] = (yMax - ty) / dx;+      s[2] = (xMin - tx) / -dy;+      s[3] = (xMax - tx) / -dy;+      for (j = 0; j < 3; ++j) {+	kk = j;+	for (k = j + 1; k < 4; ++k) {+	  if (s[k] < s[kk]) {+	    kk = k;+	  }+	}+	tmp = s[j]; s[j] = s[kk]; s[kk] = tmp;+      }+      sMin = s[1];+      sMax = s[2];+    }+    ux1 = tx - sMin * dy;+    uy1 = ty + sMin * dx;+    vx1 = tx - sMax * dy;+    vy1 = ty + sMax * dx;++    // set the color+    state->setFillColor(&color0);+    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;+    color0 = color1;+    i = next[i];+  }+}++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 sz, xz, yz, sMin, sMax;+  GBool enclosed;+  int ia, ib, k, n;+  double *ctm;+  double theta, alpha, angle, t;++  if (out->useShadedFills() &&+      out->radialShadedFill(state, shading)) {+    return;+  }++  // 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.+  if (x0 == x1 && y0 == y1) {+    enclosed = gTrue;+    theta = 0; // make gcc happy+    sz = 0; // make gcc happy+  } else if (r0 == r1) {+    enclosed = gFalse;+    theta = 0;+    sz = 0; // make gcc happy+  } else {+    sz = -r0 / (r1 - r0);+    xz = x0 + sz * (x1 - x0);+    yz = y0 + sz * (y1 - y0);+    enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;+    theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));+    if (r0 > r1) {+      theta = -theta;+    }+  }+  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 {+    sMin = 1;+    sMax = 0;+    // solve for x(s) + r(s) = xMin+    if ((x1 + r1) - (x0 + r0) != 0) {+      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // solve for x(s) - r(s) = xMax+    if ((x1 - r1) - (x0 - r0) != 0) {+      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // solve for y(s) + r(s) = yMin+    if ((y1 + r1) - (y0 + r0) != 0) {+      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // solve for y(s) - r(s) = yMax+    if ((y1 - r1) - (y0 - r0) != 0) {+      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // check against sz+    if (r0 < r1) {+      if (sMin < sz) {+	sMin = sz;+      }+    } else if (r0 > r1) {+      if (sMax > sz) {+	sMax = sz;+      }+    }+    // check the 'extend' flags+    if (!shading->getExtend0() && sMin < 0) {+      sMin = 0;+    }+    if (!shading->getExtend1() && sMax > 1) {+      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+  while (ia < radialMaxSplits) {++    // 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();+    }+  }+}++void Gfx::doGouraudTriangleShFill(GfxGouraudTriangleShading *shading) {+  double x0, y0, x1, y1, x2, y2;+  GfxColor color0, color1, color2;+  int i;++  for (i = 0; i < shading->getNTriangles(); ++i) {+    shading->getTriangle(i, &x0, &y0, &color0,+			 &x1, &y1, &color1,+			 &x2, &y2, &color2);+    gouraudFillTriangle(x0, y0, &color0, x1, y1, &color1, x2, y2, &color2,+			shading->getColorSpace()->getNComps(), 0);+  }+}++void Gfx::gouraudFillTriangle(double x0, double y0, GfxColor *color0,+			      double x1, double y1, GfxColor *color1,+			      double x2, double y2, GfxColor *color2,+			      int nComps, int depth) {+  double x01, y01, x12, y12, x20, y20;+  GfxColor color01, color12, color20;+  int i;++  for (i = 0; i < nComps; ++i) {+    if (abs(color0->c[i] - color1->c[i]) > gouraudColorDelta ||+	abs(color1->c[i] - color2->c[i]) > gouraudColorDelta) {+      break;+    }+  }+  if (i == nComps || depth == gouraudMaxDepth) {+    state->setFillColor(color0);+    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);+    //~ if the shading has a Function, this should interpolate on the+    //~ function parameter, not on the color components+    for (i = 0; i < nComps; ++i) {+      color01.c[i] = (color0->c[i] + color1->c[i]) / 2;+      color12.c[i] = (color1->c[i] + color2->c[i]) / 2;+      color20.c[i] = (color2->c[i] + color0->c[i]) / 2;+    }+    gouraudFillTriangle(x0, y0, color0, x01, y01, &color01,+			x20, y20, &color20, nComps, depth + 1);+    gouraudFillTriangle(x01, y01, &color01, x1, y1, color1,+			x12, y12, &color12, nComps, depth + 1);+    gouraudFillTriangle(x01, y01, &color01, x12, y12, &color12,+			x20, y20, &color20, nComps, depth + 1);+    gouraudFillTriangle(x20, y20, &color20, x12, y12, &color12,+			x2, y2, color2, nComps, depth + 1);+  }+}++void Gfx::doPatchMeshShFill(GfxPatchMeshShading *shading) {+  int start, i;++  if (shading->getNPatches() > 128) {+    start = 3;+  } else if (shading->getNPatches() > 64) {+    start = 2;+  } else if (shading->getNPatches() > 16) {+    start = 1;+  } else {+    start = 0;+  }+  for (i = 0; i < shading->getNPatches(); ++i) {+    fillPatch(shading->getPatch(i), shading->getColorSpace()->getNComps(),+	      start);+  }+}++void Gfx::fillPatch(GfxPatch *patch, int nComps, int depth) {+  GfxPatch patch00, patch01, patch10, patch11;+  double xx[4][8], yy[4][8];+  double xxm, yym;+  int i;++  for (i = 0; i < nComps; ++i) {+    if (abs(patch->color[0][0].c[i] - patch->color[0][1].c[i])+	  > patchColorDelta ||+	abs(patch->color[0][1].c[i] - patch->color[1][1].c[i])+	  > patchColorDelta ||+	abs(patch->color[1][1].c[i] - patch->color[1][0].c[i])+	  > patchColorDelta ||+	abs(patch->color[1][0].c[i] - patch->color[0][0].c[i])+	  > patchColorDelta) {+      break;+    }+  }+  if (i == nComps || depth == patchMaxDepth) {+    state->setFillColor(&patch->color[0][0]);+    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();+  } 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];+    }+    //~ if the shading has a Function, this should interpolate on the+    //~ function parameter, not on the color components+    for (i = 0; i < nComps; ++i) {+      patch00.color[0][0].c[i] = patch->color[0][0].c[i];+      patch00.color[0][1].c[i] = (patch->color[0][0].c[i] ++				  patch->color[0][1].c[i]) / 2;+      patch01.color[0][0].c[i] = patch00.color[0][1].c[i];+      patch01.color[0][1].c[i] = patch->color[0][1].c[i];+      patch01.color[1][1].c[i] = (patch->color[0][1].c[i] ++				  patch->color[1][1].c[i]) / 2;+      patch11.color[0][1].c[i] = patch01.color[1][1].c[i];+      patch11.color[1][1].c[i] = patch->color[1][1].c[i];+      patch11.color[1][0].c[i] = (patch->color[1][1].c[i] ++				  patch->color[1][0].c[i]) / 2;+      patch10.color[1][1].c[i] = patch11.color[1][0].c[i];+      patch10.color[1][0].c[i] = patch->color[1][0].c[i];+      patch10.color[0][0].c[i] = (patch->color[1][0].c[i] ++				  patch->color[0][0].c[i]) / 2;+      patch00.color[1][0].c[i] = patch10.color[0][0].c[i];+      patch00.color[1][1].c[i] = (patch00.color[1][0].c[i] ++				  patch01.color[1][1].c[i]) / 2;+      patch01.color[1][0].c[i] = patch00.color[1][1].c[i];+      patch11.color[0][0].c[i] = patch00.color[1][1].c[i];+      patch10.color[0][1].c[i] = patch00.color[1][1].c[i];+    }+    fillPatch(&patch00, nComps, depth + 1);+    fillPatch(&patch10, nComps, depth + 1);+    fillPatch(&patch01, nComps, depth + 1);+    fillPatch(&patch11, nComps, 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) {+  GfxFont *font;++  if (!(font = res->lookupFont(args[0].getName()))) {+    return;+  }+  if (printCommands) {+    printf("  font: tag=%s name='%s' %g\n",+	   font->getTag()->getCString(),+	   font->getName() ? font->getName()->getCString() : "???",+	   args[1].getNum());+    fflush(stdout);+  }+  state->setFont(font, args[1].getNum());+  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(getPos(), "No font in show");+    return;+  }+  if (fontChanged) {+    out->updateFont(state);+    fontChanged = gFalse;+  }+  out->beginStringOp(state);+  doShowText(args[0].getString());+  out->endStringOp(state);+}++void Gfx::opMoveShowText(Object args[], int numArgs) {+  double tx, ty;++  if (!state->getFont()) {+    error(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);+  out->beginStringOp(state);+  doShowText(args[0].getString());+  out->endStringOp(state);+}++void Gfx::opMoveSetShowText(Object args[], int numArgs) {+  double tx, ty;++  if (!state->getFont()) {+    error(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);+  out->beginStringOp(state);+  doShowText(args[2].getString());+  out->endStringOp(state);+}++void Gfx::opShowSpaceText(Object args[], int numArgs) {+  Array *a;+  Object obj;+  int wMode;+  int i;++  if (!state->getFont()) {+    error(getPos(), "No font in show/space");+    return;+  }+  if (fontChanged) {+    out->updateFont(state);+    fontChanged = gFalse;+  }+  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()) {+      // this uses the absolute value of the font size to match+      // Acrobat's behavior+      if (wMode) {+	state->textShift(0, -obj.getNum() * 0.001 *+			    fabs(state->getFontSize()));+      } else {+	state->textShift(-obj.getNum() * 0.001 *+			 fabs(state->getFontSize()), 0);+      }+      out->updateTextShift(state, obj.getNum());+    } else if (obj.isString()) {+      doShowText(obj.getString());+    } else {+      error(getPos(), "Element of show/space array must be number or string");+    }+    obj.free();+  }+  out->endStringOp(state);+}++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, lineX, lineY;+  double originX, originY, tOriginX, tOriginY;+  double oldCTM[6], newCTM[6];+  double *mat;+  Object charProc;+  Dict *resDict;+  Parser *oldParser;+  char *p;+  int len, n, uLen, nChars, nSpaces, i;++  font = state->getFont();+  wMode = font->getWMode();++  if (out->useDrawChar()) {+    out->beginString(state, s);+  }++  // 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();+    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);+    curX = state->getCurX();+    curY = state->getCurY();+    lineX = state->getLineX();+    lineY = state->getLineY();+    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);+      saveState();+      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);+      if (!out->beginType3Char(state, curX + riseX, curY + riseY, tdx, tdy,+			       code, u, uLen)) {+	((Gfx8BitFont *)font)->getCharProc(code, &charProc);+	if ((resDict = ((Gfx8BitFont *)font)->getResources())) {+	  pushResources(resDict);+	}+	if (charProc.isStream()) {+	  display(&charProc, gFalse);+	} else {+	  error(getPos(), "Missing or bad Type3 CharProc entry");+	}+	out->endType3Char(state);+	if (resDict) {+	  popResources();+	}+	charProc.free();+      }+      restoreState();+      // GfxState::restore() does *not* restore the current position,+      // so we deal with it here using (curX, curY) and (lineX, lineY)+      curX += tdx;+      curY += tdy;+      state->moveTo(curX, curY);+      state->textSetPos(lineX, lineY);+      p += n;+      len -= n;+    }+    parser = oldParser;++  } else if (out->useDrawChar()) {+    state->textTransformDelta(0, state->getRise(), &riseX, &riseY);+    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);+  }++  updateLevel += 10 * s->getLength();+}++//------------------------------------------------------------------------+// XObject operators+//------------------------------------------------------------------------++void Gfx::opXObject(Object args[], int numArgs) {+  char *name;+  Object obj1, obj2, obj3, refObj;+#if OPI_SUPPORT+  Object opiDict;+#endif++  name = args[0].getName();+  if (!res->lookupXObject(name, &obj1)) {+    return;+  }+  if (!obj1.isStream()) {+    error(getPos(), "XObject '%s' is wrong type", name);+    obj1.free();+    return;+  }+#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()) {+      out->drawForm(refObj.getRef());+    } else {+      doForm(&obj1);+    }+    refObj.free();+  } else if (obj2.isName("PS")) {+    obj1.streamGetDict()->lookup("Level1", &obj3);+    out->psXObject(obj1.getStream(),+		   obj3.isStream() ? obj3.getStream() : (Stream *)NULL);+  } else if (obj2.isName()) {+    error(getPos(), "Unknown XObject subtype '%s'", obj2.getName());+  } else {+    error(getPos(), "XObject subtype is missing or wrong type");+  }+  obj2.free();+#if OPI_SUPPORT+  if (opiDict.isDict()) {+    out->opiEnd(state, opiDict.getDict());+  }+  opiDict.free();+#endif+  obj1.free();+}++void Gfx::doImage(Object *ref, Stream *str, GBool inlineImg) {+  Dict *dict, *maskDict;+  int width, height;+  int bits, maskBits;+  StreamColorSpaceMode csMode;+  GBool mask;+  GBool invert;+  GfxColorSpace *colorSpace, *maskColorSpace;+  GfxImageColorMap *colorMap, *maskColorMap;+  Object maskObj, smaskObj;+  GBool haveColorKeyMask, haveExplicitMask, haveSoftMask;+  int maskColors[2*gfxColorMaxComps];+  int maskWidth, maskHeight;+  GBool maskInvert;+  Stream *maskStr;+  Object obj1, obj2;+  int i;++  // get info from the stream+  bits = 0;+  csMode = streamCSNone;+  str->getImageParams(&bits, &csMode);++  // get stream dict+  dict = str->getDict();++  // 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();+  dict->lookup("Height", &obj1);+  if (obj1.isNull()) {+    obj1.free();+    dict->lookup("H", &obj1);+  }+  if (!obj1.isInt())+    goto err2;+  height = obj1.getInt();+  obj1.free();++  // 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();+    } else if (mask) {+      bits = 1;+    } else {+      goto err2;+    }+    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);+      if (obj2.isInt() && obj2.getInt() == 1)+	invert = gTrue;+      obj2.free();+    } else if (!obj1.isNull()) {+      goto err2;+    }+    obj1.free();++    // draw it+    out->drawImageMask(state, ref, str, width, height, invert, inlineImg);++  } else {++    // 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 = new GfxDeviceGrayColorSpace();+    } else if (csMode == streamCSDeviceRGB) {+      colorSpace = new GfxDeviceRGBColorSpace();+    } else if (csMode == streamCSDeviceCMYK) {+      colorSpace = new GfxDeviceCMYKColorSpace();+    } else {+      colorSpace = NULL;+    }+    obj1.free();+    if (!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 = 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) {+	goto err1;+      }+      maskStr = smaskObj.getStream();+      maskDict = smaskObj.streamGetDict();+      maskDict->lookup("Width", &obj1);+      if (obj1.isNull()) {+	obj1.free();+	maskDict->lookup("W", &obj1);+      }+      if (!obj1.isInt()) {+	goto err2;+      }+      maskWidth = obj1.getInt();+      obj1.free();+      maskDict->lookup("Height", &obj1);+      if (obj1.isNull()) {+	obj1.free();+	maskDict->lookup("H", &obj1);+      }+      if (!obj1.isInt()) {+	goto err2;+      }+      maskHeight = obj1.getInt();+      obj1.free();+      maskDict->lookup("BitsPerComponent", &obj1);+      if (obj1.isNull()) {+	obj1.free();+	maskDict->lookup("BPC", &obj1);+      }+      if (!obj1.isInt()) {+	goto err2;+      }+      maskBits = obj1.getInt();+      obj1.free();+      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();+	}+      }+      maskColorSpace = GfxColorSpace::parse(&obj1);+      obj1.free();+      if (!maskColorSpace || maskColorSpace->getMode() != csDeviceGray) {+	goto err1;+      }+      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;+	goto err1;+      }+      //~ handle the Matte entry+      haveSoftMask = gTrue;+    } else if (maskObj.isArray()) {+      // color key mask+      for (i = 0;+	   i < maskObj.arrayGetLength() && i < 2*gfxColorMaxComps;+	   ++i) {+	maskObj.arrayGet(i, &obj1);+	maskColors[i] = obj1.getInt();+	obj1.free();+      }+      haveColorKeyMask = gTrue;+    } else if (maskObj.isStream()) {+      // explicit mask+      if (inlineImg) {+	goto err1;+      }+      maskStr = maskObj.getStream();+      maskDict = maskObj.streamGetDict();+      maskDict->lookup("Width", &obj1);+      if (obj1.isNull()) {+	obj1.free();+	maskDict->lookup("W", &obj1);+      }+      if (!obj1.isInt()) {+	goto err2;+      }+      maskWidth = obj1.getInt();+      obj1.free();+      maskDict->lookup("Height", &obj1);+      if (obj1.isNull()) {+	obj1.free();+	maskDict->lookup("H", &obj1);+      }+      if (!obj1.isInt()) {+	goto err2;+      }+      maskHeight = obj1.getInt();+      obj1.free();+      maskDict->lookup("ImageMask", &obj1);+      if (obj1.isNull()) {+	obj1.free();+	maskDict->lookup("IM", &obj1);+      }+      if (!obj1.isBool() || !obj1.getBool()) {+	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);+	if (obj2.isInt() && obj2.getInt() == 1) {+	  maskInvert = gTrue;+	}+	obj2.free();+      } else if (!obj1.isNull()) {+	goto err2;+      }+      obj1.free();+      haveExplicitMask = gTrue;+    }++    // draw it+    if (haveSoftMask) {+      out->drawSoftMaskedImage(state, ref, str, width, height, colorMap,+			       maskStr, maskWidth, maskHeight, maskColorMap);+      delete maskColorMap;+    } else if (haveExplicitMask) {+      out->drawMaskedImage(state, ref, str, width, height, colorMap,+			   maskStr, maskWidth, maskHeight, maskInvert);+    } else {+      out->drawImage(state, ref, str, width, height, colorMap,+		     haveColorKeyMask ? maskColors : (int *)NULL, inlineImg);+    }+    delete colorMap;++    maskObj.free();+    smaskObj.free();+  }++  if ((i = width * height) > 1000) {+    i = 1000;+  }+  updateLevel += i;++  return;++ err2:+  obj1.free();+ err1:+  error(getPos(), "Bad image parameters");+}++void Gfx::doForm(Object *str) {+  Dict *dict;+  GBool transpGroup, isolated, knockout;+  GfxColorSpace *blendingColorSpace;+  Object matrixObj, bboxObj;+  double m[6], bbox[4];+  Object resObj;+  Dict *resDict;+  Object obj1, obj2, obj3;+  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(getPos(), "Unknown form type");+  }+  obj1.free();++  // get bounding box+  dict->lookup("BBox", &bboxObj);+  if (!bboxObj.isArray()) {+    bboxObj.free();+    error(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;+  blendingColorSpace = NULL;+  if (dict->lookup("Group", &obj1)->isDict()) {+    if (obj1.dictLookup("S", &obj2)->isName("Transparency")) {+      transpGroup = gTrue;+      if (!obj1.dictLookup("CS", &obj3)->isNull()) {+	blendingColorSpace = GfxColorSpace::parse(&obj3);+      }+      obj3.free();+      if (obj1.dictLookup("I", &obj3)->isBool()) {+	isolated = obj3.getBool();+      }+      obj3.free();+      if (obj1.dictLookup("K", &obj3)->isBool()) {+	knockout = obj3.getBool();+      }+      obj3.free();+    }+    obj2.free();+  }+  obj1.free();++  // draw it+  ++formDepth;+  doForm1(str, resDict, m, bbox,+	  transpGroup, gFalse, blendingColorSpace, isolated, knockout);+  --formDepth;++  if (blendingColorSpace) {+    delete blendingColorSpace;+  }+  resObj.free();+}++void Gfx::doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,+		  GBool transpGroup, GBool softMask,+		  GfxColorSpace *blendingColorSpace,+		  GBool isolated, GBool knockout,+		  GBool alpha, Function *transferFunc,+		  GfxColor *backdropColor) {+  Parser *oldParser;+  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();++  if (softMask || transpGroup) {+    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];+  }++  // draw the form+  display(str, gFalse);++  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) {+    out->setSoftMask(state, bbox, alpha, transferFunc, backdropColor);+  } else if (transpGroup) {+    out->paintTransparencyGroup(state, bbox);+  }++  return;+}++//------------------------------------------------------------------------+// in-line image operators+//------------------------------------------------------------------------++void Gfx::opBeginImage(Object args[], int numArgs) {+  Stream *str;+  int c1, c2;++  // build dict/stream+  str = buildImageStream();++  // display the image+  if (str) {+    doImage(NULL, str, gTrue);+  +    // skip 'EI' tag+    c1 = str->getUndecodedStream()->getChar();+    c2 = str->getUndecodedStream()->getChar();+    while (!(c1 == 'E' && c2 == 'I') && c2 != EOF) {+      c1 = c2;+      c2 = str->getUndecodedStream()->getChar();+    }+    delete str;+  }+}++Stream *Gfx::buildImageStream() {+  Object dict;+  Object obj;+  char *key;+  Stream *str;++  // build dictionary+  dict.initDict(xref);+  parser->getObj(&obj);+  while (!obj.isCmd("ID") && !obj.isEOF()) {+    if (!obj.isName()) {+      error(getPos(), "Inline image dictionary key must be a name object");+      obj.free();+    } else {+      key = copyString(obj.getName());+      obj.free();+      parser->getObj(&obj);+      if (obj.isEOF() || obj.isError()) {+	gfree(key);+	break;+      }+      dict.dictAdd(key, &obj);+    }+    parser->getObj(&obj);+  }+  if (obj.isEOF()) {+    error(getPos(), "End of file in inline image");+    obj.free();+    dict.free();+    return NULL;+  }+  obj.free();++  // make stream+  str = new EmbedStream(parser->getStream(), &dict, gFalse, 0);+  str = str->addFilters(&dict);++  return str;+}++void Gfx::opImageData(Object args[], int numArgs) {+  error(getPos(), "Internal: got 'ID' operator");+}++void Gfx::opEndImage(Object args[], int numArgs) {+  error(getPos(), "Internal: 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) {+  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) {+  if (printCommands) {+    printf("  marked content: %s ", args[0].getName());+    if (numArgs == 2)+      args[2].print(stdout);+    printf("\n");+    fflush(stdout);+  }+}++void Gfx::opEndMarkedContent(Object args[], int numArgs) {+}++void Gfx::opMarkPoint(Object args[], int numArgs) {+  if (printCommands) {+    printf("  mark point: %s ", args[0].getName());+    if (numArgs == 2)+      args[2].print(stdout);+    printf("\n");+    fflush(stdout);+  }+}++//------------------------------------------------------------------------+// misc+//------------------------------------------------------------------------++void Gfx::drawAnnot(Object *str, AnnotBorderStyle *borderStyle,+		    double xMin, double yMin, double xMax, double yMax) {+  Dict *dict, *resDict;+  Object matrixObj, bboxObj, resObj;+  Object obj1;+  double m[6], bbox[4], ictm[6];+  double *ctm;+  double formX0, formY0, formX1, formY1;+  double annotX0, annotY0, annotX1, annotY1;+  double det, x, y, sx, sy;+  double r, g, b;+  GfxColor color;+  double *dash, *dash2;+  int dashLength;+  int i;++  //~ can we assume that we're in default user space?+  //~ (i.e., baseMatrix = ctm)++  // transform the annotation bbox from default user space to user+  // space: (bbox * baseMatrix) * iCTM+  ctm = state->getCTM();+  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;+  x = baseMatrix[0] * xMin + baseMatrix[2] * yMin + baseMatrix[4];+  y = baseMatrix[1] * xMin + baseMatrix[3] * yMin + baseMatrix[5];+  annotX0 = ictm[0] * x + ictm[2] * y + ictm[4];+  annotY0 = ictm[1] * x + ictm[3] * y + ictm[5];+  x = baseMatrix[0] * xMax + baseMatrix[2] * yMax + baseMatrix[4];+  y = baseMatrix[1] * xMax + baseMatrix[3] * yMax + baseMatrix[5];+  annotX1 = ictm[0] * x + ictm[2] * y + ictm[4];+  annotY1 = ictm[1] * x + ictm[3] * y + ictm[5];+  if (annotX0 > annotX1) {+    x = annotX0; annotX0 = annotX1; annotX1 = x;+  }+  if (annotY0 > annotY1) {+    y = annotY0; annotY0 = annotY1; annotY1 = y;+  }++  // draw the appearance stream (if there is one)+  if (str->isStream()) {++    // get stream dict+    dict = str->streamGetDict();++    // get the form bounding box+    dict->lookup("BBox", &bboxObj);+    if (!bboxObj.isArray()) {+      bboxObj.free();+      error(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 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 form bbox from form space to user space+    formX0 = bbox[0] * m[0] + bbox[1] * m[2] + m[4];+    formY0 = bbox[0] * m[1] + bbox[1] * m[3] + m[5];+    formX1 = bbox[2] * m[0] + bbox[3] * m[2] + m[4];+    formY1 = bbox[2] * m[1] + bbox[3] * m[3] + m[5];+    if (formX0 > formX1) {+      x = formX0; formX0 = formX1; formX1 = x;+    }+    if (formY0 > formY1) {+      y = formY0; formY0 = formY1; formY1 = y;+    }++    // scale the form to fit the annotation bbox+    if (formX1 == formX0) {+      // this shouldn't happen+      sx = 1;+    } else {+      sx = (annotX1 - annotX0) / (formX1 - formX0);+    }+    if (formY1 == formY0) {+      // this shouldn't happen+      sy = 1;+    } else {+      sy = (annotY1 - annotY0) / (formY1 - formY0);+    }+    m[0] *= sx;+    m[2] *= sx;+    m[4] = (m[4] - formX0) * sx + annotX0;+    m[1] *= sy;+    m[3] *= sy;+    m[5] = (m[5] - formY0) * sy + annotY0;++    // get resources+    dict->lookup("Resources", &resObj);+    resDict = resObj.isDict() ? resObj.getDict() : (Dict *)NULL;++    // draw it+    doForm1(str, resDict, m, bbox);++    resObj.free();+  }++  // draw the border+  if (borderStyle && borderStyle->getWidth() > 0) {+    if (state->getStrokeColorSpace()->getMode() != csDeviceRGB) {+      state->setStrokePattern(NULL);+      state->setStrokeColorSpace(new GfxDeviceRGBColorSpace());+      out->updateStrokeColorSpace(state);+    }+    borderStyle->getColor(&r, &g, &b);+    color.c[0] = dblToCol(r);+    color.c[1] = dblToCol(g);+    color.c[2] = dblToCol(b);+    state->setStrokeColor(&color);+    out->updateStrokeColor(state);+    // compute the width scale factor when going from default user+    // space to user space+    x = (baseMatrix[0] + baseMatrix[2]) * ictm[0] ++        (baseMatrix[1] + baseMatrix[3]) * ictm[2];+    y = (baseMatrix[0] + baseMatrix[2]) * ictm[1] ++        (baseMatrix[1] + baseMatrix[3]) * ictm[3];+    x = sqrt(0.5 * (x * x + y * y));+    state->setLineWidth(x * borderStyle->getWidth());+    out->updateLineWidth(state);+    borderStyle->getDash(&dash, &dashLength);+    if (borderStyle->getType() == annotBorderDashed && dashLength > 0) {+      dash2 = (double *)gmallocn(dashLength, sizeof(double));+      for (i = 0; i < dashLength; ++i) {+	dash2[i] = x * dash[i];+      }+      state->setLineDash(dash2, dashLength, 0);+      out->updateLineDash(state);+    }+    //~ this doesn't currently handle the beveled and engraved styles+    state->clearPath();+    state->moveTo(annotX0, out->upsideDown() ? annotY1 : annotY0);+    state->lineTo(annotX1, out->upsideDown() ? annotY1 : annotY0);+    if (borderStyle->getType() != annotBorderUnderlined) {+      state->lineTo(annotX1, out->upsideDown() ? annotY0 : annotY1);+      state->lineTo(annotX0, out->upsideDown() ? annotY0 : annotY1);+      state->closePath();+    }+    out->stroke(state);+  }+}++void Gfx::saveState() {+  out->saveState(state);+  state = state->save();+}++void Gfx::restoreState() {+  state = state->restore();+  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;+}
+ xpdf/Gfx.h view
@@ -0,0 +1,312 @@+//========================================================================+//+// Gfx.h+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef GFX_H+#define GFX_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "gtypes.h"++class GString;+class XRef;+class Array;+class Stream;+class Parser;+class Dict;+class Function;+class OutputDev;+class GfxFontDict;+class GfxFont;+class GfxPattern;+class GfxTilingPattern;+class GfxShadingPattern;+class GfxShading;+class GfxFunctionShading;+class GfxAxialShading;+class GfxRadialShading;+class GfxGouraudTriangleShading;+class GfxPatchMeshShading;+struct GfxPatch;+class GfxState;+struct GfxColor;+class GfxColorSpace;+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);+  GBool lookupXObject(char *name, Object *obj);+  GBool lookupXObjectNF(char *name, Object *obj);+  void lookupColorSpace(char *name, Object *obj);+  GfxPattern *lookupPattern(char *name);+  GfxShading *lookupShading(char *name);+  GBool lookupGState(char *name, Object *obj);++  GfxResources *getNext() { return next; }++private:++  GfxFontDict *fonts;+  Object xObjDict;+  Object colorSpaceDict;+  Object patternDict;+  Object shadingDict;+  Object gStateDict;+  GfxResources *next;+};++//------------------------------------------------------------------------+// Gfx+//------------------------------------------------------------------------++class Gfx {+public:++  // Constructor for regular output.+  Gfx(XRef *xrefA, 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(XRef *xrefA, OutputDev *outA, Dict *resDict,+      PDFRectangle *box, PDFRectangle *cropBox,+      GBool (*abortCheckCbkA)(void *data) = NULL,+      void *abortCheckCbkDataA = NULL);++  ~Gfx();++  // Interpret a stream or array of streams.+  void display(Object *obj, GBool topLevel = gTrue);++  // Display an annotation, given its appearance (a Form XObject),+  // border style, and bounding box (in default user space).+  void drawAnnot(Object *str, 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; }++private:++  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 updateLevel;++  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;++  Parser *parser;		// parser for page content stream(s)++  GBool				// callback to check for an abort+    (*abortCheckCbk)(void *data);+  void *abortCheckCbkData;++  static Operator opTab[];	// table of operators++  void go(GBool topLevel);+  void execOp(Object *cmd, Object args[], int numArgs);+  Operator *findOp(char *name);+  GBool checkArg(Object *arg, TchkType type);+  int 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, GBool alpha,+		  GfxColorSpace *blendingColorSpace,+		  GBool isolated, GBool knockout,+		  Function *transferFunc, GfxColor *backdropColor);+  void opSetRenderingIntent(Object args[], int numArgs);++  // 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 doTilingPatternFill(GfxTilingPattern *tPat,+			   GBool stroke, GBool eoFill);+  void doShadingPatternFill(GfxShadingPattern *sPat,+			    GBool stroke, GBool eoFill);+  void opShFill(Object args[], int numArgs);+  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, GfxColor *color0,+			   double x1, double y1, GfxColor *color1,+			   double x2, double y2, GfxColor *color2,+			   int nComps, int depth);+  void doPatchMeshShFill(GfxPatchMeshShading *shading);+  void fillPatch(GfxPatch *patch, int nComps, 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 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);++  // XObject operators+  void opXObject(Object args[], int numArgs);+  void doImage(Object *ref, Stream *str, GBool inlineImg);+  void doForm(Object *str);+  void doForm1(Object *str, Dict *resDict, double *matrix, double *bbox,+	       GBool transpGroup = gFalse, GBool softMask = gFalse,+	       GfxColorSpace *blendingColorSpace = NULL,+	       GBool isolated = gFalse, GBool knockout = gFalse,+	       GBool alpha = gFalse, Function *transferFunc = NULL,+	       GfxColor *backdropColor = NULL);++  // in-line image operators+  void opBeginImage(Object args[], int numArgs);+  Stream *buildImageStream();+  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);++  void pushResources(Dict *resDict);+  void popResources();+};++#endif
+ xpdf/GfxFont.cc view
@@ -0,0 +1,1568 @@+//========================================================================+//+// 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 "gmem.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 "FoFiType1.h"+#include "FoFiType1C.h"+#include "FoFiTrueType.h"+#include "GfxFont.h"++//------------------------------------------------------------------------++struct StdFontMapEntry {+  char *altName;+  char *properName;+};++// Acrobat 4.0 and earlier substituted Base14-compatible fonts without+// providing Widths and a FontDescriptor, so we munge the names into+// the proper Base14 names.  This table is from implementation note 44+// in the PDF 1.4 spec, with some additions based on empirical+// evidence.+static StdFontMapEntry stdFontMap[] = {+  { "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,Bold",                 "Courier-Bold" },+  { "Courier,BoldItalic",           "Courier-BoldOblique" },+  { "Courier,Italic",               "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,Bold",               "Helvetica-Bold" },+  { "Helvetica,BoldItalic",         "Helvetica-BoldOblique" },+  { "Helvetica,Italic",             "Helvetica-Oblique" },+  { "Helvetica-BoldItalic",         "Helvetica-BoldOblique" },+  { "Helvetica-Italic",             "Helvetica-Oblique" },+  { "Symbol,Bold",                  "Symbol" },+  { "Symbol,BoldItalic",            "Symbol" },+  { "Symbol,Italic",                "Symbol" },+  { "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" }+};++//------------------------------------------------------------------------+// GfxFont+//------------------------------------------------------------------------++GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {+  GString *nameA;+  GfxFont *font;+  Object obj1;++  // get base font name+  nameA = NULL;+  fontDict->lookup("BaseFont", &obj1);+  if (obj1.isName()) {+    nameA = new GString(obj1.getName());+  }+  obj1.free();++  // get font type+  font = NULL;+  fontDict->lookup("Subtype", &obj1);+  if (obj1.isName("Type1") || obj1.isName("MMType1")) {+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict);+  } else if (obj1.isName("Type1C")) {+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict);+  } else if (obj1.isName("Type3")) {+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict);+  } else if (obj1.isName("TrueType")) {+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict);+  } else if (obj1.isName("Type0")) {+    font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict);+  } else {+    error(-1, "Unknown font type: '%s'",+	  obj1.isName() ? obj1.getName() : "???");+    font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict);+  }+  obj1.free();++  return font;+}++GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {+  ok = gFalse;+  tag = new GString(tagA);+  id = idA;+  name = nameA;+  origName = nameA;+  embFontName = NULL;+  extFontFile = NULL;+}++GfxFont::~GfxFont() {+  delete tag;+  if (origName && origName != name) {+    delete origName;+  }+  if (name) {+    delete name;+  }+  if (embFontName) {+    delete embFontName;+  }+  if (extFontFile) {+    delete extFontFile;+  }+}++void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {+  Object obj1, obj2, obj3, obj4;+  double t;+  int i;++  // assume Times-Roman by default (for substitution purposes)+  flags = fontSerif;++  embFontID.num = -1;+  embFontID.gen = -1;+  missingWidth = 0;++  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 embedded font file+    if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) {+      embFontID = obj2.getRef();+      if (type != fontType1) {+	error(-1, "Mismatch between font type and embedded font file");+	type = fontType1;+      }+    }+    obj2.free();+    if (embFontID.num == -1 &&+	obj1.dictLookupNF("FontFile2", &obj2)->isRef()) {+      embFontID = obj2.getRef();+      if (type != fontTrueType && type != fontCIDType2) {+	error(-1, "Mismatch between font type and embedded font file");+	type = type == fontCIDType0 ? fontCIDType2 : fontTrueType;+      }+    }+    obj2.free();+    if (embFontID.num == -1 &&+	obj1.dictLookupNF("FontFile3", &obj2)->isRef()) {+      if (obj2.fetch(xref, &obj3)->isStream()) {+	obj3.streamGetDict()->lookup("Subtype", &obj4);+	if (obj4.isName("Type1")) {+	  embFontID = obj2.getRef();+	  if (type != fontType1) {+	    error(-1, "Mismatch between font type and embedded font file");+	    type = fontType1;+	  }+	} else if (obj4.isName("Type1C")) {+	  embFontID = obj2.getRef();+	  if (type != fontType1 && type != fontType1C) {+	    error(-1, "Mismatch between font type and embedded font file");+	  }+	  type = fontType1C;+	} else if (obj4.isName("TrueType")) {+	  embFontID = obj2.getRef();+	  if (type != fontTrueType) {+	    error(-1, "Mismatch between font type and embedded font file");+	    type = fontTrueType;+	  }+	} else if (obj4.isName("CIDFontType0C")) {+	  embFontID = obj2.getRef();+	  if (type != fontCIDType0) {+	    error(-1, "Mismatch between font type and embedded font file");+	  }+	  type = fontCIDType0C;+	} else if (obj4.isName("OpenType")) {+	  embFontID = obj2.getRef();+	  if (type == fontTrueType) {+	    type = fontTrueTypeOT;+	  } else if (type == fontType1) {+	    type = fontType1COT;+	  } else if (type == fontCIDType0) {+	    type = fontCIDType0COT;+	  } else if (type == fontCIDType2) {+	    type = fontCIDType2OT;+	  } else {+	    error(-1, "Mismatch between font type and embedded font file");+	  }+	} else {+	  error(-1, "Unknown embedded font type '%s'",+		obj4.isName() ? obj4.getName() : "???");+	}+	obj4.free();+      }+      obj3.free();+    }+    obj2.free();++    // look for MissingWidth+    obj1.dictLookup("MissingWidth", &obj2);+    if (obj2.isNum()) {+      missingWidth = obj2.getNum();+    }+    obj2.free();++    // get Ascent and Descent+    obj1.dictLookup("Ascent", &obj2);+    if (obj2.isNum()) {+      t = 0.001 * obj2.getNum();+      // some broken font descriptors set ascent and descent to 0+      if (t != 0) {+	ascent = t;+      }+    }+    obj2.free();+    obj1.dictLookup("Descent", &obj2);+    if (obj2.isNum()) {+      t = 0.001 * obj2.getNum();+      // some broken font descriptors set ascent and descent to 0+      if (t != 0) {+	descent = t;+      }+      // some broken font descriptors specify a positive descent+      if (descent > 0) {+	descent = -descent;+      }+    }+    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;+  int c;++  if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {+    obj1.free();+    return NULL;+  }+  buf = new GString();+  obj1.streamReset();+  while ((c = obj1.streamGetChar()) != EOF) {+    buf->append(c);+  }+  obj1.streamClose();+  obj1.free();+  if (ctu) {+    ctu->mergeCMap(buf, nBits);+  } else {+    ctu = CharCodeToUnicode::parseCMap(buf, nBits);+  }+  delete buf;+  return ctu;+}++void GfxFont::findExtFontFile() {+  static char *type1Exts[] = { ".pfa", ".pfb", ".ps", "", NULL };+  static char *ttExts[] = { ".ttf", NULL };++  if (name) {+    if (type == fontType1) {+      extFontFile = globalParams->findFontFile(name, type1Exts);+    } else if (type == fontTrueType) {+      extFontFile = globalParams->findFontFile(name, ttExts);+    }+  }+}++char *GfxFont::readExtFontFile(int *len) {+  FILE *f;+  char *buf;++  if (!(f = fopen(extFontFile->getCString(), "rb"))) {+    error(-1, "External font file '%s' vanished", extFontFile->getCString());+    return NULL;+  }+  fseek(f, 0, SEEK_END);+  *len = (int)ftell(f);+  fseek(f, 0, SEEK_SET);+  buf = (char *)gmalloc(*len);+  if ((int)fread(buf, 1, *len, f) != *len) {+    error(-1, "Error reading external font file '%s'",+	  extFontFile->getCString());+  }+  fclose(f);+  return buf;+}++char *GfxFont::readEmbFontFile(XRef *xref, int *len) {+  char *buf;+  Object obj1, obj2;+  Stream *str;+  int c;+  int size, i;++  obj1.initRef(embFontID.num, embFontID.gen);+  obj1.fetch(xref, &obj2);+  if (!obj2.isStream()) {+    error(-1, "Embedded font file is not a stream");+    obj2.free();+    obj1.free();+    embFontID.num = -1;+    return NULL;+  }+  str = obj2.getStream();++  buf = NULL;+  i = size = 0;+  str->reset();+  while ((c = str->getChar()) != EOF) {+    if (i == size) {+      size += 4096;+      buf = (char *)grealloc(buf, size);+    }+    buf[i++] = c;+  }+  *len = i;+  str->close();++  obj2.free();+  obj1.free();++  return buf;+}++//------------------------------------------------------------------------+// Gfx8BitFont+//------------------------------------------------------------------------++Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,+			 GfxFontType typeA, Dict *fontDict):+  GfxFont(tagA, idA, nameA)+{+  GString *name2;+  BuiltinFont *builtinFont;+  char **baseEnc;+  GBool baseEncFromFontFile;+  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;++  type = typeA;+  ctu = NULL;++  // do font name substitution for various aliases of the Base 14 font+  // names+  if (name) {+    name2 = name->copy();+    i = 0;+    while (i < name2->getLength()) {+      if (name2->getChar(i) == ' ') {+	name2->del(i);+      } else {+	++i;+      }+    }+    a = 0;+    b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);+    // invariant: stdFontMap[a].altName <= name2 < stdFontMap[b].altName+    while (b - a > 1) {+      m = (a + b) / 2;+      if (name2->cmp(stdFontMap[m].altName) >= 0) {+	a = m;+      } else {+	b = m;+      }+    }+    if (!name2->cmp(stdFontMap[a].altName)) {+      name = new GString(stdFontMap[a].properName);+    }+    delete name2;+  }++  // is it a built-in font?+  builtinFont = NULL;+  if (name) {+    for (i = 0; i < nBuiltinFonts; ++i) {+      if (!name->cmp(builtinFonts[i].name)) {+	builtinFont = &builtinFonts[i];+	break;+      }+    }+  }++  // default ascent/descent values+  if (builtinFont) {+    ascent = 0.001 * builtinFont->ascent;+    descent = 0.001 * builtinFont->descent;+    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 {+    ascent = 0.95;+    descent = -0.35;+    fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;+  }++  // get info from font descriptor+  readFontDescriptor(xref, fontDict);++  // for non-embedded fonts, don't trust the ascent/descent/bbox+  // values from the font descriptor+  if (builtinFont && embFontID.num < 0) {+    ascent = 0.001 * builtinFont->ascent;+    descent = 0.001 * builtinFont->descent;+    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];+  }++  // look for an external font file+  findExtFontFile();++  // 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(-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 or external 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 && (extFontFile || embFontID.num >= 0)) {+    if (extFontFile) {+      ffT1 = FoFiType1::load(extFontFile->getCString());+    } else {+      buf = readEmbFontFile(xref, &len);+      ffT1 = FoFiType1::make(buf, len);+    }+    if (ffT1) {+      if (ffT1->getName()) {+	if (embFontName) {+	  delete embFontName;+	}+	embFontName = new GString(ffT1->getName());+      }+      if (!baseEnc) {+	baseEnc = ffT1->getEncoding();+	baseEncFromFontFile = gTrue;+      }+    }+  } else if (type == fontType1C && (extFontFile || embFontID.num >= 0)) {+    if (extFontFile) {+      ffT1C = FoFiType1C::load(extFontFile->getCString());+    } else {+      buf = readEmbFontFile(xref, &len);+      ffT1C = FoFiType1C::make(buf, len);+    }+    if (ffT1C) {+      if (ffT1C->getName()) {+	if (embFontName) {+	  delete embFontName;+	}+	embFontName = new GString(ffT1C->getName());+      }+      if (!baseEnc) {+	baseEnc = ffT1C->getEncoding();+	baseEncFromFontFile = gTrue;+      }+    }+  }+  if (buf) {+    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] = baseEnc[i];+    if ((encFree[i] = 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 && (extFontFile || embFontID.num >= 0) &&+      baseEncFromFontFile) {+    for (i = 0; i < 256; ++i) {+      if (!enc[i] && standardEncoding[i]) {+	enc[i] = 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(-1, "Wrong type in font encoding resource differences (%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]) &&+	     isxdigit(charName[1]) && isxdigit(charName[2]) &&+	     ((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]) && isxdigit(charName[1]) &&+	     ((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+  // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B'+  // are any letters, 'xx' is two hex digits, and 'nn' is 2-4+  // decimal digits+  if (missing && globalParams->getMapNumericCharNames()) {+    for (code = 0; code < 256; ++code) {+      if ((charName = enc[code]) && !toUnicode[code] &&+	  strcmp(charName, ".notdef")) {+	n = strlen(charName);+	code2 = -1;+	if (hex && n == 3 && isalpha(charName[0]) &&+	    isxdigit(charName[1]) && isxdigit(charName[2])) {+	  sscanf(charName+1, "%x", &code2);+	} else if (hex && n == 2 &&+		   isxdigit(charName[0]) && isxdigit(charName[1])) {+	  sscanf(charName, "%x", &code2);+	} else if (!hex && n >= 2 && n <= 4 &&+		   isdigit(charName[0]) && isdigit(charName[1])) {+	  code2 = atoi(charName);+	} else if (n >= 3 && n <= 5 &&+		   isdigit(charName[1]) && isdigit(charName[2])) {+	  code2 = atoi(charName+1);+	} else if (n >= 4 && n <= 6 &&+		   isdigit(charName[2]) && isdigit(charName[3])) {+	  code2 = atoi(charName+2);+	}+	if (code2 >= 0 && code2 <= 0xff) {+	  toUnicode[code] = (Unicode)code2;+	}+      }+    }++  // 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 (widths[code] != widths[firstChar]) {+	  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;+}++Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {+  Gushort *map;+  int cmapPlatform, cmapEncoding;+  int unicodeCmap, macRomanCmap, msSymbolCmap, cmap;+  GBool useMacRoman, useUnicode;+  char *charName;+  Unicode u;+  int code, i, n;++  map = (Gushort *)gmallocn(256, sizeof(Gushort));+  for (i = 0; i < 256; ++i) {+    map[i] = 0;+  }++  // To match up with the Adobe-defined behaviour, we choose a cmap+  // like this:+  // 1. If the PDF font has an encoding:+  //    1a. If the PDF font specified MacRomanEncoding and the+  //        TrueType font has a Macintosh Roman cmap, use it, and+  //        reverse map the char names through MacRomanEncoding to+  //        get char codes.+  //    1b. If the TrueType font has a Microsoft Unicode cmap or a+  //        non-Microsoft Unicode cmap, use it, and use the Unicode+  //        indexes, not the char codes.+  //    1c. If the PDF font is symbolic and the TrueType font has a+  //        Microsoft Symbol cmap, use it, and use char codes+  //        directly (possibly with an offset of 0xf000).+  //    1d. If the TrueType font has a Macintosh Roman cmap, use it,+  //        as in case 1a.+  // 2. If the PDF font does not have an encoding or the PDF font is+  //    symbolic:+  //    2a. If the TrueType font has a Macintosh Roman cmap, use it,+  //        and use char codes directly (possibly with an offset of+  //        0xf000).+  //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,+  //        and use char codes directly (possible with an offset of+  //        0xf000).+  // 3. If none of these rules apply, use the first cmap and hope for+  //    the best (this shouldn't happen).+  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) {+      unicodeCmap = i;+    } else if (cmapPlatform == 1 && cmapEncoding == 0) {+      macRomanCmap = i;+    } else if (cmapPlatform == 3 && cmapEncoding == 0) {+      msSymbolCmap = i;+    }+  }+  cmap = 0;+  useMacRoman = gFalse;+  useUnicode = gFalse;+  if (hasEncoding) {+    if (usesMacRomanEnc && macRomanCmap >= 0) {+      cmap = macRomanCmap;+      useMacRoman = gTrue;+    } else if (unicodeCmap >= 0) {+      cmap = unicodeCmap;+      useUnicode = gTrue;+    } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) {+      cmap = msSymbolCmap;+    } else if ((flags & fontSymbolic) && macRomanCmap >= 0) {+      cmap = macRomanCmap;+    } else if (macRomanCmap >= 0) {+      cmap = macRomanCmap;+      useMacRoman = gTrue;+    }+  } else {+    if (msSymbolCmap >= 0) {+      cmap = msSymbolCmap;+    } else if (macRomanCmap >= 0) {+      cmap = macRomanCmap;+    }+  }++  // reverse map the char names through MacRomanEncoding, then map the+  // char codes through the cmap+  if (useMacRoman) {+    for (i = 0; i < 256; ++i) {+      if ((charName = enc[i])) {+	if ((code = globalParams->getMacRomanCharCode(charName))) {+	  map[i] = ff->mapCodeToGID(cmap, code);+	}+      }+    }++  // 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);+      }+    }++  // 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] && (charName = enc[i])) {+      map[i] = (Gushort)(int)ff->mapNameToGID(charName);+    }+  }++  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;+}++Dict *Gfx8BitFont::getResources() {+  return resources.isDict() ? resources.getDict() : (Dict *)NULL;+}++//------------------------------------------------------------------------+// GfxCIDFont+//------------------------------------------------------------------------++static int CDECL cmpWidthExcep(const void *w1, const void *w2) {+  return ((GfxFontCIDWidthExcep *)w1)->first -+         ((GfxFontCIDWidthExcep *)w2)->first;+}++static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {+  return ((GfxFontCIDWidthExcepV *)w1)->first -+         ((GfxFontCIDWidthExcepV *)w2)->first;+}++GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,+		       Dict *fontDict):+  GfxFont(tagA, idA, nameA)+{+  Dict *desFontDict;+  GString *collection, *cMapName;+  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;++  ascent = 0.95;+  descent = -0.35;+  fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;+  cMap = NULL;+  ctu = NULL;+  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()) {+    error(-1, "Missing DescendantFonts entry in Type 0 font");+    obj1.free();+    goto err1;+  }+  if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {+    error(-1, "Bad descendant font in Type 0 font");+    goto err3;+  }+  obj1.free();+  desFontDict = desFontDictObj.getDict();++  // font type+  if (!desFontDict->lookup("Subtype", &obj1)) {+    error(-1, "Missing Subtype entry in Type 0 descendant font");+    goto err3;+  }+  if (obj1.isName("CIDFontType0")) {+    type = fontCIDType0;+  } else if (obj1.isName("CIDFontType2")) {+    type = fontCIDType2;+  } else {+    error(-1, "Unknown Type 0 descendant font type '%s'",+	  obj1.isName() ? obj1.getName() : "???");+    goto err3;+  }+  obj1.free();++  // get info from font descriptor+  readFontDescriptor(xref, desFontDict);++  // look for an external font file+  findExtFontFile();++  //----- encoding info -----++  // char collection+  if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) {+    error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font");+    goto err3;+  }+  obj1.dictLookup("Registry", &obj2);+  obj1.dictLookup("Ordering", &obj3);+  if (!obj2.isString() || !obj3.isString()) {+    error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font");+    goto err4;+  }+  collection = obj2.getString()->copy()->append('-')->append(obj3.getString());+  obj3.free();+  obj2.free();+  obj1.free();++  // look for a ToUnicode CMap+  if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {++    // the "Adobe-Identity" and "Adobe-UCS" collections don't have+    // cidToUnicode files+    if (collection->cmp("Adobe-Identity") &&+	collection->cmp("Adobe-UCS")) {++      // look for a user-supplied .cidToUnicode file+      if (!(ctu = globalParams->getCIDToUnicode(collection))) {+	error(-1, "Unknown character collection '%s'",+	      collection->getCString());+	// fall-through, assuming the Identity mapping -- this appears+	// to match Adobe's behavior+      }+    }+  }++  // look for a Unicode-to-Unicode mapping+  if (name && (utu = globalParams->getUnicodeToUnicode(name))) {+    if (ctu) {+      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)+  //~ need to handle a CMap stream here+  //~ also need to deal with the UseCMap entry in the stream dict+  if (!fontDict->lookup("Encoding", &obj1)->isName()) {+    error(-1, "Missing or invalid Encoding entry in Type 0 font");+    delete collection;+    goto err3;+  }+  cMapName = new GString(obj1.getName());+  obj1.free();+  if (!(cMap = globalParams->getCMap(collection, cMapName))) {+    error(-1, "Unknown CMap '%s' for character collection '%s'",+	  cMapName->getCString(), collection->getCString());+    delete collection;+    delete cMapName;+    goto err2;+  }+  delete collection;+  delete cMapName;++  // CIDToGIDMap (for embedded TrueType fonts)+  if (type == fontCIDType2) {+    desFontDict->lookup("CIDToGIDMap", &obj1);+    if (obj1.isStream()) {+      cidToGIDLen = 0;+      i = 64;+      cidToGID = (Gushort *)gmallocn(i, sizeof(Gushort));+      obj1.streamReset();+      while ((c1 = obj1.streamGetChar()) != EOF &&+	     (c2 = obj1.streamGetChar()) != EOF) {+	if (cidToGIDLen == i) {+	  i *= 2;+	  cidToGID = (Gushort *)greallocn(cidToGID, i, sizeof(Gushort));+	}+	cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2);+      }+    } else if (!obj1.isName("Identity") && !obj1.isNull()) {+      error(-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(-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(-1, "Bad widths array in Type 0 font");+	  }+	  obj4.free();+	}+	i += 2;+      } else {+	error(-1, "Bad widths array in Type 0 font");+	++i;+      }+      obj3.free();+      obj2.free();+    }+    qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep),+	  &cmpWidthExcep);+  }+  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(-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 < 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.nExceps].first = j;+	    widths.excepsV[widths.nExceps].last = j;+	    widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001;+	    widths.excepsV[widths.nExceps].vx = obj5.getNum() * 0.001;+	    widths.excepsV[widths.nExceps].vy = obj6.getNum() * 0.001;+	    ++j;+	    ++widths.nExcepsV;+	  } else {+	    error(-1, "Bad widths (W2) array in Type 0 font");+	  }+	  obj6.free();+	  obj5.free();+	  obj4.free();+	}+	i += 2;+      } else {+	error(-1, "Bad widths (W2) array in Type 0 font");+	++i;+      }+      obj3.free();+      obj2.free();+    }+    qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV),+	  &cmpWidthExcepV);+  }+  obj1.free();++  desFontDictObj.free();+  ok = gTrue;+  return;++ err4:+  obj3.free();+  obj2.free();+ err3:+  obj1.free();+ err2:+  desFontDictObj.free();+ err1:;+}++GfxCIDFont::~GfxCIDFont() {+  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;+  double w, h, vx, vy;+  int n, a, b, m;++  if (!cMap) {+    *code = 0;+    *uLen = 0;+    *dx = *dy = 0;+    return 1;+  }++  *code = (CharCode)(cid = cMap->getCID(s, len, &n));+  if (ctu) {+    *uLen = ctu->mapToUnicode(cid, u, uSize);+  } else {+    *uLen = 0;+  }++  // horizontal+  if (cMap->getWMode() == 0) {+    w = widths.defWidth;+    h = vx = vy = 0;+    if (widths.nExceps > 0 && cid >= widths.exceps[0].first) {+      a = 0;+      b = widths.nExceps;+      // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first+      while (b - a > 1) {+	m = (a + b) / 2;+	if (widths.exceps[m].first <= cid) {+	  a = m;+	} else {+	  b = m;+	}+      }+      if (cid <= widths.exceps[a].last) {+	w = widths.exceps[a].width;+      }+    }++  // vertical+  } else {+    w = 0;+    h = widths.defHeight;+    vx = widths.defWidth / 2;+    vy = widths.defVY;+    if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) {+      a = 0;+      b = widths.nExcepsV;+      // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first+      while (b - a > 1) {+	m = (a + b) / 2;+	if (widths.excepsV[m].last <= cid) {+	  a = m;+	} else {+	  b = m;+	}+      }+      if (cid <= widths.excepsV[a].last) {+	h = widths.excepsV[a].height;+	vx = widths.excepsV[a].vx;+	vy = widths.excepsV[a].vy;+      }+    }+  }++  *dx = w;+  *dy = h;+  *ox = vx;+  *oy = vy;++  return n;+}++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;+}++//------------------------------------------------------------------------+// GfxFontDict+//------------------------------------------------------------------------++GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {+  int i;+  Object obj1, obj2;+  Ref r;++  numFonts = fontDict->getLength();+  fonts = (GfxFont **)gmallocn(numFonts, sizeof(GfxFont *));+  for (i = 0; i < numFonts; ++i) {+    fontDict->getValNF(i, &obj1);+    obj1.fetch(xref, &obj2);+    if (obj2.isDict()) {+      if (obj1.isRef()) {+	r = obj1.getRef();+      } else {+	// no indirect reference for this font, so invent a unique one+	// (legal generation numbers are five digits, so any 6-digit+	// number would be safe)+	r.num = i;+	if (fontDictRef) {+	  r.gen = 100000 + fontDictRef->num;+	} else {+	  r.gen = 999999;+	}+      }+      fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),+				   r, obj2.getDict());+      if (fonts[i] && !fonts[i]->isOk()) {+	delete fonts[i];+	fonts[i] = NULL;+      }+    } else {+      error(-1, "font resource is not a dictionary");+      fonts[i] = NULL;+    }+    obj1.free();+    obj2.free();+  }+}++GfxFontDict::~GfxFontDict() {+  int i;++  for (i = 0; i < numFonts; ++i) {+    if (fonts[i]) {+      delete fonts[i];+    }+  }+  gfree(fonts);+}++GfxFont *GfxFontDict::lookup(char *tag) {+  int i;++  for (i = 0; i < numFonts; ++i) {+    if (fonts[i] && fonts[i]->matches(tag)) {+      return fonts[i];+    }+  }+  return NULL;+}
+ xpdf/GfxFont.h view
@@ -0,0 +1,320 @@+//========================================================================+//+// 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 Dict;+class CMap;+class CharCodeToUnicode;+class FoFiTrueType;+struct GfxFontCIDWidths;++//------------------------------------------------------------------------+// 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+};++//------------------------------------------------------------------------+// 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, char *tagA, Ref idA, Dict *fontDict);++  GfxFont(char *tagA, Ref idA, GString *nameA);++  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 base font name.+  GString *getName() { return name; }++  // Get the original font name (ignornig any munging that might have+  // been done to map to a canonical Base-14 font name).+  GString *getOrigName() { return origName; }++  // 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 the name of the external font file.  Returns NULL if there+  // is no external font file.+  GString *getExtFontFile() { return extFontFile; }++  // 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; }++  // Return the writing mode (0=horizontal, 1=vertical).+  virtual int getWMode() { return 0; }++  // Read an external or embedded font file into a buffer.+  char *readExtFontFile(int *len);+  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;++protected:++  void readFontDescriptor(XRef *xref, Dict *fontDict);+  CharCodeToUnicode *readToUnicodeCMap(Dict *fontDict, int nBits,+				       CharCodeToUnicode *ctu);+  void findExtFontFile();++  GString *tag;			// PDF font tag+  Ref id;			// reference (used as unique ID)+  GString *name;		// font name+  GString *origName;		// original 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+  GString *extFontFile;		// external font file name+  double fontMat[6];		// font matrix (Type 3 only)+  double fontBBox[4];		// font bounding box (Type 3 only)+  double missingWidth;		// "default" width+  double ascent;		// max height above baseline+  double descent;		// max depth below baseline+  GBool ok;+};++//------------------------------------------------------------------------+// Gfx8BitFont+//------------------------------------------------------------------------++class Gfx8BitFont: public GfxFont {+public:++  Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,+	      GfxFontType typeA, 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.)+  Gushort *getCodeToGIDMap(FoFiTrueType *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);++  // Return the Type 3 Resources dictionary, or NULL if none.+  Dict *getResources();++private:++  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;+  double widths[256];		// character widths+  Object charProcs;		// Type 3 CharProcs dictionary+  Object resources;		// Type 3 Resources dictionary+};++//------------------------------------------------------------------------+// GfxCIDFont+//------------------------------------------------------------------------++class GfxCIDFont: public GfxFont {+public:++  GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,+	     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 CID-to-GID mapping table.  These should only be called+  // if type is fontCIDType2.+  Gushort *getCIDToGID() { return cidToGID; }+  int getCIDToGIDLen() { return cidToGIDLen; }++private:++  CMap *cMap;			// char code --> CID+  CharCodeToUnicode *ctu;	// CID --> Unicode+  GfxFontCIDWidths widths;	// character widths+  Gushort *cidToGID;		// CID --> GID mapping (for embedded+				//   TrueType fonts)+  int cidToGIDLen;+};++//------------------------------------------------------------------------+// 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);++  // Iterative access.+  int getNumFonts() { return numFonts; }+  GfxFont *getFont(int i) { return fonts[i]; }++private:++  GfxFont **fonts;		// list of fonts+  int numFonts;			// number of fonts+};++#endif
+ xpdf/GfxState.cc view
@@ -0,0 +1,4137 @@+//========================================================================+//+// GfxState.cc+//+// Copyright 1996-2003 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 "Error.h"+#include "Object.h"+#include "Array.h"+#include "Page.h"+#include "GfxState.h"++//------------------------------------------------------------------------++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 {+  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 char *gfxColorSpaceModeNames[] = {+  "DeviceGray",+  "CalGray",+  "DeviceRGB",+  "CalRGB",+  "DeviceCMYK",+  "Lab",+  "ICCBased",+  "Indexed",+  "Separation",+  "DeviceN",+  "Pattern"+};++#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))++//------------------------------------------------------------------------+// GfxColorSpace+//------------------------------------------------------------------------++GfxColorSpace::GfxColorSpace() {+}++GfxColorSpace::~GfxColorSpace() {+}++GfxColorSpace *GfxColorSpace::parse(Object *csObj) {+  GfxColorSpace *cs;+  Object obj1;++  cs = NULL;+  if (csObj->isName()) {+    if (csObj->isName("DeviceGray") || csObj->isName("G")) {+      cs = new GfxDeviceGrayColorSpace();+    } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {+      cs = new GfxDeviceRGBColorSpace();+    } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {+      cs = new GfxDeviceCMYKColorSpace();+    } else if (csObj->isName("Pattern")) {+      cs = new GfxPatternColorSpace(NULL);+    } else {+      error(-1, "Bad color space '%s'", csObj->getName());+    }+  } else if (csObj->isArray()) {+    csObj->arrayGet(0, &obj1);+    if (obj1.isName("DeviceGray") || obj1.isName("G")) {+      cs = new GfxDeviceGrayColorSpace();+    } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {+      cs = new GfxDeviceRGBColorSpace();+    } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {+      cs = new GfxDeviceCMYKColorSpace();+    } else if (obj1.isName("CalGray")) {+      cs = GfxCalGrayColorSpace::parse(csObj->getArray());+    } else if (obj1.isName("CalRGB")) {+      cs = GfxCalRGBColorSpace::parse(csObj->getArray());+    } else if (obj1.isName("Lab")) {+      cs = GfxLabColorSpace::parse(csObj->getArray());+    } else if (obj1.isName("ICCBased")) {+      cs = GfxICCBasedColorSpace::parse(csObj->getArray());+    } else if (obj1.isName("Indexed") || obj1.isName("I")) {+      cs = GfxIndexedColorSpace::parse(csObj->getArray());+    } else if (obj1.isName("Separation")) {+      cs = GfxSeparationColorSpace::parse(csObj->getArray());+    } else if (obj1.isName("DeviceN")) {+      cs = GfxDeviceNColorSpace::parse(csObj->getArray());+    } else if (obj1.isName("Pattern")) {+      cs = GfxPatternColorSpace::parse(csObj->getArray());+    } else {+      error(-1, "Bad color space");+    }+    obj1.free();+  } else {+    error(-1, "Bad color space - expected name or array");+  }+  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;+}++char *GfxColorSpace::getColorSpaceModeName(int idx) {+  return gfxColorSpaceModeNames[idx];+}++//------------------------------------------------------------------------+// GfxDeviceGrayColorSpace+//------------------------------------------------------------------------++GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {+}++GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {+}++GfxColorSpace *GfxDeviceGrayColorSpace::copy() {+  return new GfxDeviceGrayColorSpace();+}++void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {+  *gray = clip01(color->c[0]);+}++void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);+}++void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {+  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) {+  GfxCalGrayColorSpace *cs;+  Object obj1, obj2, obj3;++  arr->get(1, &obj1);+  if (!obj1.isDict()) {+    error(-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) {+  *gray = clip01(color->c[0]);+}++void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);+}++void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {+  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() {+  return new GfxDeviceRGBColorSpace();+}++void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {+  *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) {+  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) {+  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) {+  GfxCalRGBColorSpace *cs;+  Object obj1, obj2, obj3;+  int i;++  arr->get(1, &obj1);+  if (!obj1.isDict()) {+    error(-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) {+  *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) {+  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) {+  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() {+  return new GfxDeviceCMYKColorSpace();+}++void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {+  *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) {+  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) {+  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) {+  GfxLabColorSpace *cs;+  Object obj1, obj2, obj3;++  arr->get(1, &obj1);+  if (!obj1.isDict()) {+    error(-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) {+  GfxRGB rgb;++  getRGB(color, &rgb);+  *gray = clip01((GfxColorComp)(0.299 * rgb.r ++				0.587 * rgb.g ++				0.114 * rgb.b + 0.5));+}++void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  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) {+  GfxRGB rgb;+  GfxColorComp c, m, y, k;++  getRGB(color, &rgb);+  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) {+  GfxICCBasedColorSpace *cs;+  Ref iccProfileStreamA;+  int nCompsA;+  GfxColorSpace *altA;+  Dict *dict;+  Object obj1, obj2, obj3;+  int i;++  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(-1, "Bad ICCBased color space (stream)");+    obj1.free();+    return NULL;+  }+  dict = obj1.streamGetDict();+  if (!dict->lookup("N", &obj2)->isInt()) {+    error(-1, "Bad ICCBased color space (N)");+    obj2.free();+    obj1.free();+    return NULL;+  }+  nCompsA = obj2.getInt();+  obj2.free();+  if (nCompsA > gfxColorMaxComps) {+    error(-1, "ICCBased color space with too many (%d > %d) components",+	  nCompsA, gfxColorMaxComps);+    nCompsA = gfxColorMaxComps;+  }+  if (dict->lookup("Alternate", &obj2)->isNull() ||+      !(altA = GfxColorSpace::parse(&obj2))) {+    switch (nCompsA) {+    case 1:+      altA = new GfxDeviceGrayColorSpace();+      break;+    case 3:+      altA = new GfxDeviceRGBColorSpace();+      break;+    case 4:+      altA = new GfxDeviceCMYKColorSpace();+      break;+    default:+      error(-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) {+  alt->getGray(color, gray);+}++void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  alt->getRGB(color, rgb);+}++void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {+  alt->getCMYK(color, cmyk);+}++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));+}++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) {+  GfxIndexedColorSpace *cs;+  GfxColorSpace *baseA;+  int indexHighA;+  Object obj1;+  int x;+  char *s;+  int n, i, j;++  if (arr->getLength() != 4) {+    error(-1, "Bad Indexed color space");+    goto err1;+  }+  arr->get(1, &obj1);+  if (!(baseA = GfxColorSpace::parse(&obj1))) {+    error(-1, "Bad Indexed color space (base color space)");+    goto err2;+  }+  obj1.free();+  if (!arr->get(2, &obj1)->isInt()) {+    error(-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(-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(-1, "Bad Indexed color space (lookup table stream too short)");+	  goto err3;+	}+	cs->lookup[i*n + j] = (Guchar)x;+      }+    }+    obj1.streamClose();+  } else if (obj1.isString()) {+    if (obj1.getString()->getLength() < (indexHighA + 1) * n) {+      error(-1, "Bad Indexed color space (lookup table string too short)");+      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(-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;++  n = base->getNComps();+  base->getDefaultRanges(low, range, indexHigh);+  p = &lookup[(int)(colToDbl(color->c[0]) + 0.5) * 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) {+  GfxColor color2;++  base->getGray(mapColorToBase(color, &color2), gray);+}++void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  GfxColor color2;++  base->getRGB(mapColorToBase(color, &color2), rgb);+}++void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {+  GfxColor color2;++  base->getCMYK(mapColorToBase(color, &color2), cmyk);+}++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");+}++GfxSeparationColorSpace::~GfxSeparationColorSpace() {+  delete name;+  delete alt;+  delete func;+}++GfxColorSpace *GfxSeparationColorSpace::copy() {+  return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy());+}++//~ handle the 'All' and 'None' colorants+GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr) {+  GfxSeparationColorSpace *cs;+  GString *nameA;+  GfxColorSpace *altA;+  Function *funcA;+  Object obj1;++  if (arr->getLength() != 4) {+    error(-1, "Bad Separation color space");+    goto err1;+  }+  if (!arr->get(1, &obj1)->isName()) {+    error(-1, "Bad Separation color space (name)");+    goto err2;+  }+  nameA = new GString(obj1.getName());+  obj1.free();+  arr->get(2, &obj1);+  if (!(altA = GfxColorSpace::parse(&obj1))) {+    error(-1, "Bad Separation color space (alternate color space)");+    goto err3;+  }+  obj1.free();+  arr->get(3, &obj1);+  if (!(funcA = Function::parse(&obj1))) {+    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) {+  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);+}++void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  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);+}++void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {+  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);+}++void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {+  color->c[0] = gfxColorComp1;+}++//------------------------------------------------------------------------+// GfxDeviceNColorSpace+//------------------------------------------------------------------------++GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,+					   GfxColorSpace *altA,+					   Function *funcA) {+  nComps = nCompsA;+  alt = altA;+  func = funcA;+  nonMarking = gFalse;+}++GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {+  int i;++  for (i = 0; i < nComps; ++i) {+    delete names[i];+  }+  delete alt;+  delete func;+}++GfxColorSpace *GfxDeviceNColorSpace::copy() {+  GfxDeviceNColorSpace *cs;+  int i;++  cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy());+  for (i = 0; i < nComps; ++i) {+    cs->names[i] = names[i]->copy();+  }+  cs->nonMarking = nonMarking;+  return cs;+}++//~ handle the 'None' colorant+GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr) {+  GfxDeviceNColorSpace *cs;+  int nCompsA;+  GString *namesA[gfxColorMaxComps];+  GfxColorSpace *altA;+  Function *funcA;+  Object obj1, obj2;+  int i;++  if (arr->getLength() != 4 && arr->getLength() != 5) {+    error(-1, "Bad DeviceN color space");+    goto err1;+  }+  if (!arr->get(1, &obj1)->isArray()) {+    error(-1, "Bad DeviceN color space (names)");+    goto err2;+  }+  nCompsA = obj1.arrayGetLength();+  if (nCompsA > gfxColorMaxComps) {+    error(-1, "DeviceN color space with too many (%d > %d) components",+	  nCompsA, gfxColorMaxComps);+    nCompsA = gfxColorMaxComps;+  }+  for (i = 0; i < nCompsA; ++i) {+    if (!obj1.arrayGet(i, &obj2)->isName()) {+      error(-1, "Bad DeviceN color space (names)");+      obj2.free();+      goto err2;+    }+    namesA[i] = new GString(obj2.getName());+    obj2.free();+  }+  obj1.free();+  arr->get(2, &obj1);+  if (!(altA = GfxColorSpace::parse(&obj1))) {+    error(-1, "Bad DeviceN color space (alternate color space)");+    goto err3;+  }+  obj1.free();+  arr->get(3, &obj1);+  if (!(funcA = Function::parse(&obj1))) {+    goto err4;+  }+  obj1.free();+  cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA);+  cs->nonMarking = gTrue;+  for (i = 0; i < nCompsA; ++i) {+    cs->names[i] = namesA[i];+    if (namesA[i]->cmp("None")) {+      cs->nonMarking = gFalse;+    }+  }+  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) {+  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);+}++void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  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);+}++void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {+  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);+}++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() {+  return new GfxPatternColorSpace(under ? under->copy() :+				          (GfxColorSpace *)NULL);+}++GfxColorSpace *GfxPatternColorSpace::parse(Array *arr) {+  GfxPatternColorSpace *cs;+  GfxColorSpace *underA;+  Object obj1;++  if (arr->getLength() != 1 && arr->getLength() != 2) {+    error(-1, "Bad Pattern color space");+    return NULL;+  }+  underA = NULL;+  if (arr->getLength() == 2) {+    arr->get(1, &obj1);+    if (!(underA = GfxColorSpace::parse(&obj1))) {+      error(-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) {+  *gray = 0;+}++void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {+  rgb->r = rgb->g = rgb->b = 0;+}++void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {+  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 *obj) {+  GfxPattern *pattern;+  Object obj1;++  if (obj->isDict()) {+    obj->dictLookup("PatternType", &obj1);+  } else if (obj->isStream()) {+    obj->streamGetDict()->lookup("PatternType", &obj1);+  } else {+    return NULL;+  }+  pattern = NULL;+  if (obj1.isInt() && obj1.getInt() == 1) {+    pattern = GfxTilingPattern::parse(obj);+  } else if (obj1.isInt() && obj1.getInt() == 2) {+    pattern = GfxShadingPattern::parse(obj);+  }+  obj1.free();+  return pattern;+}++//------------------------------------------------------------------------+// GfxTilingPattern+//------------------------------------------------------------------------++GfxTilingPattern *GfxTilingPattern::parse(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(-1, "Invalid or missing PaintType in pattern");+  }+  obj1.free();+  if (dict->lookup("TilingType", &obj1)->isInt()) {+    tilingTypeA = obj1.getInt();+  } else {+    tilingTypeA = 1;+    error(-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(-1, "Invalid or missing BBox in pattern");+  }+  obj1.free();+  if (dict->lookup("XStep", &obj1)->isNum()) {+    xStepA = obj1.getNum();+  } else {+    xStepA = 1;+    error(-1, "Invalid or missing XStep in pattern");+  }+  obj1.free();+  if (dict->lookup("YStep", &obj1)->isNum()) {+    yStepA = obj1.getNum();+  } else {+    yStepA = 1;+    error(-1, "Invalid or missing YStep in pattern");+  }+  obj1.free();+  if (!dict->lookup("Resources", &resDictA)->isDict()) {+    resDictA.free();+    resDictA.initNull();+    error(-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, patObj);+  resDictA.free();+  return pat;+}++GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,+				   double *bboxA, double xStepA, double yStepA,+				   Object *resDictA, double *matrixA,+				   Object *contentStreamA):+  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];+  }+  contentStreamA->copy(&contentStream);+}++GfxTilingPattern::~GfxTilingPattern() {+  resDict.free();+  contentStream.free();+}++GfxPattern *GfxTilingPattern::copy() {+  return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,+			      &resDict, matrix, &contentStream);+}++//------------------------------------------------------------------------+// 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(-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(-1, "Invalid Type 4 shading object");+      goto err1;+    }+    break;+  case 5:+    if (obj->isStream()) {+      shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream());+    } else {+      error(-1, "Invalid Type 5 shading object");+      goto err1;+    }+    break;+  case 6:+    if (obj->isStream()) {+      shading = GfxPatchMeshShading::parse(6, dict, obj->getStream());+    } else {+      error(-1, "Invalid Type 6 shading object");+      goto err1;+    }+    break;+  case 7:+    if (obj->isStream()) {+      shading = GfxPatchMeshShading::parse(7, dict, obj->getStream());+    } else {+      error(-1, "Invalid Type 7 shading object");+      goto err1;+    }+    break;+  default:+    error(-1, "Unimplemented shading type %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(-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(-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(-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();+    y0A = obj1.arrayGet(1, &obj2)->getNum();+    obj2.free();+    x1A = 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(-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))) {+	goto err2;+      }+      obj2.free();+    }+  } else {+    nFuncsA = 1;+    if (!(funcsA[0] = Function::parse(&obj1))) {+      goto err1;+    }+  }+  obj1.free();++  shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,+				   funcsA, nFuncsA);+  if (!shading->init(dict)) {+    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;+  y1 = 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(-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(-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))) {+	obj1.free();+	obj2.free();+	goto err1;+      }+      obj2.free();+    }+  } else {+    nFuncsA = 1;+    if (!(funcsA[0] = Function::parse(&obj1))) {+      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;+  }+  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;+  y1 = 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(-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(-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))) {+	obj1.free();+	obj2.free();+	goto err1;+      }+      obj2.free();+    }+  } else {+    nFuncsA = 1;+    if (!(funcsA[0] = Function::parse(&obj1))) {+      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;+  }+  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,+			       Function **funcsA, int nFuncsA):+  GfxShading(typeA)+{+  int i;++  vertices = verticesA;+  nVertices = nVerticesA;+  triangles = trianglesA;+  nTriangles = nTrianglesA;+  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));+  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 nComps, 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(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");+    goto err2;+  }+  obj1.free();+  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {+    compBits = obj1.getInt();+  } else {+    error(-1, "Missing or 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(-1, "Missing or invalid BitsPerFlag in shading dictionary");+      goto err2;+    }+    obj1.free();+  } else {+    if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {+      vertsPerRow = obj1.getInt();+    } else {+      error(-1, "Missing or invalid VerticesPerRow 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);+    }+    nComps = i;+  } else {+    error(-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(-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))) {+	  obj1.free();+	  obj2.free();+	  goto err1;+	}+	obj2.free();+      }+    } else {+      nFuncsA = 1;+      if (!(funcsA[0] = Function::parse(&obj1))) {+	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 < nComps; ++i) {+      if (!bitBuf->getBits(compBits, &c[i])) {+	break;+      }+    }+    if (i < nComps) {+      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 < nComps; ++i) {+      verticesA[nVerticesA].color.c[i] =+	  dblToCol(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,+					  funcsA, nFuncsA);+  if (!shading->init(dict)) {+    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, GfxColor *color0,+				    double *x1, double *y1, GfxColor *color1,+				    double *x2, double *y2, GfxColor *color2) {+  double in;+  double out[gfxColorMaxComps];+  int v, j;++  v = triangles[i][0];+  *x0 = vertices[v].x;+  *y0 = vertices[v].y;+  if (nFuncs > 0) {+    in = colToDbl(vertices[v].color.c[0]);+    for (j = 0; j < nFuncs; ++j) {+      funcs[j]->transform(&in, &out[j]);+    }+    for (j = 0; j < gfxColorMaxComps; ++j) {+      color0->c[j] = dblToCol(out[j]);+    }+  } else {+    *color0 = vertices[v].color;+  }+  v = triangles[i][1];+  *x1 = vertices[v].x;+  *y1 = vertices[v].y;+  if (nFuncs > 0) {+    in = colToDbl(vertices[v].color.c[0]);+    for (j = 0; j < nFuncs; ++j) {+      funcs[j]->transform(&in, &out[j]);+    }+    for (j = 0; j < gfxColorMaxComps; ++j) {+      color1->c[j] = dblToCol(out[j]);+    }+  } else {+    *color1 = vertices[v].color;+  }+  v = triangles[i][2];+  *x2 = vertices[v].x;+  *y2 = vertices[v].y;+  if (nFuncs > 0) {+    in = colToDbl(vertices[v].color.c[0]);+    for (j = 0; j < nFuncs; ++j) {+      funcs[j]->transform(&in, &out[j]);+    }+    for (j = 0; j < gfxColorMaxComps; ++j) {+      color2->c[j] = dblToCol(out[j]);+    }+  } else {+    *color2 = vertices[v].color;+  }+}++//------------------------------------------------------------------------+// GfxPatchMeshShading+//------------------------------------------------------------------------++GfxPatchMeshShading::GfxPatchMeshShading(int typeA,+					 GfxPatch *patchesA, int nPatchesA,+					 Function **funcsA, int nFuncsA):+  GfxShading(typeA)+{+  int i;++  patches = patchesA;+  nPatches = nPatchesA;+  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));+  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 nComps, nPatchesA, patchesSize, nPts, nColors;+  Guint flag;+  double x[16], y[16];+  Guint xi, yi;+  GfxColorComp c[4][gfxColorMaxComps];+  Guint ci[4];+  GfxShadingBitBuf *bitBuf;+  Object obj1, obj2;+  int i, j;++  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {+    coordBits = obj1.getInt();+  } else {+    error(-1, "Missing or invalid BitsPerCoordinate in shading dictionary");+    goto err2;+  }+  obj1.free();+  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {+    compBits = obj1.getInt();+  } else {+    error(-1, "Missing or invalid BitsPerComponent in shading dictionary");+    goto err2;+  }+  obj1.free();+  if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {+    flagBits = obj1.getInt();+  } else {+    error(-1, "Missing or 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);+    }+    nComps = i;+  } else {+    error(-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(-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))) {+	  obj1.free();+	  obj2.free();+	  goto err1;+	}+	obj2.free();+      }+    } else {+      nFuncsA = 1;+      if (!(funcsA[0] = Function::parse(&obj1))) {+	obj1.free();+	goto err1;+      }+    }+  } else {+    nFuncsA = 0;+  }+  obj1.free();++  nPatchesA = 0;+  patchesA = NULL;+  patchesSize = 0;+  bitBuf = new GfxShadingBitBuf(str);+  while (1) {+    if (!bitBuf->getBits(flagBits, &flag)) {+      break;+    }+    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 < nComps; ++j) {+	if (!bitBuf->getBits(compBits, &ci[j])) {+	  break;+	}+	c[i][j] = dblToCol(cMin[j] + cMul[j] * (double)ci[j]);+      }+      if (j < nComps) {+	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 < nComps; ++j) {+	  p->color[0][0].c[j] = c[0][j];+	  p->color[0][1].c[j] = c[1][j];+	  p->color[1][1].c[j] = c[2][j];+	  p->color[1][0].c[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 < nComps; ++j) {+	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];+	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];+	  p->color[1][1].c[j] = c[0][j];+	  p->color[1][0].c[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 < nComps; ++j) {+	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];+	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];+	  p->color[1][1].c[j] = c[0][j];+	  p->color[1][0].c[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 < nComps; ++j) {+	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];+	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];+	  p->color[1][1].c[j] = c[0][j];+	  p->color[1][0].c[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 < nComps; ++j) {+	  p->color[0][0].c[j] = c[0][j];+	  p->color[0][1].c[j] = c[1][j];+	  p->color[1][1].c[j] = c[2][j];+	  p->color[1][0].c[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 < nComps; ++j) {+	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];+	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];+	  p->color[1][1].c[j] = c[0][j];+	  p->color[1][0].c[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 < nComps; ++j) {+	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];+	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];+	  p->color[1][1].c[j] = c[0][j];+	  p->color[1][0].c[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 < nComps; ++j) {+	  p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];+	  p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];+	  p->color[1][1].c[j] = c[0][j];+	  p->color[1][0].c[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,+				    funcsA, nFuncsA);+  if (!shading->init(dict)) {+    delete shading;+    return NULL;+  }+  return shading;++ err2:+  obj1.free();+ err1:+  return NULL;+}++GfxShading *GfxPatchMeshShading::copy() {+  return new GfxPatchMeshShading(this);+}++//------------------------------------------------------------------------+// GfxImageColorMap+//------------------------------------------------------------------------++GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,+				   GfxColorSpace *colorSpaceA) {+  GfxIndexedColorSpace *indexedCS;+  GfxSeparationColorSpace *sepCS;+  int maxPixel, indexHigh;+  Guchar *lookup2;+  Function *sepFunc;+  Object obj;+  double x[gfxColorMaxComps];+  double y[gfxColorMaxComps];+  int i, j, k;++  ok = gTrue;++  // bits per component and color space+  bits = bitsA;+  maxPixel = (1 << bits) - 1;+  colorSpace = colorSpaceA;++  // initialize+  for (k = 0; k < gfxColorMaxComps; ++k) {+    lookup[k] = NULL;+  }++  // get decode map+  if (decode->isNull()) {+    nComps = colorSpace->getNComps();+    colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);+  } else if (decode->isArray()) {+    nComps = decode->arrayGetLength() / 2;+    if (nComps != colorSpace->getNComps()) {+      goto err1;+    }+    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.+  //+  // Optimization: for Indexed and Separation color spaces (which have+  // only one component), we store color values in the lookup table+  // rather than component 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();+    lookup2 = indexedCS->getLookup();+    colorSpace2->getDefaultRanges(x, y, indexHigh);+    for (k = 0; k < nComps2; ++k) {+      lookup[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;+	}+	lookup[k][i] =+	    dblToCol(x[k] + (lookup2[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) {+      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,+					   sizeof(GfxColorComp));+      for (i = 0; i <= maxPixel; ++i) {+	x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;+	sepFunc->transform(x, y);+	lookup[k][i] = dblToCol(y[k]);+      }+    }+  } else {+    for (k = 0; k < nComps; ++k) {+      lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,+					   sizeof(GfxColorComp));+      for (i = 0; i <= maxPixel; ++i) {+	lookup[k][i] = dblToCol(decodeLow[k] ++				(i * decodeRange[k]) / maxPixel);+      }+    }+  }++  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;+  }+  n = 1 << bits;+  if (colorSpace->getMode() == csIndexed) {+    colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();+    for (k = 0; k < nComps2; ++k) {+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));+    }+  } else if (colorSpace->getMode() == csSeparation) {+    colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();+    for (k = 0; k < nComps2; ++k) {+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));+      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));+    }+  } else {+    for (k = 0; k < nComps; ++k) {+      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));+      memcpy(lookup[k], colorMap->lookup[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]);+  }+}++void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {+  GfxColor color;+  int i;++  if (colorSpace2) {+    for (i = 0; i < nComps2; ++i) {+      color.c[i] = lookup[i][x[0]];+    }+    colorSpace2->getGray(&color, gray);+  } else {+    for (i = 0; i < nComps; ++i) {+      color.c[i] = lookup[i][x[i]];+    }+    colorSpace->getGray(&color, gray);+  }+}++void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {+  GfxColor color;+  int i;++  if (colorSpace2) {+    for (i = 0; i < nComps2; ++i) {+      color.c[i] = lookup[i][x[0]];+    }+    colorSpace2->getRGB(&color, rgb);+  } else {+    for (i = 0; i < nComps; ++i) {+      color.c[i] = lookup[i][x[i]];+    }+    colorSpace->getRGB(&color, rgb);+  }+}++void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {+  GfxColor color;+  int i;++  if (colorSpace2) {+    for (i = 0; i < nComps2; ++i) {+      color.c[i] = lookup[i][x[0]];+    }+    colorSpace2->getCMYK(&color, cmyk);+  } else {+    for (i = 0; i < nComps; ++i) {+      color.c[i] = lookup[i][x[i]];+    }+    colorSpace->getCMYK(&color, cmyk);+  }+}++void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {+  int maxPixel, i;++  maxPixel = (1 << bits) - 1;+  for (i = 0; i < nComps; ++i) {+    color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);+  }+}++//------------------------------------------------------------------------+// 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 += 16;+    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 += 16;+    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) {+    if (n >= size) {+      size += 16;+      subpaths = (GfxSubpath **)+	           greallocn(subpaths, size, sizeof(GfxSubpath *));+    }+    subpaths[n] = new GfxSubpath(firstX, firstY);+    ++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) {+    if (n >= size) {+      size += 16;+      subpaths = (GfxSubpath **)+	           greallocn(subpaths, size, sizeof(GfxSubpath *));+    }+    subpaths[n] = new GfxSubpath(firstX, firstY);+    ++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 += 16;+      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 = new GfxDeviceGrayColorSpace();+  strokeColorSpace = new GfxDeviceGrayColorSpace();+  fillColor.c[0] = 0;+  strokeColor.c[0] = 0;+  fillPattern = NULL;+  strokePattern = NULL;+  blendMode = gfxBlendNormal;+  fillOpacity = 1;+  strokeOpacity = 1;+  fillOverprint = gFalse;+  strokeOverprint = gFalse;+  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;++  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;+  }+  if (saved) {+    delete saved;+  }+}++// Used for copy();+GfxState::GfxState(GfxState *state) {+  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));+  }+  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;+  *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;+  *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::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;+  }+}
+ xpdf/GfxState.h view
@@ -0,0 +1,1244 @@+//========================================================================+//+// GfxState.h+//+// Copyright 1996-2003 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 GfxShading;++//------------------------------------------------------------------------+// GfxBlendMode+//------------------------------------------------------------------------++enum GfxBlendMode {+  gfxBlendNormal,+  gfxBlendMultiply,+  gfxBlendScreen,+  gfxBlendOverlay,+  gfxBlendDarken,+  gfxBlendLighten,+  gfxBlendColorDodge,+  gfxBlendColorBurn,+  gfxBlendHardLight,+  gfxBlendSoftLight,+  gfxBlendDifference,+  gfxBlendExclusion,+  gfxBlendHue,+  gfxBlendSaturation,+  gfxBlendColor,+  gfxBlendLuminosity+};++//------------------------------------------------------------------------+// 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) + ...) << 16+  //                  =  (x << 8) + (x) + (x >> 7)+  //                                      [for rounding]+  return (GfxColorComp)((x << 8) + x + (x >> 7));+}++static inline Guchar colToByte(GfxColorComp x) {+  // 255 * x + 0.5  =  256 * x - x + 0x8000+  return (Guchar)(((x << 8) - 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);++  // Convert to gray, RGB, or CMYK.+  virtual void getGray(GfxColor *color, GfxGray *gray) = 0;+  virtual void getRGB(GfxColor *color, GfxRGB *rgb) = 0;+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk) = 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 number of color space modes+  static int getNumColorSpaceModes();++  // Return the name of the <idx>th color space mode.+  static char *getColorSpaceModeName(int idx);++private:+};++//------------------------------------------------------------------------+// GfxDeviceGrayColorSpace+//------------------------------------------------------------------------++class GfxDeviceGrayColorSpace: public GfxColorSpace {+public:++  GfxDeviceGrayColorSpace();+  virtual ~GfxDeviceGrayColorSpace();+  virtual GfxColorSpace *copy();+  virtual GfxColorSpaceMode getMode() { return csDeviceGray; }++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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; }++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 a Lab color space.  Returns NULL if unsuccessful.+  static GfxColorSpace *parse(Array *arr);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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:++  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, GfxColorSpace *alt, Function *func);+  virtual ~GfxDeviceNColorSpace();+  virtual GfxColorSpace *copy();+  virtual GfxColorSpaceMode getMode() { return csDeviceN; }++  // Construct a DeviceN color space.  Returns NULL if unsuccessful.+  static GfxColorSpace *parse(Array *arr);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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; }++private:++  int nComps;			// number of components+  GString			// colorant names+    *names[gfxColorMaxComps];+  GfxColorSpace *alt;		// alternate color space+  Function *func;		// tint transform (into alternate color space)+  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);++  virtual void getGray(GfxColor *color, GfxGray *gray);+  virtual void getRGB(GfxColor *color, GfxRGB *rgb);+  virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk);++  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 *obj);++  virtual GfxPattern *copy() = 0;++  int getType() { return type; }++private:++  int type;+};++//------------------------------------------------------------------------+// GfxTilingPattern+//------------------------------------------------------------------------++class GfxTilingPattern: public GfxPattern {+public:++  static GfxTilingPattern *parse(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 *getContentStream() { return &contentStream; }++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 contentStream;+};++//------------------------------------------------------------------------+// 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;+  GfxColor color;+};++class GfxGouraudTriangleShading: public GfxShading {+public:++  GfxGouraudTriangleShading(int typeA,+			    GfxGouraudVertex *verticesA, int nVerticesA,+			    int (*trianglesA)[3], int nTrianglesA,+			    Function **funcsA, int nFuncsA);+  GfxGouraudTriangleShading(GfxGouraudTriangleShading *shading);+  virtual ~GfxGouraudTriangleShading();++  static GfxGouraudTriangleShading *parse(int typeA, Dict *dict, Stream *str);++  virtual GfxShading *copy();++  int getNTriangles() { return nTriangles; }+  void getTriangle(int i, double *x0, double *y0, GfxColor *color0,+		   double *x1, double *y1, GfxColor *color1,+		   double *x2, double *y2, GfxColor *color2);++private:++  GfxGouraudVertex *vertices;+  int nVertices;+  int (*triangles)[3];+  int nTriangles;+  Function *funcs[gfxColorMaxComps];+  int nFuncs;+};++//------------------------------------------------------------------------+// GfxPatchMeshShading+//------------------------------------------------------------------------++struct GfxPatch {+  double x[4][4];+  double y[4][4];+  GfxColor color[2][2];+};++class GfxPatchMeshShading: public GfxShading {+public:++  GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA,+		      Function **funcsA, int nFuncsA);+  GfxPatchMeshShading(GfxPatchMeshShading *shading);+  virtual ~GfxPatchMeshShading();++  static GfxPatchMeshShading *parse(int typeA, Dict *dict, Stream *str);++  virtual GfxShading *copy();++  int getNPatches() { return nPatches; }+  GfxPatch *getPatch(int i) { return &patches[i]; }++private:++  GfxPatch *patches;+  int nPatches;+  Function *funcs[gfxColorMaxComps];+  int nFuncs;+};++//------------------------------------------------------------------------+// GfxImageColorMap+//------------------------------------------------------------------------++class GfxImageColorMap {+public:++  // Constructor.+  GfxImageColorMap(int bitsA, Object *decode, GfxColorSpace *colorSpaceA);++  // 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);+  void getRGB(Guchar *x, GfxRGB *rgb);+  void getCMYK(Guchar *x, GfxCMYK *cmyk);+  void getColor(Guchar *x, GfxColor *color);++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];+  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() { return new GfxState(this); }++  // 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); }+  void getStrokeGray(GfxGray *gray)+    { strokeColorSpace->getGray(&strokeColor, gray); }+  void getFillRGB(GfxRGB *rgb)+    { fillColorSpace->getRGB(&fillColor, rgb); }+  void getStrokeRGB(GfxRGB *rgb)+    { strokeColorSpace->getRGB(&strokeColor, rgb); }+  void getFillCMYK(GfxCMYK *cmyk)+    { fillColorSpace->getCMYK(&fillColor, cmyk); }+  void getStrokeCMYK(GfxCMYK *cmyk)+    { strokeColorSpace->getCMYK(&strokeColor, cmyk); }+  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; }+  Function **getTransfer() { return transfer; }+  double getLineWidth() { return lineWidth; }+  void getLineDash(double **dash, int *length, double *start)+    { *dash = lineDash; *length = lineDashLength; *start = lineDashStart; }+  int 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; }++  // 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 setTransfer(Function **funcs);+  void setLineWidth(double width) { lineWidth = width; }+  void setLineDash(double *dash, int length, double start);+  void setFlatness(int 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();++  // 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);++  // 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+  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;+  int 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;++  GfxState *saved;		// next GfxState on stack++  GfxState(GfxState *state);+};++#endif
+ xpdf/GlobalParams.cc view
@@ -0,0 +1,2908 @@+//========================================================================+//+// GlobalParams.cc+//+// Copyright 2001-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <string.h>+#include <stdio.h>+#include <ctype.h>+#ifdef ENABLE_PLUGINS+#  ifndef WIN32+#    include <dlfcn.h>+#  endif+#endif+#ifdef WIN32+#  include <shlobj.h>+#endif+#if HAVE_PAPER_H+#include <paper.h>+#endif+#include "gmem.h"+#include "GString.h"+#include "GList.h"+#include "GHash.h"+#include "gfile.h"+#include "Error.h"+#include "NameToCharCode.h"+#include "CharCodeToUnicode.h"+#include "UnicodeMap.h"+#include "CMap.h"+#include "BuiltinFontTables.h"+#include "FontEncodingTables.h"+#ifdef ENABLE_PLUGINS+#  include "XpdfPluginAPI.h"+#endif+#include "GlobalParams.h"++#ifdef WIN32+#  define strcasecmp stricmp+#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"++#ifdef ENABLE_PLUGINS+#  ifdef WIN32+extern XpdfPluginVecTable xpdfPluginVecTable;+#  endif+#endif++//------------------------------------------------------------------------++#define cidToUnicodeCacheSize     4+#define unicodeToUnicodeCacheSize 4++//------------------------------------------------------------------------++static struct {+  char *name;+  char *t1FileName;+  char *ttFileName;+} displayFontTab[] = {+  {"Courier",               "n022003l.pfb", "cour.ttf"},+  {"Courier-Bold",          "n022004l.pfb", "courbd.ttf"},+  {"Courier-BoldOblique",   "n022024l.pfb", "courbi.ttf"},+  {"Courier-Oblique",       "n022023l.pfb", "couri.ttf"},+  {"Helvetica",             "n019003l.pfb", "arial.ttf"},+  {"Helvetica-Bold",        "n019004l.pfb", "arialbd.ttf"},+  {"Helvetica-BoldOblique", "n019024l.pfb", "arialbi.ttf"},+  {"Helvetica-Oblique",     "n019023l.pfb", "ariali.ttf"},+  {"Symbol",                "s050000l.pfb", NULL},+  {"Times-Bold",            "n021004l.pfb", "timesbd.ttf"},+  {"Times-BoldItalic",      "n021024l.pfb", "timesbi.ttf"},+  {"Times-Italic",          "n021023l.pfb", "timesi.ttf"},+  {"Times-Roman",           "n021003l.pfb", "times.ttf"},+  {"ZapfDingbats",          "d050000l.pfb", NULL},+  {NULL}+};++#ifdef WIN32+static char *displayFontDirs[] = {+  "c:/windows/fonts",+  "c:/winnt/fonts",+  NULL+};+#else+static 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",+  NULL+};+#endif++//------------------------------------------------------------------------++GlobalParams *globalParams = NULL;++//------------------------------------------------------------------------+// DisplayFontParam+//------------------------------------------------------------------------++DisplayFontParam::DisplayFontParam(GString *nameA,+				   DisplayFontParamKind kindA) {+  name = nameA;+  kind = kindA;+  switch (kind) {+  case displayFontT1:+    t1.fileName = NULL;+    break;+  case displayFontTT:+    tt.fileName = NULL;+    break;+  }+}++DisplayFontParam::~DisplayFontParam() {+  delete name;+  switch (kind) {+  case displayFontT1:+    if (t1.fileName) {+      delete t1.fileName;+    }+    break;+  case displayFontTT:+    if (tt.fileName) {+      delete tt.fileName;+    }+    break;+  }+}++#ifdef WIN32++//------------------------------------------------------------------------+// WinFontInfo+//------------------------------------------------------------------------++class WinFontInfo: public DisplayFontParam {+public:++  GBool bold, italic;++  static WinFontInfo *make(GString *nameA, GBool boldA, GBool italicA,+			   HKEY regKey, char *winFontDir);+  WinFontInfo(GString *nameA, GBool boldA, GBool italicA,+	      GString *fileNameA);+  virtual ~WinFontInfo();+  GBool equals(WinFontInfo *fi);+};++WinFontInfo *WinFontInfo::make(GString *nameA, GBool boldA, GBool italicA,+			       HKEY regKey, char *winFontDir) {+  GString *regName;+  GString *fileNameA;+  char buf[MAX_PATH];+  DWORD n;+  char c;+  int i;++  //----- find the font file+  fileNameA = NULL;+  regName = nameA->copy();+  if (boldA) {+    regName->append(" Bold");+  }+  if (italicA) {+    regName->append(" Italic");+  }+  regName->append(" (TrueType)");+  n = sizeof(buf);+  if (RegQueryValueEx(regKey, regName->getCString(), NULL, NULL,+		      (LPBYTE)buf, &n) == ERROR_SUCCESS) {+    fileNameA = new GString(winFontDir);+    fileNameA->append('\\')->append(buf);+  }+  delete regName;+  if (!fileNameA) {+    delete nameA;+    return NULL;+  }++  //----- normalize the font name+  i = 0;+  while (i < nameA->getLength()) {+    c = nameA->getChar(i);+    if (c == ' ' || c == ',' || c == '-') {+      nameA->del(i);+    } else {+      ++i;+    }+  }++  return new WinFontInfo(nameA, boldA, italicA, fileNameA);+}++WinFontInfo::WinFontInfo(GString *nameA, GBool boldA, GBool italicA,+			 GString *fileNameA):+  DisplayFontParam(nameA, displayFontTT)+{+  bold = boldA;+  italic = italicA;+  tt.fileName = fileNameA;+}++WinFontInfo::~WinFontInfo() {+}++GBool WinFontInfo::equals(WinFontInfo *fi) {+  return !name->cmp(fi->name) && bold == fi->bold && italic == fi->italic;+}++//------------------------------------------------------------------------+// WinFontList+//------------------------------------------------------------------------++class WinFontList {+public:++  WinFontList(char *winFontDirA);+  ~WinFontList();+  WinFontInfo *find(GString *font);++private:++  void add(WinFontInfo *fi);+  static int CALLBACK enumFunc1(CONST LOGFONT *font,+				CONST TEXTMETRIC *metrics,+				DWORD type, LPARAM data);+  static int CALLBACK enumFunc2(CONST LOGFONT *font,+				CONST TEXTMETRIC *metrics,+				DWORD type, LPARAM data);++  GList *fonts;			// [WinFontInfo]+  HDC dc;			// (only used during enumeration)+  HKEY regKey;			// (only used during enumeration)+  char *winFontDir;		// (only used during enumeration)+};++WinFontList::WinFontList(char *winFontDirA) {+  OSVERSIONINFO version;+  char *path;++  fonts = new GList();+  dc = GetDC(NULL);+  winFontDir = winFontDirA;+  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 (RegOpenKeyEx(HKEY_LOCAL_MACHINE, path, 0,+		   KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,+		   &regKey) == ERROR_SUCCESS) {+    EnumFonts(dc, NULL, &WinFontList::enumFunc1, (LPARAM)this);+    RegCloseKey(regKey);+  }+  ReleaseDC(NULL, dc);+}++WinFontList::~WinFontList() {+  deleteGList(fonts, WinFontInfo);+}++void WinFontList::add(WinFontInfo *fi) {+  int i;++  for (i = 0; i < fonts->getLength(); ++i) {+    if (((WinFontInfo *)fonts->get(i))->equals(fi)) {+      delete fi;+      return;+    }+  }+  fonts->append(fi);+}++WinFontInfo *WinFontList::find(GString *font) {+  GString *name;+  GBool bold, italic;+  WinFontInfo *fi;+  char c;+  int n, i;++  name = font->copy();++  // remove space, comma, dash chars+  i = 0;+  while (i < name->getLength()) {+    c = name->getChar(i);+    if (c == ' ' || c == ',' || c == '-') {+      name->del(i);+    } else {+      ++i;+    }+  }+  n = name->getLength();++  // remove trailing "MT" (Foo-MT, Foo-BoldMT, etc.)+  if (!strcmp(name->getCString() + n - 2, "MT")) {+    name->del(n - 2, 2);+    n -= 2;+  }++  // look for "Italic"+  if (!strcmp(name->getCString() + n - 6, "Italic")) {+    name->del(n - 6, 6);+    italic = gTrue;+    n -= 6;+  } else {+    italic = gFalse;+  }++  // look for "Bold"+  if (!strcmp(name->getCString() + n - 4, "Bold")) {+    name->del(n - 4, 4);+    bold = gTrue;+    n -= 4;+  } else {+    bold = gFalse;+  }++  // remove trailing "MT" (FooMT-Bold, etc.)+  if (!strcmp(name->getCString() + n - 2, "MT")) {+    name->del(n - 2, 2);+    n -= 2;+  }++  // remove trailing "PS"+  if (!strcmp(name->getCString() + n - 2, "PS")) {+    name->del(n - 2, 2);+    n -= 2;+  }++  // search for the font+  fi = NULL;+  for (i = 0; i < fonts->getLength(); ++i) {+    fi = (WinFontInfo *)fonts->get(i);+    if (!fi->name->cmp(name) && fi->bold == bold && fi->italic == italic) {+      break;+    }+    fi = NULL;+  }++  delete name;+  return fi;+}++int CALLBACK WinFontList::enumFunc1(CONST LOGFONT *font,+				    CONST TEXTMETRIC *metrics,+				    DWORD type, LPARAM data) {+  WinFontList *fl = (WinFontList *)data;++  EnumFonts(fl->dc, font->lfFaceName, &WinFontList::enumFunc2, (LPARAM)fl);+  return 1;+}++int CALLBACK WinFontList::enumFunc2(CONST LOGFONT *font,+				    CONST TEXTMETRIC *metrics,+				    DWORD type, LPARAM data) {+  WinFontList *fl = (WinFontList *)data;+  WinFontInfo *fi;++  if (type & TRUETYPE_FONTTYPE) {+    if ((fi = WinFontInfo::make(new GString(font->lfFaceName),+				font->lfWeight >= 600,+				font->lfItalic ? gTrue : gFalse,+				fl->regKey, fl->winFontDir))) {+      fl->add(fi);+    }+  }+  return 1;+}++#endif // WIN32++//------------------------------------------------------------------------+// PSFontParam+//------------------------------------------------------------------------++PSFontParam::PSFontParam(GString *pdfFontNameA, int wModeA,+			 GString *psFontNameA, GString *encodingA) {+  pdfFontName = pdfFontNameA;+  wMode = wModeA;+  psFontName = psFontNameA;+  encoding = encodingA;+}++PSFontParam::~PSFontParam() {+  delete pdfFontName;+  delete psFontName;+  if (encoding) {+    delete encoding;+  }+}++//------------------------------------------------------------------------+// KeyBinding+//------------------------------------------------------------------------++KeyBinding::KeyBinding(int codeA, int modsA, int contextA, char *cmd0) {+  code = codeA;+  mods = modsA;+  context = contextA;+  cmds = new GList();+  cmds->append(new GString(cmd0));+}++KeyBinding::KeyBinding(int codeA, int modsA, int contextA,+		       char *cmd0, 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);+}++#ifdef ENABLE_PLUGINS+//------------------------------------------------------------------------+// Plugin+//------------------------------------------------------------------------++class Plugin {+public:++  static Plugin *load(char *type, char *name);+  ~Plugin();++private:++#ifdef WIN32+  Plugin(HMODULE libA);+  HMODULE lib;+#else+  Plugin(void *dlA);+  void *dl;+#endif+};++Plugin *Plugin::load(char *type, char *name) {+  GString *path;+  Plugin *plugin;+  XpdfPluginVecTable *vt;+  XpdfBool (*xpdfInitPlugin)(void);+#ifdef WIN32+  HMODULE libA;+#else+  void *dlA;+#endif++  path = globalParams->getBaseDir();+  appendToPath(path, "plugins");+  appendToPath(path, type);+  appendToPath(path, name);++#ifdef WIN32+  path->append(".dll");+  if (!(libA = LoadLibrary(path->getCString()))) {+    error(-1, "Failed to load plugin '%s'",+	  path->getCString());+    goto err1;+  }+  if (!(vt = (XpdfPluginVecTable *)+	         GetProcAddress(libA, "xpdfPluginVecTable"))) {+    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",+	  path->getCString());+    goto err2;+  }+#else+  //~ need to deal with other extensions here+  path->append(".so");+  if (!(dlA = dlopen(path->getCString(), RTLD_NOW))) {+    error(-1, "Failed to load plugin '%s': %s",+	  path->getCString(), dlerror());+    goto err1;+  }+  if (!(vt = (XpdfPluginVecTable *)dlsym(dlA, "xpdfPluginVecTable"))) {+    error(-1, "Failed to find xpdfPluginVecTable in plugin '%s'",+	  path->getCString());+    goto err2;+  }+#endif++  if (vt->version != xpdfPluginVecTable.version) {+    error(-1, "Plugin '%s' is wrong version", path->getCString());+    goto err2;+  }+  memcpy(vt, &xpdfPluginVecTable, sizeof(xpdfPluginVecTable));++#ifdef WIN32+  if (!(xpdfInitPlugin = (XpdfBool (*)(void))+	                     GetProcAddress(libA, "xpdfInitPlugin"))) {+    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",+	  path->getCString());+    goto err2;+  }+#else+  if (!(xpdfInitPlugin = (XpdfBool (*)(void))dlsym(dlA, "xpdfInitPlugin"))) {+    error(-1, "Failed to find xpdfInitPlugin in plugin '%s'",+	  path->getCString());+    goto err2;+  }+#endif++  if (!(*xpdfInitPlugin)()) {+    error(-1, "Initialization of plugin '%s' failed",+	  path->getCString());+    goto err2;+  }++#ifdef WIN32+  plugin = new Plugin(libA);+#else+  plugin = new Plugin(dlA);+#endif++  delete path;+  return plugin;++ err2:+#ifdef WIN32+  FreeLibrary(libA);+#else+  dlclose(dlA);+#endif+ err1:+  delete path;+  return NULL;+}++#ifdef WIN32+Plugin::Plugin(HMODULE libA) {+  lib = libA;+}+#else+Plugin::Plugin(void *dlA) {+  dl = dlA;+}+#endif++Plugin::~Plugin() {+  void (*xpdfFreePlugin)(void);++#ifdef WIN32+  if ((xpdfFreePlugin = (void (*)(void))+                            GetProcAddress(lib, "xpdfFreePlugin"))) {+    (*xpdfFreePlugin)();+  }+  FreeLibrary(lib);+#else+  if ((xpdfFreePlugin = (void (*)(void))dlsym(dl, "xpdfFreePlugin"))) {+    (*xpdfFreePlugin)();+  }+  dlclose(dl);+#endif+}++#endif // ENABLE_PLUGINS++//------------------------------------------------------------------------+// parsing+//------------------------------------------------------------------------++GlobalParams::GlobalParams(char *cfgFileName) {+  UnicodeMap *map;+  GString *fileName;+  FILE *f;+  int i;++#if MULTITHREADED+  gInitMutex(&mutex);+  gInitMutex(&unicodeMapCacheMutex);+  gInitMutex(&cMapCacheMutex);+#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+  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();+  displayFonts = new GHash();+  displayCIDFonts = new GHash();+  displayNamedCIDFonts = new GHash();+#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(-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;+  psExpandSmaller = gFalse;+  psShrinkLarger = gTrue;+  psCenter = gTrue;+  psDuplex = gFalse;+  psLevel = psLevel2;+  psFile = NULL;+  psFonts = new GHash();+  psNamedFonts16 = new GList();+  psFonts16 = new GList();+  psEmbedType1 = gTrue;+  psEmbedTrueType = gTrue;+  psEmbedCIDPostScript = gTrue;+  psEmbedCIDTrueType = gTrue;+  psPreload = gFalse;+  psOPI = gFalse;+  psASCIIHex = gFalse;+  textEncoding = new GString("Latin1");+#if defined(WIN32)+  textEOL = eolDOS;+#elif defined(MACOS)+  textEOL = eolMac;+#else+  textEOL = eolUnix;+#endif+  textPageBreaks = gTrue;+  textKeepTinyChars = gFalse;+  fontDirs = new GList();+  initialZoom = new GString("125");+  continuousView = gFalse;+  enableT1lib = gTrue;+  enableFreeType = gTrue;+  antialias = gTrue;+  vectorAntialias = gTrue;+  strokeAdjust = gTrue;+  screenType = screenUnset;+  screenSize = -1;+  screenDotRadius = -1;+  screenGamma = 1.0;+  screenBlackThreshold = 0.0;+  screenWhiteThreshold = 1.0;+  urlCommand = NULL;+  movieCommand = NULL;+  mapNumericCharNames = gTrue;+  mapUnknownCharNames = gFalse;+  createDefaultKeyBindings();+  printCommands = gFalse;+  errQuiet = gFalse;++  cidToUnicodeCache = new CharCodeToUnicodeCache(cidToUnicodeCacheSize);+  unicodeToUnicodeCache =+      new CharCodeToUnicodeCache(unicodeToUnicodeCacheSize);+  unicodeMapCache = new UnicodeMapCache();+  cMapCache = new CMapCache();++#ifdef WIN32+  winFontList = NULL;+#endif++#ifdef ENABLE_PLUGINS+  plugins = new GList();+  securityHandlers = new GList();+#endif++  // 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]) {+    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) {+#if defined(WIN32) && !defined(__CYGWIN32__)+    char buf[512];+    i = GetModuleFileName(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);+  }+}++void GlobalParams::createDefaultKeyBindings() {+  keyBindings = new GList();++  //----- mouse buttons+  keyBindings->append(new KeyBinding(xpdfKeyCodeMousePress1, xpdfKeyModNone,+				     xpdfKeyContextAny, "startSelection"));+  keyBindings->append(new KeyBinding(xpdfKeyCodeMouseRelease1, xpdfKeyModNone,+				     xpdfKeyContextAny, "endSelection",+				     "followLinkNoSel"));+  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)"));++  //----- 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"));+  keyBindings->append(new KeyBinding(xpdfKeyCodePgUp, xpdfKeyModNone,+				     xpdfKeyContextAny, "pageUp"));+  keyBindings->append(new KeyBinding(xpdfKeyCodeBackspace, xpdfKeyModNone,+				     xpdfKeyContextAny, "pageUp"));+  keyBindings->append(new KeyBinding(xpdfKeyCodeDelete, xpdfKeyModNone,+				     xpdfKeyContextAny, "pageUp"));+  keyBindings->append(new KeyBinding(xpdfKeyCodePgDn, xpdfKeyModNone,+				     xpdfKeyContextAny, "pageDown"));+  keyBindings->append(new KeyBinding(' ', xpdfKeyModNone,+				     xpdfKeyContextAny, "pageDown"));+  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('o', xpdfKeyModNone,+				     xpdfKeyContextAny, "open"));+  keyBindings->append(new KeyBinding('O', xpdfKeyModNone,+				     xpdfKeyContextAny, "open"));+  keyBindings->append(new KeyBinding('r', xpdfKeyModNone,+				     xpdfKeyContextAny, "reload"));+  keyBindings->append(new KeyBinding('R', xpdfKeyModNone,+				     xpdfKeyContextAny, "reload"));+  keyBindings->append(new KeyBinding('f', xpdfKeyModNone,+				     xpdfKeyContextAny, "find"));+  keyBindings->append(new KeyBinding('F', xpdfKeyModNone,+				     xpdfKeyContextAny, "find"));+  keyBindings->append(new KeyBinding('f', xpdfKeyModCtrl,+				     xpdfKeyContextAny, "find"));+  keyBindings->append(new KeyBinding('g', xpdfKeyModCtrl,+				     xpdfKeyContextAny, "findNext"));+  keyBindings->append(new KeyBinding('p', xpdfKeyModCtrl,+				     xpdfKeyContextAny, "print"));+  keyBindings->append(new KeyBinding('n', xpdfKeyModNone,+				     xpdfKeyContextScrLockOff, "nextPage"));+  keyBindings->append(new KeyBinding('N', xpdfKeyModNone,+				     xpdfKeyContextScrLockOff, "nextPage"));+  keyBindings->append(new KeyBinding('n', xpdfKeyModNone,+				     xpdfKeyContextScrLockOn,+				     "nextPageNoScroll"));+  keyBindings->append(new KeyBinding('N', xpdfKeyModNone,+				     xpdfKeyContextScrLockOn,+				     "nextPageNoScroll"));+  keyBindings->append(new KeyBinding('p', xpdfKeyModNone,+				     xpdfKeyContextScrLockOff, "prevPage"));+  keyBindings->append(new KeyBinding('P', xpdfKeyModNone,+				     xpdfKeyContextScrLockOff, "prevPage"));+  keyBindings->append(new KeyBinding('p', xpdfKeyModNone,+				     xpdfKeyContextScrLockOn,+				     "prevPageNoScroll"));+  keyBindings->append(new KeyBinding('P', xpdfKeyModNone,+				     xpdfKeyContextScrLockOn,+				     "prevPageNoScroll"));+  keyBindings->append(new KeyBinding('v', xpdfKeyModNone,+				     xpdfKeyContextAny, "goForward"));+  keyBindings->append(new KeyBinding('b', xpdfKeyModNone,+				     xpdfKeyContextAny, "goBackward"));+  keyBindings->append(new KeyBinding('g', xpdfKeyModNone,+				     xpdfKeyContextAny, "focusToPageNum"));+  keyBindings->append(new KeyBinding('0', xpdfKeyModNone,+				     xpdfKeyContextAny, "zoomPercent(125)"));+  keyBindings->append(new KeyBinding('+', xpdfKeyModNone,+				     xpdfKeyContextAny, "zoomIn"));+  keyBindings->append(new KeyBinding('-', xpdfKeyModNone,+				     xpdfKeyContextAny, "zoomOut"));+  keyBindings->append(new KeyBinding('z', xpdfKeyModNone,+				     xpdfKeyContextAny, "zoomFitPage"));+  keyBindings->append(new KeyBinding('w', xpdfKeyModNone,+				     xpdfKeyContextAny, "zoomFitWidth"));+  keyBindings->append(new KeyBinding('f', xpdfKeyModAlt,+				     xpdfKeyContextAny,+				     "toggleFullScreenMode"));+  keyBindings->append(new KeyBinding('l', xpdfKeyModCtrl,+				     xpdfKeyContextAny, "redraw"));+  keyBindings->append(new KeyBinding('w', xpdfKeyModCtrl,+				     xpdfKeyContextAny, "closeWindow"));+  keyBindings->append(new KeyBinding('?', xpdfKeyModNone,+				     xpdfKeyContextAny, "about"));+  keyBindings->append(new KeyBinding('q', xpdfKeyModNone,+				     xpdfKeyContextAny, "quit"));+  keyBindings->append(new KeyBinding('Q', xpdfKeyModNone,+				     xpdfKeyContextAny, "quit"));+}++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;+  char *p1, *p2;+  FILE *f2;++  // break the line into tokens+  tokens = new GList();+  p1 = buf;+  while (*p1) {+    for (; *p1 && isspace(*p1); ++p1) ;+    if (!*p1) {+      break;+    }+    if (*p1 == '"' || *p1 == '\'') {+      for (p2 = p1 + 1; *p2 && *p2 != *p1; ++p2) ;+      ++p1;+    } else {+      for (p2 = p1 + 1; *p2 && !isspace(*p2); ++p2) ;+    }+    tokens->append(new GString(p1, p2 - p1));+    p1 = *p2 ? p2 + 1 : p2;+  }++  // 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 = fopen(incFile->getCString(), "r"))) {+	  parseFile(incFile, f2);+	  fclose(f2);+	} else {+	  error(-1, "Couldn't find included config file: '%s' (%s:%d)",+		incFile->getCString(), fileName->getCString(), line);+	}+      } else {+	error(-1, "Bad 'include' config file command (%s:%d)",+	      fileName->getCString(), 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("displayFontT1")) {+      parseDisplayFont(tokens, displayFonts, displayFontT1, fileName, line);+    } else if (!cmd->cmp("displayFontTT")) {+      parseDisplayFont(tokens, displayFonts, displayFontTT, fileName, line);+    } else if (!cmd->cmp("displayNamedCIDFontT1")) {+      parseDisplayFont(tokens, displayNamedCIDFonts,+		       displayFontT1, fileName, line);+    } else if (!cmd->cmp("displayCIDFontT1")) {+      parseDisplayFont(tokens, displayCIDFonts,+		       displayFontT1, fileName, line);+    } else if (!cmd->cmp("displayNamedCIDFontTT")) {+      parseDisplayFont(tokens, displayNamedCIDFonts,+		       displayFontTT, fileName, line);+    } else if (!cmd->cmp("displayCIDFontTT")) {+      parseDisplayFont(tokens, displayCIDFonts,+		       displayFontTT, fileName, line);+    } else if (!cmd->cmp("psFile")) {+      parsePSFile(tokens, fileName, line);+    } else if (!cmd->cmp("psFont")) {+      parsePSFont(tokens, fileName, line);+    } else if (!cmd->cmp("psNamedFont16")) {+      parsePSFont16("psNamedFont16", psNamedFonts16,+		    tokens, fileName, line);+    } else if (!cmd->cmp("psFont16")) {+      parsePSFont16("psFont16", psFonts16, 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("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("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("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("textEncoding")) {+      parseTextEncoding(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("fontDir")) {+      parseFontDir(tokens, fileName, line);+    } else if (!cmd->cmp("initialZoom")) {+      parseInitialZoom(tokens, fileName, line);+    } else if (!cmd->cmp("continuousView")) {+      parseYesNo("continuousView", &continuousView, tokens, fileName, line);+    } else if (!cmd->cmp("enableT1lib")) {+      parseYesNo("enableT1lib", &enableT1lib, tokens, fileName, line);+    } else if (!cmd->cmp("enableFreeType")) {+      parseYesNo("enableFreeType", &enableFreeType, 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("strokeAdjust")) {+      parseYesNo("strokeAdjust", &strokeAdjust, 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("urlCommand")) {+      parseCommand("urlCommand", &urlCommand, tokens, fileName, line);+    } else if (!cmd->cmp("movieCommand")) {+      parseCommand("movieCommand", &movieCommand, 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("bind")) {+      parseBind(tokens, fileName, line);+    } else if (!cmd->cmp("unbind")) {+      parseUnbind(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 {+      error(-1, "Unknown config file command '%s' (%s:%d)",+	    cmd->getCString(), fileName->getCString(), line);+      if (!cmd->cmp("displayFontX") ||+	  !cmd->cmp("displayNamedCIDFontX") ||+	  !cmd->cmp("displayCIDFontX")) {+	error(-1, "-- Xpdf no longer supports X fonts");+      } else if (!cmd->cmp("t1libControl") || !cmd->cmp("freetypeControl")) {+	error(-1, "-- The t1libControl and freetypeControl options have been replaced");+	error(-1, "   by the enableT1lib, enableFreeType, and antialias options");+      } else if (!cmd->cmp("fontpath") || !cmd->cmp("fontmap")) {+	error(-1, "-- the config file format has changed since Xpdf 0.9x");+      }+    }+  }++  deleteGList(tokens, GString);+}++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(-1, "Bad 'nameToUnicode' config file command (%s:%d)",+	  fileName->getCString(), line);+    return;+  }+  name = (GString *)tokens->get(1);+  if (!(f = fopen(name->getCString(), "r"))) {+    error(-1, "Couldn't open 'nameToUnicode' file '%s'",+	  name->getCString());+    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(-1, "Bad line in 'nameToUnicode' file (%s:%d)", name, line2);+    }+    ++line2;+  }+  fclose(f);+}++void GlobalParams::parseCIDToUnicode(GList *tokens, GString *fileName,+				     int line) {+  GString *collection, *name, *old;++  if (tokens->getLength() != 3) {+    error(-1, "Bad 'cidToUnicode' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'unicodeToUnicode' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'unicodeMap' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'cMapDir' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'toUnicodeDir' config file command (%s:%d)",+	  fileName->getCString(), line);+    return;+  }+  toUnicodeDirs->append(((GString *)tokens->get(1))->copy());+}++void GlobalParams::parseDisplayFont(GList *tokens, GHash *fontHash,+				    DisplayFontParamKind kind,+				    GString *fileName, int line) {+  DisplayFontParam *param, *old;++  if (tokens->getLength() < 2) {+    goto err1;+  }+  param = new DisplayFontParam(((GString *)tokens->get(1))->copy(), kind);+  +  switch (kind) {+  case displayFontT1:+    if (tokens->getLength() != 3) {+      goto err2;+    }+    param->t1.fileName = ((GString *)tokens->get(2))->copy();+    break;+  case displayFontTT:+    if (tokens->getLength() != 3) {+      goto err2;+    }+    param->tt.fileName = ((GString *)tokens->get(2))->copy();+    break;+  }++  if ((old = (DisplayFontParam *)fontHash->remove(param->name))) {+    delete old;+  }+  fontHash->add(param->name, param);+  return;++ err2:+  delete param;+ err1:+  error(-1, "Bad 'display*Font*' config file command (%s:%d)",+	fileName->getCString(), line);+}++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(-1, "Bad 'psPaperSize' config file command (%s:%d)",+	    fileName->getCString(), 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(-1, "Bad 'psPaperSize' config file command (%s:%d)",+	  fileName->getCString(), line);+  }+}++void GlobalParams::parsePSImageableArea(GList *tokens, GString *fileName,+					int line) {+  if (tokens->getLength() != 5) {+    error(-1, "Bad 'psImageableArea' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'psLevel' config file command (%s:%d)",+	  fileName->getCString(), 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("level2sep")) {+    psLevel = psLevel2Sep;+  } else if (!tok->cmp("level3")) {+    psLevel = psLevel3;+  } else if (!tok->cmp("level3Sep")) {+    psLevel = psLevel3Sep;+  } else {+    error(-1, "Bad 'psLevel' config file command (%s:%d)",+	  fileName->getCString(), line);+  }+}++void GlobalParams::parsePSFile(GList *tokens, GString *fileName, int line) {+  if (tokens->getLength() != 2) {+    error(-1, "Bad 'psFile' config file command (%s:%d)",+	  fileName->getCString(), line);+    return;+  }+  if (psFile) {+    delete psFile;+  }+  psFile = ((GString *)tokens->get(1))->copy();+}++void GlobalParams::parsePSFont(GList *tokens, GString *fileName, int line) {+  PSFontParam *param;++  if (tokens->getLength() != 3) {+    error(-1, "Bad 'psFont' config file command (%s:%d)",+	  fileName->getCString(), line);+    return;+  }+  param = new PSFontParam(((GString *)tokens->get(1))->copy(), 0,+			  ((GString *)tokens->get(2))->copy(), NULL);+  psFonts->add(param->pdfFontName, param);+}++void GlobalParams::parsePSFont16(char *cmdName, GList *fontList,+				 GList *tokens, GString *fileName, int line) {+  PSFontParam *param;+  int wMode;+  GString *tok;++  if (tokens->getLength() != 5) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), line);+    return;+  }+  tok = (GString *)tokens->get(2);+  if (!tok->cmp("H")) {+    wMode = 0;+  } else if (!tok->cmp("V")) {+    wMode = 1;+  } else {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), line);+    return;+  }+  param = new PSFontParam(((GString *)tokens->get(1))->copy(),+			  wMode,+			  ((GString *)tokens->get(3))->copy(),+			  ((GString *)tokens->get(4))->copy());+  fontList->append(param);+}++void GlobalParams::parseTextEncoding(GList *tokens, GString *fileName,+				     int line) {+  if (tokens->getLength() != 2) {+    error(-1, "Bad 'textEncoding' config file command (%s:%d)",+	  fileName->getCString(), line);+    return;+  }+  delete textEncoding;+  textEncoding = ((GString *)tokens->get(1))->copy();+}++void GlobalParams::parseTextEOL(GList *tokens, GString *fileName, int line) {+  GString *tok;++  if (tokens->getLength() != 2) {+    error(-1, "Bad 'textEOL' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'textEOL' config file command (%s:%d)",+	  fileName->getCString(), line);+  }+}++void GlobalParams::parseFontDir(GList *tokens, GString *fileName, int line) {+  if (tokens->getLength() != 2) {+    error(-1, "Bad 'fontDir' config file command (%s:%d)",+	  fileName->getCString(), line);+    return;+  }+  fontDirs->append(((GString *)tokens->get(1))->copy());+}++void GlobalParams::parseInitialZoom(GList *tokens,+				    GString *fileName, int line) {+  if (tokens->getLength() != 2) {+    error(-1, "Bad 'initialZoom' config file command (%s:%d)",+	  fileName->getCString(), line);+    return;+  }+  delete initialZoom;+  initialZoom = ((GString *)tokens->get(1))->copy();+}++void GlobalParams::parseScreenType(GList *tokens, GString *fileName,+				   int line) {+  GString *tok;++  if (tokens->getLength() != 2) {+    error(-1, "Bad 'screenType' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'screenType' config file command (%s:%d)",+	  fileName->getCString(), line);+  }+}++void GlobalParams::parseBind(GList *tokens, GString *fileName, int line) {+  KeyBinding *binding;+  GList *cmds;+  int code, mods, context, i;++  if (tokens->getLength() < 4) {+    error(-1, "Bad 'bind' config file command (%s:%d)",+	  fileName->getCString(), 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(-1, "Bad 'unbind' config file command (%s:%d)",+	  fileName->getCString(), 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,+			     char *cmdName,+			     GList *tokens, GString *fileName, int line) {+  char *p0;++  *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, "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] >= '1' && p0[10] <= '7' && !p0[11]) {+    *code = xpdfKeyCodeMousePress1 + (p0[10] - '1');+  } else if (!strncmp(p0, "mouseRelease", 12) &&+	     p0[12] >= '1' && p0[12] <= '7' && !p0[13]) {+    *code = xpdfKeyCodeMouseRelease1 + (p0[12] - '1');+  } else if (*p0 >= 0x20 && *p0 <= 0x7e && !p0[1]) {+    *code = (int)*p0;+  } else {+    error(-1, "Bad key/modifier in '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), 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(-1, "Bad context in '%s' config file command (%s:%d)",+	      cmdName, fileName->getCString(), line);+	return gFalse;+      }+      if (!*p0) {+	break;+      }+      if (*p0 != ',') {+	error(-1, "Bad context in '%s' config file command (%s:%d)",+	      cmdName, fileName->getCString(), line);+	return gFalse;+      }+      ++p0;+    }+  }++  return gTrue;+}++void GlobalParams::parseCommand(char *cmdName, GString **val,+				GList *tokens, GString *fileName, int line) {+  if (tokens->getLength() != 2) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), line);+    return;+  }+  if (*val) {+    delete *val;+  }+  *val = ((GString *)tokens->get(1))->copy();+}++void GlobalParams::parseYesNo(char *cmdName, GBool *flag,+			      GList *tokens, GString *fileName, int line) {+  GString *tok;++  if (tokens->getLength() != 2) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), line);+    return;+  }+  tok = (GString *)tokens->get(1);+  if (!parseYesNo2(tok->getCString(), flag)) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), 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::parseInteger(char *cmdName, int *val,+				GList *tokens, GString *fileName, int line) {+  GString *tok;+  int i;++  if (tokens->getLength() != 2) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), line);+    return;+  }+  tok = (GString *)tokens->get(1);+  if (tok->getLength() == 0) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), 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(-1, "Bad '%s' config file command (%s:%d)",+	    cmdName, fileName->getCString(), line);+      return;+    }+  }+  *val = atoi(tok->getCString());+}++void GlobalParams::parseFloat(char *cmdName, double *val,+			      GList *tokens, GString *fileName, int line) {+  GString *tok;+  int i;++  if (tokens->getLength() != 2) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), line);+    return;+  }+  tok = (GString *)tokens->get(1);+  if (tok->getLength() == 0) {+    error(-1, "Bad '%s' config file command (%s:%d)",+	  cmdName, fileName->getCString(), 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(-1, "Bad '%s' config file command (%s:%d)",+	    cmdName, fileName->getCString(), line);+      return;+    }+  }+  *val = atof(tok->getCString());+}++GlobalParams::~GlobalParams() {+  GHashIter *iter;+  GString *key;+  GList *list;++  freeBuiltinFontTables();++  delete macRomanReverseMap;++  delete baseDir;+  delete nameToUnicode;+  deleteGHash(cidToUnicodes, GString);+  deleteGHash(unicodeToUnicodes, GString);+  deleteGHash(residentUnicodeMaps, UnicodeMap);+  deleteGHash(unicodeMaps, GString);+  deleteGList(toUnicodeDirs, GString);+  deleteGHash(displayFonts, DisplayFontParam);+  deleteGHash(displayCIDFonts, DisplayFontParam);+  deleteGHash(displayNamedCIDFonts, DisplayFontParam);+#ifdef WIN32+  if (winFontList) {+    delete winFontList;+  }+#endif+  if (psFile) {+    delete psFile;+  }+  deleteGHash(psFonts, PSFontParam);+  deleteGList(psNamedFonts16, PSFontParam);+  deleteGList(psFonts16, PSFontParam);+  delete textEncoding;+  deleteGList(fontDirs, GString);+  delete initialZoom;+  if (urlCommand) {+    delete urlCommand;+  }+  if (movieCommand) {+    delete movieCommand;+  }+  deleteGList(keyBindings, KeyBinding);++  cMapDirs->startIter(&iter);+  while (cMapDirs->getNext(&iter, &key, (void **)&list)) {+    deleteGList(list, GString);+  }+  delete cMapDirs;++  delete cidToUnicodeCache;+  delete unicodeToUnicodeCache;+  delete unicodeMapCache;+  delete cMapCache;++#ifdef ENABLE_PLUGINS+  delete securityHandlers;+  deleteGList(plugins, Plugin);+#endif++#if MULTITHREADED+  gDestroyMutex(&mutex);+  gDestroyMutex(&unicodeMapCacheMutex);+  gDestroyMutex(&cMapCacheMutex);+#endif+}++//------------------------------------------------------------------------++void GlobalParams::setBaseDir(char *dir) {+  delete baseDir;+  baseDir = new GString(dir);+}++void GlobalParams::setupBaseFonts(char *dir) {+  GString *fontName;+  GString *fileName;+#ifdef WIN32+  HMODULE shell32Lib;+  BOOL (__stdcall *SHGetSpecialFolderPathFunc)(HWND hwndOwner,+					       LPTSTR lpszPath,+					       int nFolder,+					       BOOL fCreate);+  char winFontDir[MAX_PATH];+#endif+  FILE *f;+  DisplayFontParamKind kind;+  DisplayFontParam *dfp;+  int i, j;++#ifdef WIN32+  // SHGetSpecialFolderPath isn't available in older versions of+  // shell32.dll (Win95 and WinNT4), so do a dynamic load+  winFontDir[0] = '\0';+  if ((shell32Lib = LoadLibrary("shell32.dll"))) {+    if ((SHGetSpecialFolderPathFunc = +	 (BOOL (__stdcall *)(HWND hwndOwner, LPTSTR lpszPath,+			     int nFolder, BOOL fCreate))+	 GetProcAddress(shell32Lib, "SHGetSpecialFolderPathA"))) {+      if (!(*SHGetSpecialFolderPathFunc)(NULL, winFontDir,+					 CSIDL_FONTS, FALSE)) {+	winFontDir[0] = '\0';+      }+    }+  }+#endif+  for (i = 0; displayFontTab[i].name; ++i) {+    fontName = new GString(displayFontTab[i].name);+    if (getDisplayFont(fontName)) {+      delete fontName;+      continue;+    }+    fileName = NULL;+    kind = displayFontT1; // make gcc happy+    if (dir) {+      fileName = appendToPath(new GString(dir), displayFontTab[i].t1FileName);+      kind = displayFontT1;+      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);+      kind = displayFontTT;+      if ((f = fopen(fileName->getCString(), "rb"))) {+	fclose(f);+      } else {+	delete fileName;+	fileName = NULL;+      }+    }+    // SHGetSpecialFolderPath(CSIDL_FONTS) doesn't work on Win 2k Server+    // or Win2003 Server, or with older versions of shell32.dll, so check+    // the "standard" directories+    if (displayFontTab[i].ttFileName) {+      for (j = 0; !fileName && displayFontDirs[j]; ++j) {+	fileName = appendToPath(new GString(displayFontDirs[j]),+				displayFontTab[i].ttFileName);+	kind = displayFontTT;+	if ((f = fopen(fileName->getCString(), "rb"))) {+	  fclose(f);+	} else {+	  delete fileName;+	  fileName = NULL;+	}+      }+    }+#else+    for (j = 0; !fileName && displayFontDirs[j]; ++j) {+      fileName = appendToPath(new GString(displayFontDirs[j]),+			      displayFontTab[i].t1FileName);+      kind = displayFontT1;+      if ((f = fopen(fileName->getCString(), "rb"))) {+	fclose(f);+      } else {+	delete fileName;+	fileName = NULL;+      }+    }+#endif+    if (!fileName) {+      error(-1, "No display font for '%s'", displayFontTab[i].name);+      delete fontName;+      continue;+    }+    dfp = new DisplayFontParam(fontName, kind);+    dfp->t1.fileName = fileName;+    globalParams->addDisplayFont(dfp);+  }++#ifdef WIN32+  if (winFontDir[0]) {+    winFontList = new WinFontList(winFontDir);+  }+#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(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 = fopen(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 = fopen(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 = fopen(fileName->getCString(), "r");+    delete fileName;+    if (f) {+      unlockGlobalParams;+      return f;+    }+  }+  unlockGlobalParams;+  return NULL;+}++DisplayFontParam *GlobalParams::getDisplayFont(GString *fontName) {+  DisplayFontParam *dfp;++  lockGlobalParams;+  dfp = (DisplayFontParam *)displayFonts->lookup(fontName);+#ifdef WIN32+  if (!dfp && winFontList) {+    dfp = winFontList->find(fontName);+  }+#endif+  unlockGlobalParams;+  return dfp;+}++DisplayFontParam *GlobalParams::getDisplayCIDFont(GString *fontName,+						  GString *collection) {+  DisplayFontParam *dfp;++  lockGlobalParams;+  if (!fontName ||+      !(dfp = (DisplayFontParam *)displayNamedCIDFonts->lookup(fontName))) {+    dfp = (DisplayFontParam *)displayCIDFonts->lookup(collection);+  }+  unlockGlobalParams;+  return dfp;+}++GString *GlobalParams::getPSFile() {+  GString *s;++  lockGlobalParams;+  s = psFile ? psFile->copy() : (GString *)NULL;+  unlockGlobalParams;+  return s;+}++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::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;+}++PSFontParam *GlobalParams::getPSFont(GString *fontName) {+  PSFontParam *p;++  lockGlobalParams;+  p = (PSFontParam *)psFonts->lookup(fontName);+  unlockGlobalParams;+  return p;+}++PSFontParam *GlobalParams::getPSFont16(GString *fontName,+				       GString *collection, int wMode) {+  PSFontParam *p;+  int i;++  lockGlobalParams;+  p = NULL;+  if (fontName) {+    for (i = 0; i < psNamedFonts16->getLength(); ++i) {+      p = (PSFontParam *)psNamedFonts16->get(i);+      if (!p->pdfFontName->cmp(fontName) &&+	  p->wMode == wMode) {+	break;+      }+      p = NULL;+    }+  }+  if (!p && collection) {+    for (i = 0; i < psFonts16->getLength(); ++i) {+      p = (PSFontParam *)psFonts16->get(i);+      if (!p->pdfFontName->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::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;+}++GString *GlobalParams::getTextEncodingName() {+  GString *s;++  lockGlobalParams;+  s = textEncoding->copy();+  unlockGlobalParams;+  return s;+}++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::findFontFile(GString *fontName, char **exts) {+  GString *dir, *fileName;+  char **ext;+  FILE *f;+  int i;++  lockGlobalParams;+  for (i = 0; i < fontDirs->getLength(); ++i) {+    dir = (GString *)fontDirs->get(i);+    for (ext = exts; *ext; ++ext) {+      fileName = appendToPath(dir->copy(), fontName->getCString());+      fileName->append(*ext);+      if ((f = fopen(fileName->getCString(), "rb"))) {+	fclose(f);+	unlockGlobalParams;+	return fileName;+      }+      delete fileName;+    }+  }+  unlockGlobalParams;+  return NULL;+}++GString *GlobalParams::getInitialZoom() {+  GString *s;++  lockGlobalParams;+  s = initialZoom->copy();+  unlockGlobalParams;+  return s;+}++GBool GlobalParams::getContinuousView() {+  GBool f;++  lockGlobalParams;+  f = continuousView;+  unlockGlobalParams;+  return f;+}++GBool GlobalParams::getEnableT1lib() {+  GBool f;++  lockGlobalParams;+  f = enableT1lib;+  unlockGlobalParams;+  return f;+}++GBool GlobalParams::getEnableFreeType() {+  GBool f;++  lockGlobalParams;+  f = enableFreeType;+  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::getStrokeAdjust() {+  GBool f;++  lockGlobalParams;+  f = strokeAdjust;+  unlockGlobalParams;+  return f;+}++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;+}++GBool GlobalParams::getMapNumericCharNames() {+  GBool map;++  lockGlobalParams;+  map = mapNumericCharNames;+  unlockGlobalParams;+  return map;+}++GBool GlobalParams::getMapUnknownCharNames() {+  GBool map;++  lockGlobalParams;+  map = mapUnknownCharNames;+  unlockGlobalParams;+  return map;+}++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 <= 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;+}++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;+}++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::addDisplayFont(DisplayFontParam *param) {+  DisplayFontParam *old;++  lockGlobalParams;+  if ((old = (DisplayFontParam *)displayFonts->remove(param->name))) {+    delete old;+  }+  displayFonts->add(param->name, param);+  unlockGlobalParams;+}++void GlobalParams::setPSFile(char *file) {+  lockGlobalParams;+  if (psFile) {+    delete psFile;+  }+  psFile = new GString(file);+  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::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::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(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;+}++void GlobalParams::setContinuousView(GBool cont) {+  lockGlobalParams;+  continuousView = cont;+  unlockGlobalParams;+}++GBool GlobalParams::setEnableT1lib(char *s) {+  GBool ok;++  lockGlobalParams;+  ok = parseYesNo2(s, &enableT1lib);+  unlockGlobalParams;+  return ok;+}++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::setMapNumericCharNames(GBool map) {+  lockGlobalParams;+  mapNumericCharNames = map;+  unlockGlobalParams;+}++void GlobalParams::setMapUnknownCharNames(GBool map) {+  lockGlobalParams;+  mapUnknownCharNames = map;+  unlockGlobalParams;+}++void GlobalParams::setPrintCommands(GBool printCommandsA) {+  lockGlobalParams;+  printCommands = printCommandsA;+  unlockGlobalParams;+}++void GlobalParams::setErrQuiet(GBool errQuietA) {+  lockGlobalParams;+  errQuiet = errQuietA;+  unlockGlobalParams;+}++void GlobalParams::addSecurityHandler(XpdfSecurityHandler *handler) {+#ifdef ENABLE_PLUGINS+  lockGlobalParams;+  securityHandlers->append(handler);+  unlockGlobalParams;+#endif+}++XpdfSecurityHandler *GlobalParams::getSecurityHandler(char *name) {+#ifdef ENABLE_PLUGINS+  XpdfSecurityHandler *hdlr;+  int i;++  lockGlobalParams;+  for (i = 0; i < securityHandlers->getLength(); ++i) {+    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);+    if (!strcasecmp(hdlr->name, name)) {+      unlockGlobalParams;+      return hdlr;+    }+  }+  unlockGlobalParams;++  if (!loadPlugin("security", name)) {+    return NULL;+  }++  lockGlobalParams;+  for (i = 0; i < securityHandlers->getLength(); ++i) {+    hdlr = (XpdfSecurityHandler *)securityHandlers->get(i);+    if (!strcmp(hdlr->name, name)) {+      unlockGlobalParams;+      return hdlr;+    }+  }+  unlockGlobalParams;+#endif++  return NULL;+}++#ifdef ENABLE_PLUGINS+//------------------------------------------------------------------------+// plugins+//------------------------------------------------------------------------++GBool GlobalParams::loadPlugin(char *type, char *name) {+  Plugin *plugin;++  if (!(plugin = Plugin::load(type, name))) {+    return gFalse;+  }+  lockGlobalParams;+  plugins->append(plugin);+  unlockGlobalParams;+  return gTrue;+}++#endif // ENABLE_PLUGINS
+ xpdf/GlobalParams.h view
@@ -0,0 +1,463 @@+//========================================================================+//+// GlobalParams.h+//+// Copyright 2001-2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef GLOBALPARAMS_H+#define GLOBALPARAMS_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include <stdio.h>+#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 CMap;+class CMapCache;+struct XpdfSecurityHandler;+class GlobalParams;+#ifdef WIN32+class WinFontList;+#endif++//------------------------------------------------------------------------++// The global parameters object.+extern GlobalParams *globalParams;++//------------------------------------------------------------------------++enum DisplayFontParamKind {+  displayFontT1,+  displayFontTT+};++struct DisplayFontParamT1 {+  GString *fileName;+};++struct DisplayFontParamTT {+  GString *fileName;+};++class DisplayFontParam {+public:++  GString *name;		// font name for 8-bit fonts and named+				//   CID fonts; collection name for+				//   generic CID fonts+  DisplayFontParamKind kind;+  union {+    DisplayFontParamT1 t1;+    DisplayFontParamTT tt;+  };++  DisplayFontParam(GString *nameA, DisplayFontParamKind kindA);+  virtual ~DisplayFontParam();+};++//------------------------------------------------------------------------++class PSFontParam {+public:++  GString *pdfFontName;		// PDF font name for 8-bit fonts and+				//   named 16-bit fonts; char collection+				//   name for generic 16-bit fonts+  int wMode;			// writing mode (0=horiz, 1=vert) for+				//   16-bit fonts+  GString *psFontName;		// PostScript font name+  GString *encoding;		// encoding, for 16-bit fonts only++  PSFontParam(GString *pdfFontNameA, int wModeA,+	      GString *psFontNameA, GString *encodingA);+  ~PSFontParam();+};++//------------------------------------------------------------------------++enum PSLevel {+  psLevel1,+  psLevel1Sep,+  psLevel2,+  psLevel2Sep,+  psLevel3,+  psLevel3Sep+};++//------------------------------------------------------------------------++enum EndOfLineKind {+  eolUnix,			// LF+  eolDOS,			// CR+LF+  eolMac			// CR+};++//------------------------------------------------------------------------++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, char *cmd0);+  KeyBinding(int codeA, int modsA, int contextA, char *cmd0, 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 xpdfKeyCodeInsert         0x1004+#define xpdfKeyCodeDelete         0x1005+#define xpdfKeyCodeHome           0x1006+#define xpdfKeyCodeEnd            0x1007+#define xpdfKeyCodePgUp           0x1008+#define xpdfKeyCodePgDn           0x1009+#define xpdfKeyCodeLeft           0x100a+#define xpdfKeyCodeRight          0x100b+#define xpdfKeyCodeUp             0x100c+#define xpdfKeyCodeDown           0x100d+#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 xpdfKeyCodeMouseRelease1  0x2101+#define xpdfKeyCodeMouseRelease2  0x2102+#define xpdfKeyCodeMouseRelease3  0x2103+#define xpdfKeyCodeMouseRelease4  0x2104+#define xpdfKeyCodeMouseRelease5  0x2105+#define xpdfKeyCodeMouseRelease6  0x2106+#define xpdfKeyCodeMouseRelease7  0x2107+#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 GlobalParams {+public:++  // Initialize the global parameters by attempting to read a config+  // file.+  GlobalParams(char *cfgFileName);++  ~GlobalParams();++  void setBaseDir(char *dir);+  void setupBaseFonts(char *dir);++  void parseLine(char *buf, GString *fileName, int line);++  //----- accessors++  CharCode getMacRomanCharCode(char *charName);++  GString *getBaseDir();+  Unicode mapNameToUnicode(char *charName);+  UnicodeMap *getResidentUnicodeMap(GString *encodingName);+  FILE *getUnicodeMapFile(GString *encodingName);+  FILE *findCMapFile(GString *collection, GString *cMapName);+  FILE *findToUnicodeFile(GString *name);+  DisplayFontParam *getDisplayFont(GString *fontName);+  DisplayFontParam *getDisplayCIDFont(GString *fontName, GString *collection);+  GString *getPSFile();+  int getPSPaperWidth();+  int getPSPaperHeight();+  void getPSImageableArea(int *llx, int *lly, int *urx, int *ury);+  GBool getPSDuplex();+  GBool getPSCrop();+  GBool getPSExpandSmaller();+  GBool getPSShrinkLarger();+  GBool getPSCenter();+  PSLevel getPSLevel();+  PSFontParam *getPSFont(GString *fontName);+  PSFontParam *getPSFont16(GString *fontName, GString *collection, int wMode);+  GBool getPSEmbedType1();+  GBool getPSEmbedTrueType();+  GBool getPSEmbedCIDPostScript();+  GBool getPSEmbedCIDTrueType();+  GBool getPSPreload();+  GBool getPSOPI();+  GBool getPSASCIIHex();+  GString *getTextEncodingName();+  EndOfLineKind getTextEOL();+  GBool getTextPageBreaks();+  GBool getTextKeepTinyChars();+  GString *findFontFile(GString *fontName, char **exts);+  GString *getInitialZoom();+  GBool getContinuousView();+  GBool getEnableT1lib();+  GBool getEnableFreeType();+  GBool getAntialias();+  GBool getVectorAntialias();+  GBool getStrokeAdjust();+  ScreenType getScreenType();+  int getScreenSize();+  int getScreenDotRadius();+  double getScreenGamma();+  double getScreenBlackThreshold();+  double getScreenWhiteThreshold();+  GString *getURLCommand() { return urlCommand; }+  GString *getMovieCommand() { return movieCommand; }+  GBool getMapNumericCharNames();+  GBool getMapUnknownCharNames();+  GList *getKeyBinding(int code, int mods, int context);+  GBool getPrintCommands();+  GBool getErrQuiet();++  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 addDisplayFont(DisplayFontParam *param);+  void setPSFile(char *file);+  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 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 setPSPreload(GBool preload);+  void setPSOPI(GBool opi);+  void setPSASCIIHex(GBool hex);+  void setTextEncoding(char *encodingName);+  GBool setTextEOL(char *s);+  void setTextPageBreaks(GBool pageBreaks);+  void setTextKeepTinyChars(GBool keep);+  void setInitialZoom(char *s);+  void setContinuousView(GBool cont);+  GBool setEnableT1lib(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 setMapNumericCharNames(GBool map);+  void setMapUnknownCharNames(GBool map);+  void setPrintCommands(GBool printCommandsA);+  void setErrQuiet(GBool errQuietA);++  //----- security handlers++  void addSecurityHandler(XpdfSecurityHandler *handler);+  XpdfSecurityHandler *getSecurityHandler(char *name);++private:++  void createDefaultKeyBindings();+  void parseFile(GString *fileName, FILE *f);+  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 parseDisplayFont(GList *tokens, GHash *fontHash,+			DisplayFontParamKind kind,+			GString *fileName, int line);+  void parsePSFile(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 parsePSFont(GList *tokens, GString *fileName, int line);+  void parsePSFont16(char *cmdName, GList *fontList,+		     GList *tokens, GString *fileName, int line);+  void parseTextEncoding(GList *tokens, GString *fileName, int line);+  void parseTextEOL(GList *tokens, GString *fileName, int line);+  void parseFontDir(GList *tokens, GString *fileName, int line);+  void parseInitialZoom(GList *tokens, GString *fileName, int line);+  void parseScreenType(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,+		 char *cmdName,+		 GList *tokens, GString *fileName, int line);+  void parseCommand(char *cmdName, GString **val,+		    GList *tokens, GString *fileName, int line);+  void parseYesNo(char *cmdName, GBool *flag,+		  GList *tokens, GString *fileName, int line);+  GBool parseYesNo2(char *token, GBool *flag);+  void parseInteger(char *cmdName, int *val,+		    GList *tokens, GString *fileName, int line);+  void parseFloat(char *cmdName, double *val,+		  GList *tokens, GString *fileName, int line);+  UnicodeMap *getUnicodeMap2(GString *encodingName);+#ifdef ENABLE_PLUGINS+  GBool loadPlugin(char *type, char *name);+#endif++  //----- static tables++  NameToCharCode *		// mapping from char name to+    macRomanReverseMap;		//   MacRomanEncoding index++  //----- user-modifiable settings++  GString *baseDir;		// base directory - for plugins, etc.+  NameToCharCode *		// mapping from char name to Unicode+    nameToUnicode;+  GHash *cidToUnicodes;		// files for mappings from char collections+				//   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]+  GHash *displayFonts;		// display font info, indexed by font name+				//   [DisplayFontParam]+#ifdef WIN32+  WinFontList *winFontList;	// system TrueType fonts+#endif+  GHash *displayCIDFonts;	// display CID font info, indexed by+				//   collection [DisplayFontParam]+  GHash *displayNamedCIDFonts;	// display CID font info, indexed by+				//   font name [DisplayFontParam]+  GString *psFile;		// PostScript file or command (for xpdf)+  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 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 *psFonts;		// PostScript font info, indexed by PDF+				//   font name [PSFontParam]+  GList *psNamedFonts16;	// named 16-bit fonts [PSFontParam]+  GList *psFonts16;		// generic 16-bit fonts [PSFontParam]+  GBool psEmbedType1;		// embed Type 1 fonts?+  GBool psEmbedTrueType;	// embed TrueType fonts?+  GBool psEmbedCIDPostScript;	// embed CID PostScript fonts?+  GBool psEmbedCIDTrueType;	// embed CID TrueType fonts?+  GBool psPreload;		// preload PostScript images and forms into+				//   memory+  GBool psOPI;			// generate PostScript OPI comments?+  GBool psASCIIHex;		// use ASCIIHex instead of ASCII85?+  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+  GList *fontDirs;		// list of font dirs [GString]+  GString *initialZoom;		// initial zoom level+  GBool continuousView;		// continuous view mode+  GBool enableT1lib;		// t1lib enable flag+  GBool enableFreeType;		// FreeType enable flag+  GBool antialias;		// font anti-aliasing enable flag+  GBool vectorAntialias;	// vector anti-aliasing enable flag+  GBool strokeAdjust;		// stroke adjustment enable flag+  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+  GString *urlCommand;		// command executed for URL links+  GString *movieCommand;	// command executed for movie annotations+  GBool mapNumericCharNames;	// map numeric char names (from font subsets)?+  GBool mapUnknownCharNames;	// map unknown char names?+  GList *keyBindings;		// key & mouse button bindings [KeyBinding]+  GBool printCommands;		// print the drawing commands+  GBool errQuiet;		// suppress error messages?++  CharCodeToUnicodeCache *cidToUnicodeCache;+  CharCodeToUnicodeCache *unicodeToUnicodeCache;+  UnicodeMapCache *unicodeMapCache;+  CMapCache *cMapCache;++#ifdef ENABLE_PLUGINS+  GList *plugins;		// list of plugins [Plugin]+  GList *securityHandlers;	// list of loaded security handlers+				//   [XpdfSecurityHandler]+#endif++#if MULTITHREADED+  GMutex mutex;+  GMutex unicodeMapCacheMutex;+  GMutex cMapCacheMutex;+#endif+};++#endif
+ xpdf/ImageOutputDev.cc view
@@ -0,0 +1,195 @@+//========================================================================+//+// 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 "gmem.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) {+  fileRoot = copyString(fileRootA);+  fileName = (char *)gmalloc(strlen(fileRoot) + 20);+  dumpJPEG = dumpJPEGA;+  imgNum = 0;+  ok = gTrue;+}++ImageOutputDev::~ImageOutputDev() {+  gfree(fileName);+  gfree(fileRoot);+}++void ImageOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,+				   int width, int height, GBool invert,+				   GBool inlineImg) {+  FILE *f;+  int c;+  int size, i;++  // dump JPEG file+  if (dumpJPEG && str->getKind() == strDCT && !inlineImg) {++    // open the image file+    sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);+    ++imgNum;+    if (!(f = fopen(fileName, "wb"))) {+      error(-1, "Couldn't open image file '%s'", fileName);+      return;+    }++    // initialize stream+    str = ((DCTStream *)str)->getRawStream();+    str->reset();++    // copy the stream+    while ((c = str->getChar()) != EOF)+      fputc(c, f);++    str->close();+    fclose(f);++  // dump PBM file+  } else {++    // open the image file and write the PBM header+    sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);+    ++imgNum;+    if (!(f = fopen(fileName, "wb"))) {+      error(-1, "Couldn't open image file '%s'", 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);+    for (i = 0; i < size; ++i) {+      fputc(str->getChar(), f);+    }++    str->close();+    fclose(f);+  }+}++void ImageOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,+			       int width, int height,+			       GfxImageColorMap *colorMap,+			       int *maskColors, GBool inlineImg) {+  FILE *f;+  ImageStream *imgStr;+  Guchar *p;+  GfxRGB rgb;+  int x, y;+  int c;+  int size, i;++  // dump JPEG file+  if (dumpJPEG && str->getKind() == strDCT &&+      (colorMap->getNumPixelComps() == 1 ||+       colorMap->getNumPixelComps() == 3) &&+      !inlineImg) {++    // open the image file+    sprintf(fileName, "%s-%03d.jpg", fileRoot, imgNum);+    ++imgNum;+    if (!(f = fopen(fileName, "wb"))) {+      error(-1, "Couldn't open image file '%s'", fileName);+      return;+    }++    // initialize stream+    str = ((DCTStream *)str)->getRawStream();+    str->reset();++    // copy the stream+    while ((c = str->getChar()) != EOF)+      fputc(c, 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+    sprintf(fileName, "%s-%03d.pbm", fileRoot, imgNum);+    ++imgNum;+    if (!(f = fopen(fileName, "wb"))) {+      error(-1, "Couldn't open image file '%s'", 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);+    for (i = 0; i < size; ++i) {+      fputc(str->getChar() ^ 0xff, f);+    }++    str->close();+    fclose(f);++  // dump PPM file+  } else {++    // open the image file and write the PPM header+    sprintf(fileName, "%s-%03d.ppm", fileRoot, imgNum);+    ++imgNum;+    if (!(f = fopen(fileName, "wb"))) {+      error(-1, "Couldn't open image file '%s'", 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+      p = imgStr->getLine();+      for (x = 0; x < width; ++x) {+	colorMap->getRGB(p, &rgb);+	fputc(colToByte(rgb.r), f);+	fputc(colToByte(rgb.g), f);+	fputc(colToByte(rgb.b), f);+	p += colorMap->getNumPixelComps();+      }+    }+    delete imgStr;++    fclose(f);+  }+}
+ xpdf/ImageOutputDev.h view
@@ -0,0 +1,76 @@+//========================================================================+//+// 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 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.+  ImageOutputDev(char *fileRootA, GBool dumpJPEGA);++  // Destructor.+  virtual ~ImageOutputDev();++  // Check if file was successfully created.+  virtual GBool isOk() { return ok; }++  // 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; }++  //----- image drawing+  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,+			     int width, int height, GBool invert,+			     GBool inlineImg);+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,+			 int width, int height, GfxImageColorMap *colorMap,+			 int *maskColors, GBool inlineImg);++private:++  char *fileRoot;		// root of output file names+  char *fileName;		// buffer for output file names+  GBool dumpJPEG;		// set to dump native JPEG files+  int imgNum;			// current image number+  GBool ok;			// set up ok?+};++#endif
+ xpdf/JArithmeticDecoder.cc view
@@ -0,0 +1,322 @@+//========================================================================+//+// JArithmeticDecoder.cc+//+// Copyright 2002-2004 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#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] = (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;+}++inline Guint JArithmeticDecoder::readByte() {+  if (limitStream) {+    --dataLen;+    if (dataLen < 0) {+      return 0xff;+    }+  }+  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) {+  int oldDataLen;++  oldDataLen = dataLen;+  dataLen = dataLenA;+  if (oldDataLen == -1) {+    buf1 = readByte();+  } else if (oldDataLen <= -2) {+    buf0 = readByte();+    buf1 = readByte();+  }+}++void JArithmeticDecoder::cleanup() {+  if (limitStream) {+    while (dataLen > 0) {+      buf0 = buf1;+      buf1 = 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] = (nlpsTab[iCX] << 1) | (1 - mpsCX);+	} else {+	  stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;+	}+      } else {+	bit = mpsCX;+	stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;+      }+      // RENORMD+      do {+	if (ct == 0) {+	  byteIn();+	}+	a <<= 1;+	c <<= 1;+	--ct;+      } while (!(a & 0x80000000));+    }+  } else {+    c -= a;+    // LPS_EXCHANGE+    if (a < qe) {+      bit = mpsCX;+      stats->cxTab[context] = (nmpsTab[iCX] << 1) | mpsCX;+    } else {+      bit = 1 - mpsCX;+      if (switchTab[iCX]) {+	stats->cxTab[context] = (nlpsTab[iCX] << 1) | (1 - mpsCX);+      } else {+	stats->cxTab[context] = (nlpsTab[iCX] << 1) | mpsCX;+      }+    }+    a = qe;+    // RENORMD+    do {+      if (ct == 0) {+	byteIn();+      }+      a <<= 1;+      c <<= 1;+      --ct;+    } while (!(a & 0x80000000));+  }+  return bit;+}++int 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) {+      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;+  }+}
+ xpdf/JArithmeticDecoder.h view
@@ -0,0 +1,109 @@+//========================================================================+//+// 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);++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;+  int dataLen;+  GBool limitStream;+};++#endif
+ xpdf/JBIG2Stream.cc view
@@ -0,0 +1,3413 @@+//========================================================================+//+// 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 "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);++private:++  Stream *str;+  Guint buf;+  Guint bufLen;+};++JBIG2HuffmanDecoder::JBIG2HuffmanDecoder() {+  str = NULL;+  reset();+}++JBIG2HuffmanDecoder::~JBIG2HuffmanDecoder() {+}++void JBIG2HuffmanDecoder::reset() {+  buf = 0;+  bufLen = 0;+}++//~ optimize this+GBool JBIG2HuffmanDecoder::decodeInt(int *x, JBIG2HuffmanTable *table) {+  Guint i, len, prefix;++  i = 0;+  len = 0;+  prefix = 0;+  while (table[i].rangeLen != jbig2HuffmanEOT) {+    while (len < table[i].prefixLen) {+      prefix = (prefix << 1) | readBit();+      ++len;+    }+    if (prefix == table[i].prefix) {+      if (table[i].rangeLen == jbig2HuffmanOOB) {+	return gFalse;+      }+      if (table[i].rangeLen == jbig2HuffmanLOW) {+	*x = table[i].val - readBits(32);+      } else if (table[i].rangeLen > 0) {+	*x = table[i].val + readBits(table[i].rangeLen);+      } else {+	*x = table[i].val;+      }+      return gTrue;+    }+    ++i;+  }+  return gFalse;+}++Guint JBIG2HuffmanDecoder::readBits(Guint n) {+  Guint x, mask, nLeft;++  mask = (n == 32) ? 0xffffffff : ((1 << n) - 1);+  if (bufLen >= n) {+    x = (buf >> (bufLen - n)) & mask;+    bufLen -= n;+  } else {+    x = buf & ((1 << bufLen) - 1);+    nLeft = n - bufLen;+    bufLen = 0;+    while (nLeft >= 8) {+      x = (x << 8) | (str->getChar() & 0xff);+      nLeft -= 8;+    }+    if (nLeft > 0) {+      buf = str->getChar();+      bufLen = 8 - nLeft;+      x = (x << nLeft) | ((buf >> bufLen) & ((1 << nLeft) - 1));+    }+  }+  return x;+}++Guint JBIG2HuffmanDecoder::readBit() {+  if (bufLen == 0) {+    buf = str->getChar();+    bufLen = 8;+  }+  --bufLen;+  return (buf >> bufLen) & 1;+}++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+  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 skipTo(Guint length);++private:++  Stream *str;+  Guint buf;+  Guint bufLen;+  Guint nBytesRead;+};++JBIG2MMRDecoder::JBIG2MMRDecoder() {+  str = NULL;+  reset();+}++JBIG2MMRDecoder::~JBIG2MMRDecoder() {+}++void JBIG2MMRDecoder::reset() {+  buf = 0;+  bufLen = 0;+  nBytesRead = 0;+}++int JBIG2MMRDecoder::get2DCode() {+  CCITTCode *p;++  if (bufLen == 0) {+    buf = str->getChar() & 0xff;+    bufLen = 8;+    ++nBytesRead;+    p = &twoDimTab1[(buf >> 1) & 0x7f];+  } else if (bufLen == 8) {+    p = &twoDimTab1[(buf >> 1) & 0x7f];+  } else {+    p = &twoDimTab1[(buf << (7 - bufLen)) & 0x7f];+    if (p->bits < 0 || p->bits > (int)bufLen) {+      buf = (buf << 8) | (str->getChar() & 0xff);+      bufLen += 8;+      ++nBytesRead;+      p = &twoDimTab1[(buf >> (bufLen - 7)) & 0x7f];+    }+  }+  if (p->bits < 0) {+    error(str->getPos(), "Bad two dim code in JBIG2 MMR stream");+    return 0;+  }+  bufLen -= p->bits;+  return p->n;+}++int JBIG2MMRDecoder::getWhiteCode() {+  CCITTCode *p;+  Guint code;++  if (bufLen == 0) {+    buf = str->getChar() & 0xff;+    bufLen = 8;+    ++nBytesRead;+  }+  while (1) {+    if (bufLen >= 7 && ((buf >> (bufLen - 7)) & 0x7f) == 0) {+      if (bufLen <= 12) {+	code = buf << (12 - bufLen);+      } else {+	code = buf >> (bufLen - 12);+      }+      p = &whiteTab1[code & 0x1f];+    } else {+      if (bufLen <= 9) {+	code = buf << (9 - bufLen);+      } else {+	code = buf >> (bufLen - 9);+      }+      p = &whiteTab2[code & 0x1ff];+    }+    if (p->bits > 0 && p->bits <= (int)bufLen) {+      bufLen -= p->bits;+      return p->n;+    }+    if (bufLen >= 12) {+      break;+    }+    buf = (buf << 8) | (str->getChar() & 0xff);+    bufLen += 8;+    ++nBytesRead;+  }+  error(str->getPos(), "Bad white code in JBIG2 MMR stream");+  // eat a bit and return a positive number so that the caller doesn't+  // go into an infinite loop+  --bufLen;+  return 1;+}++int JBIG2MMRDecoder::getBlackCode() {+  CCITTCode *p;+  Guint code;++  if (bufLen == 0) {+    buf = str->getChar() & 0xff;+    bufLen = 8;+    ++nBytesRead;+  }+  while (1) {+    if (bufLen >= 6 && ((buf >> (bufLen - 6)) & 0x3f) == 0) {+      if (bufLen <= 13) {+	code = buf << (13 - bufLen);+      } else {+	code = buf >> (bufLen - 13);+      }+      p = &blackTab1[code & 0x7f];+    } else if (bufLen >= 4 && ((buf >> (bufLen - 4)) & 0x0f) == 0) {+      if (bufLen <= 12) {+	code = buf << (12 - bufLen);+      } else {+	code = buf >> (bufLen - 12);+      }+      p = &blackTab2[(code & 0xff) - 64];+    } else {+      if (bufLen <= 6) {+	code = buf << (6 - bufLen);+      } else {+	code = buf >> (bufLen - 6);+      }+      p = &blackTab3[code & 0x3f];+    }+    if (p->bits > 0 && p->bits <= (int)bufLen) {+      bufLen -= p->bits;+      return p->n;+    }+    if (bufLen >= 13) {+      break;+    }+    buf = (buf << 8) | (str->getChar() & 0xff);+    bufLen += 8;+    ++nBytesRead;+  }+  error(str->getPos(), "Bad black code in JBIG2 MMR stream");+  // eat a bit and return a positive number so that the caller doesn't+  // go into an infinite loop+  --bufLen;+  return 1;+}++Guint JBIG2MMRDecoder::get24Bits() {+  while (bufLen < 24) {+    buf = (buf << 8) | (str->getChar() & 0xff);+    bufLen += 8;+    ++nBytesRead;+  }+  return (buf >> (bufLen - 24)) & 0xffffff;+}++void JBIG2MMRDecoder::skipTo(Guint length) {+  while (nBytesRead < length) {+    str->getChar();+    ++nBytesRead;+  }+}++//------------------------------------------------------------------------+// JBIG2Segment+//------------------------------------------------------------------------++enum JBIG2SegmentType {+  jbig2SegBitmap,+  jbig2SegSymbolDict,+  jbig2SegPatternDict,+  jbig2SegCodeTable+};++class JBIG2Segment {+public:++  JBIG2Segment(Guint segNumA) { segNum = segNumA; }+  virtual ~JBIG2Segment() {}+  void setSegNum(Guint segNumA) { segNum = segNumA; }+  Guint getSegNum() { return segNum; }+  virtual JBIG2SegmentType getType() = 0;++private:++  Guint segNum;+};++//------------------------------------------------------------------------+// JBIG2Bitmap+//------------------------------------------------------------------------++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 getPixel(int x, int y)+    { return (x < 0 || x >= w || y < 0 || y >= h) ? 0 :+             (data[y * line + (x >> 3)] >> (7 - (x & 7))) & 1; }+  void setPixel(int x, int y)+    { data[y * line + (x >> 3)] |= 1 << (7 - (x & 7)); }+  void clearPixel(int x, int y)+    { data[y * line + (x >> 3)] &= 0x7f7f >> (x & 7); }+  void 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) {+    data = NULL;+    return;+  }+  // 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) {+    data = NULL;+    return;+  }+  // 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;+  } 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;+  Guint src0, src1, src, dest, s1, s2, m1, m2, m3;+  GBool oneByte;++  if (y < 0) {+    y0 = -y;+  } else {+    y0 = 0;+  }+  if (y + bitmap->h > h) {+    y1 = h - y;+  } else {+    y1 = bitmap->h;+  }+  if (y0 >= y1) {+    return;+  }++  if (x >= 0) {+    x0 = x & ~7;+  } else {+    x0 = 0;+  }+  x1 = x + bitmap->w;+  if (x1 > w) {+    x1 = w;+  }+  if (x0 >= x1) {+    return;+  }++  s1 = x & 7;+  s2 = 8 - s1;+  m1 = 0xff >> (x1 & 7);+  m2 = 0xff << (((x1 & 7) == 0) ? 0 : 8 - (x1 & 7));+  m3 = (0xff >> s1) & m2;++  oneByte = x0 == ((x1 - 1) & ~7);++  for (yy = y0; yy < y1; ++yy) {++    // one byte per line -- need to mask both left and right side+    if (oneByte) {+      if (x >= 0) {+	destPtr = data + (y + yy) * line + (x >> 3);+	srcPtr = bitmap->data + yy * bitmap->line;+	dest = *destPtr;+	src1 = *srcPtr;+	switch (combOp) {+	case 0: // or+	  dest |= (src1 >> s1) & m2;+	  break;+	case 1: // and+	  dest &= ((0xff00 | src1) >> s1) | m1;+	  break;+	case 2: // xor+	  dest ^= (src1 >> s1) & m2;+	  break;+	case 3: // xnor+	  dest ^= ((src1 ^ 0xff) >> s1) & m2;+	  break;+	case 4: // replace+	  dest = (dest & ~m3) | ((src1 >> s1) & m3);+	  break;+	}+	*destPtr = dest;+      } else {+	destPtr = data + (y + yy) * line;+	srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);+	dest = *destPtr;+	src1 = *srcPtr;+	switch (combOp) {+	case 0: // or+	  dest |= src1 & m2;+	  break;+	case 1: // and+	  dest &= src1 | m1;+	  break;+	case 2: // xor+	  dest ^= src1 & m2;+	  break;+	case 3: // xnor+	  dest ^= (src1 ^ 0xff) & m2;+	  break;+	case 4: // replace+	  dest = (src1 & m2) | (dest & m1);+	  break;+	}+	*destPtr = dest;+      }++    // multiple bytes per line -- need to mask left side of left-most+    // byte and right side of right-most byte+    } else {++      // left-most byte+      if (x >= 0) {+	destPtr = data + (y + yy) * line + (x >> 3);+	srcPtr = bitmap->data + yy * bitmap->line;+	src1 = *srcPtr++;+	dest = *destPtr;+	switch (combOp) {+	case 0: // or+	  dest |= src1 >> s1;+	  break;+	case 1: // and+	  dest &= (0xff00 | src1) >> s1;+	  break;+	case 2: // xor+	  dest ^= src1 >> s1;+	  break;+	case 3: // xnor+	  dest ^= (src1 ^ 0xff) >> s1;+	  break;+	case 4: // replace+	  dest = (dest & (0xff << s2)) | (src1 >> s1);+	  break;+	}+	*destPtr++ = dest;+	xx = x0 + 8;+      } else {+	destPtr = data + (y + yy) * line;+	srcPtr = bitmap->data + yy * bitmap->line + (-x >> 3);+	src1 = *srcPtr++;+	xx = x0;+      }++      // middle bytes+      for (; xx < x1 - 8; xx += 8) {+	dest = *destPtr;+	src0 = src1;+	src1 = *srcPtr++;+	src = (((src0 << 8) | src1) >> s1) & 0xff;+	switch (combOp) {+	case 0: // or+	  dest |= src;+	  break;+	case 1: // and+	  dest &= src;+	  break;+	case 2: // xor+	  dest ^= src;+	  break;+	case 3: // xnor+	  dest ^= src ^ 0xff;+	  break;+	case 4: // replace+	  dest = src;+	  break;+	}+	*destPtr++ = dest;+      }++      // right-most byte+      // 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 = (((src0 << 8) | src1) >> s1) & 0xff;+      switch (combOp) {+      case 0: // or+	dest |= src & m2;+	break;+      case 1: // and+	dest &= src | m1;+	break;+      case 2: // xor+	dest ^= src & m2;+	break;+      case 3: // xnor+	dest ^= (src ^ 0xff) & m2;+	break;+      case 4: // replace+	dest = (src & m2) | (dest & m1);+	break;+      }+      *destPtr = dest;+    }+  }+}++//------------------------------------------------------------------------+// JBIG2SymbolDict+//------------------------------------------------------------------------++class JBIG2SymbolDict: public JBIG2Segment {+public:++  JBIG2SymbolDict(Guint segNumA, Guint sizeA);+  virtual ~JBIG2SymbolDict();+  virtual JBIG2SegmentType getType() { return jbig2SegSymbolDict; }+  Guint getSize() { return size; }+  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }+  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }+  void setGenericRegionStats(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)+{+  size = sizeA;+  bitmaps = (JBIG2Bitmap **)gmallocn(size, sizeof(JBIG2Bitmap *));+  genericRegionStats = NULL;+  refinementRegionStats = NULL;+}++JBIG2SymbolDict::~JBIG2SymbolDict() {+  Guint i;++  for (i = 0; i < size; ++i) {+    delete bitmaps[i];+  }+  gfree(bitmaps);+  if (genericRegionStats) {+    delete genericRegionStats;+  }+  if (refinementRegionStats) {+    delete refinementRegionStats;+  }+}++//------------------------------------------------------------------------+// JBIG2PatternDict+//------------------------------------------------------------------------++class JBIG2PatternDict: public JBIG2Segment {+public:++  JBIG2PatternDict(Guint segNumA, Guint sizeA);+  virtual ~JBIG2PatternDict();+  virtual JBIG2SegmentType getType() { return jbig2SegPatternDict; }+  Guint getSize() { return size; }+  void setBitmap(Guint idx, JBIG2Bitmap *bitmap) { bitmaps[idx] = bitmap; }+  JBIG2Bitmap *getBitmap(Guint idx) { return bitmaps[idx]; }++private:++  Guint size;+  JBIG2Bitmap **bitmaps;+};++JBIG2PatternDict::JBIG2PatternDict(Guint segNumA, Guint sizeA):+  JBIG2Segment(segNumA)+{+  size = sizeA;+  bitmaps = (JBIG2Bitmap **)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;+}++void JBIG2Stream::reset() {+  // read the globals stream+  globalSegments = new GList();+  if (globalsStream.isStream()) {+    segments = globalSegments;+    curStr = globalsStream.getStream();+    curStr->reset();+    arithDecoder->setStream(curStr);+    huffDecoder->setStream(curStr);+    mmrDecoder->setStream(curStr);+    readSegments();+    curStr->close();+  }++  // read the main stream+  segments = new GList();+  curStr = str;+  curStr->reset();+  arithDecoder->setStream(curStr);+  huffDecoder->setStream(curStr);+  mmrDecoder->setStream(curStr);+  readSegments();++  if (pageBitmap) {+    dataPtr = pageBitmap->getDataPtr();+    dataEnd = dataPtr + pageBitmap->getDataSize();+  } else {+    dataPtr = 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;+}++GString *JBIG2Stream::getPSFilter(int psLevel, char *indent) {+  return NULL;+}++GBool JBIG2Stream::isBinary(GBool last) {+  return str->isBinary(gTrue);+}++void JBIG2Stream::readSegments() {+  Guint segNum, segFlags, segType, page, segLength;+  Guint refFlags, nRefSegs;+  Guint *refSegs;+  int c1, c2, c3;+  Guint i;++  while (readULong(&segNum)) {++    // segment header flags+    if (!readUByte(&segFlags)) {+      goto eofError1;+    }+    segType = segFlags & 0x3f;++    // referred-to segment count and retention flags+    if (!readUByte(&refFlags)) {+      goto eofError1;+    }+    nRefSegs = refFlags >> 5;+    if (nRefSegs == 7) {+      if ((c1 = curStr->getChar()) == EOF ||+	  (c2 = curStr->getChar()) == EOF ||+	  (c3 = curStr->getChar()) == EOF) {+	goto eofError1;+      }+      refFlags = (refFlags << 24) | (c1 << 16) | (c2 << 8) | c3;+      nRefSegs = refFlags & 0x1fffffff;+      for (i = 0; i < (nRefSegs + 9) >> 3; ++i) {+	c1 = curStr->getChar();+      }+    }++    // referred-to segment numbers+    refSegs = (Guint *)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;+    }++    // read the segment data+    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 52:+      readProfilesSeg(segLength);+      break;+    case 53:+      readCodeTableSeg(segNum, segLength);+      break;+    case 62:+      readExtensionSeg(segLength);+      break;+    default:+      error(getPos(), "Unknown segment type in JBIG2 stream");+      for (i = 0; i < segLength; ++i) {+	if ((c1 = curStr->getChar()) == EOF) {+	  goto eofError2;+	}+      }+      break;+    }++    gfree(refSegs);+  }++  return;++ syntaxError:+  gfree(refSegs);+  return;++ eofError2:+  gfree(refSegs);+ eofError1:+  error(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, cnt;+  Guint i, j, k;+  Guchar *p;++  // symbol dictionary flags+  if (!readUWord(&flags)) {+    goto eofError;+  }+  sdTemplate = (flags >> 10) & 3;+  sdrTemplate = (flags >> 12) & 1;+  huff = flags & 1;+  refAgg = (flags >> 1) & 1;+  huffDH = (flags >> 2) & 3;+  huffDW = (flags >> 4) & 3;+  huffBMSize = (flags >> 6) & 1;+  huffAggInst = (flags >> 7) & 1;+  contextUsed = (flags >> 8) & 1;+  contextRetained = (flags >> 9) & 1;++  // symbol dictionary AT flags+  if (!huff) {+    if (sdTemplate == 0) {+      if (!readByte(&sdATX[0]) ||+	  !readByte(&sdATY[0]) ||+	  !readByte(&sdATX[1]) ||+	  !readByte(&sdATY[1]) ||+	  !readByte(&sdATX[2]) ||+	  !readByte(&sdATY[2]) ||+	  !readByte(&sdATX[3]) ||+	  !readByte(&sdATY[3])) {+	goto eofError;+      }+    } else {+      if (!readByte(&sdATX[0]) ||+	  !readByte(&sdATY[0])) {+	goto eofError;+      }+    }+  }++  // symbol dictionary refinement AT flags+  if (refAgg && !sdrTemplate) {+    if (!readByte(&sdrATX[0]) ||+	!readByte(&sdrATY[0]) ||+	!readByte(&sdrATX[1]) ||+	!readByte(&sdrATY[1])) {+      goto eofError;+    }+  }++  // SDNUMEXSYMS and SDNUMNEWSYMS+  if (!readULong(&numExSyms) || !readULong(&numNewSyms)) {+    goto eofError;+  }++  // get referenced segments: input symbol dictionaries and code tables+  codeTables = new GList();+  numInputSyms = 0;+  for (i = 0; i < nRefSegs; ++i) {+    seg = findSegment(refSegs[i]);+    if (seg->getType() == jbig2SegSymbolDict) {+      numInputSyms += ((JBIG2SymbolDict *)seg)->getSize();+    } else if (seg->getType() == jbig2SegCodeTable) {+      codeTables->append(seg);+    }+  }++  // compute symbol code length+  symCodeLen = 0;+  i = 1;+  while (i < numInputSyms + numNewSyms) {+    ++symCodeLen;+    i <<= 1;+  }++  // get the input symbol bitmaps+  bitmaps = (JBIG2Bitmap **)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) {+    seg = findSegment(refSegs[i]);+    if (seg->getType() == jbig2SegSymbolDict) {+      inputSymbolDict = (JBIG2SymbolDict *)seg;+      for (j = 0; j < inputSymbolDict->getSize(); ++j) {+	bitmaps[k++] = inputSymbolDict->getBitmap(j);+      }+    }+  }++  // get the Huffman tables+  huffDHTable = huffDWTable = NULL; // make gcc happy+  huffBMSizeTable = huffAggInstTable = NULL; // make gcc happy+  i = 0;+  if (huff) {+    if (huffDH == 0) {+      huffDHTable = huffTableD;+    } else if (huffDH == 1) {+      huffDHTable = huffTableE;+    } else {+      huffDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffDW == 0) {+      huffDWTable = huffTableB;+    } else if (huffDW == 1) {+      huffDWTable = huffTableC;+    } else {+      huffDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffBMSize == 0) {+      huffBMSizeTable = huffTableA;+    } else {+      huffBMSizeTable =+	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffAggInst == 0) {+      huffAggInstTable = huffTableA;+    } else {+      huffAggInstTable =+	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+  }+  delete codeTables;++  // set up the Huffman decoder+  if (huff) {+    huffDecoder->reset();++  // set up the arithmetic decoder+  } else {+    if (contextUsed && inputSymbolDict) {+      resetGenericStats(sdTemplate, inputSymbolDict->getGenericRegionStats());+    } 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+  symWidths = NULL;+  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) {+      error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");+      goto syntaxError;+    }+    symHeight += dh;+    symWidth = 0;+    totalWidth = 0;+    j = i;++    // read the symbols in this height class+    while (1) {++      // read the delta width+      if (huff) {+	if (!huffDecoder->decodeInt(&dw, huffDWTable)) {+	  break;+	}+      } else {+	if (!arithDecoder->decodeInt(&dw, iadwStats)) {+	  break;+	}+      }+      if (dw < 0 && (Guint)-dw >= symWidth) {+	error(getPos(), "Bad delta-height value in JBIG2 symbol dictionary");+	goto syntaxError;+      }+      symWidth += dw;++      // using a collective bitmap, so don't read a bitmap here+      if (huff && !refAgg) {+	symWidths[i] = symWidth;+	totalWidth += symWidth;++      // refinement/aggregate coding+      } else if (refAgg) {+	if (huff) {+	  if (!huffDecoder->decodeInt(&refAggNum, huffAggInstTable)) {+	    break;+	  }+	} else {+	  if (!arithDecoder->decodeInt(&refAggNum, iaaiStats)) {+	    break;+	  }+	}+#if 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);+	  }+	  refBitmap = bitmaps[symID];+	  bitmaps[numInputSyms + i] =+	      readGenericRefinementRegion(symWidth, symHeight,+					  sdrTemplate, gFalse,+					  refBitmap, refDX, refDY,+					  sdrATX, sdrATY);+	  //~ do we need to use the bmSize value here (in Huffman mode)?+	} else {+	  bitmaps[numInputSyms + i] =+	      readTextRegion(huff, gTrue, symWidth, symHeight,+			     refAggNum, 0, numInputSyms + i, NULL,+			     symCodeLen, bitmaps, 0, 0, 0, 1, 0,+			     huffTableF, huffTableH, huffTableK, huffTableO,+			     huffTableO, huffTableO, huffTableO, huffTableA,+			     sdrTemplate, sdrATX, sdrATY);+	}++      // non-ref/agg coding+      } else {+	bitmaps[numInputSyms + i] =+	    readGenericBitmap(gFalse, symWidth, symHeight,+			      sdTemplate, gFalse, gFalse, NULL,+			      sdATX, sdATY, 0);+      }++      ++i;+    }++    // read the collective bitmap+    if (huff && !refAgg) {+      huffDecoder->decodeInt(&bmSize, huffBMSizeTable);+      huffDecoder->reset();+      if (bmSize == 0) {+	collBitmap = new JBIG2Bitmap(0, totalWidth, symHeight);+	bmSize = symHeight * ((totalWidth + 7) >> 3);+	p = collBitmap->getDataPtr();+	for (k = 0; k < (Guint)bmSize; ++k) {+	  *p++ = curStr->getChar();+	}+      } else {+	collBitmap = readGenericBitmap(gTrue, totalWidth, symHeight,+				       0, gFalse, gFalse, NULL, NULL, NULL,+				       bmSize);+      }+      x = 0;+      for (; j < i; ++j) {+	bitmaps[numInputSyms + j] =+	    collBitmap->getSlice(x, 0, symWidths[j], symHeight);+	x += symWidths[j];+      }+      delete collBitmap;+    }+  }++  // create the symbol dict object+  symbolDict = new JBIG2SymbolDict(segNum, numExSyms);++  // exported symbol list+  i = j = 0;+  ex = gFalse;+  while (i < numInputSyms + numNewSyms) {+    if (huff) {+      huffDecoder->decodeInt(&run, huffTableA);+    } else {+      arithDecoder->decodeInt(&run, iaexStats);+    }+    if (ex) {+      for (cnt = 0; cnt < run; ++cnt) {+	symbolDict->setBitmap(j++, bitmaps[i++]->copy());+      }+    } else {+      i += run;+    }+    ex = !ex;+  }++  for (i = 0; i < numNewSyms; ++i) {+    delete bitmaps[numInputSyms + i];+  }+  gfree(bitmaps);+  if (symWidths) {+    gfree(symWidths);+  }++  // save the arithmetic decoder stats+  if (!huff && contextRetained) {+    symbolDict->setGenericRegionStats(genericRegionStats->copy());+    if (refAgg) {+      symbolDict->setRefinementRegionStats(refinementRegionStats->copy());+    }+  }++  // store the new symbol dict+  segments->append(symbolDict);++  return gTrue;++ 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(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;+  }+  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(getPos(), "Invalid segment reference in JBIG2 text region");+    }+  }+  symCodeLen = 0;+  i = 1;+  while (i < numSyms) {+    ++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 {+      huffFSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffDS == 0) {+      huffDSTable = huffTableH;+    } else if (huffDS == 1) {+      huffDSTable = huffTableI;+    } else if (huffDS == 2) {+      huffDSTable = huffTableJ;+    } else {+      huffDSTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffDT == 0) {+      huffDTTable = huffTableK;+    } else if (huffDT == 1) {+      huffDTTable = huffTableL;+    } else if (huffDT == 2) {+      huffDTTable = huffTableM;+    } else {+      huffDTTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffRDW == 0) {+      huffRDWTable = huffTableN;+    } else if (huffRDW == 1) {+      huffRDWTable = huffTableO;+    } else {+      huffRDWTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffRDH == 0) {+      huffRDHTable = huffTableN;+    } else if (huffRDH == 1) {+      huffRDHTable = huffTableO;+    } else {+      huffRDHTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffRDX == 0) {+      huffRDXTable = huffTableN;+    } else if (huffRDX == 1) {+      huffRDXTable = huffTableO;+    } else {+      huffRDXTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffRDY == 0) {+      huffRDYTable = huffTableN;+    } else if (huffRDY == 1) {+      huffRDYTable = huffTableO;+    } else {+      huffRDYTable = ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+    if (huffRSize == 0) {+      huffRSizeTable = huffTableA;+    } else {+      huffRSizeTable =+	  ((JBIG2CodeTable *)codeTables->get(i++))->getHuffTable();+    }+  }+  delete codeTables;++  // symbol ID Huffman decoding table+  if (huff) {+    huffDecoder->reset();+    for (i = 0; i < 32; ++i) {+      runLengthTab[i].val = i;+      runLengthTab[i].prefixLen = huffDecoder->readBits(4);+      runLengthTab[i].rangeLen = 0;+    }+    runLengthTab[32].val = 0x103;+    runLengthTab[32].prefixLen = huffDecoder->readBits(4);+    runLengthTab[32].rangeLen = 2;+    runLengthTab[33].val = 0x203;+    runLengthTab[33].prefixLen = huffDecoder->readBits(4);+    runLengthTab[33].rangeLen = 3;+    runLengthTab[34].val = 0x20b;+    runLengthTab[34].prefixLen = huffDecoder->readBits(4);+    runLengthTab[34].rangeLen = 7;+    runLengthTab[35].prefixLen = 0;+    runLengthTab[35].rangeLen = jbig2HuffmanEOT;+    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) {+	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;++ eofError:+  error(getPos(), "Unexpected EOF in JBIG2 stream");+}++JBIG2Bitmap *JBIG2Stream::readTextRegion(GBool huff, GBool refine,+					 int w, int h,+					 Guint numInstances,+					 Guint logStrips,+					 int numSyms,+					 JBIG2HuffmanTable *symCodeTab,+					 Guint symCodeLen,+					 JBIG2Bitmap **syms,+					 Guint defPixel, Guint combOp,+					 Guint transposed, Guint refCorner,+					 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) {+      huffDecoder->decodeInt(&dt, huffDTTable);+    } else {+      arithDecoder->decodeInt(&dt, iadtStats);+    }+    t += dt * strips;++    // first S value+    if (huff) {+      huffDecoder->decodeInt(&ds, huffFSTable);+    } else {+      arithDecoder->decodeInt(&ds, iafsStats);+    }+    sFirst += ds;+    s = sFirst;++    // read the instances+    while (1) {++      // T value+      if (strips == 1) {+	dt = 0;+      } else if (huff) {+	dt = huffDecoder->readBits(logStrips);+      } else {+	arithDecoder->decodeInt(&dt, iaitStats);+      }+      tt = t + dt;++      // symbol ID+      if (huff) {+	if (symCodeTab) {+	  huffDecoder->decodeInt(&j, symCodeTab);+	  symID = (Guint)j;+	} else {+	  symID = huffDecoder->readBits(symCodeLen);+	}+      } else {+	symID = arithDecoder->decodeIAID(symCodeLen, iaidStats);+      }++      if (symID >= (Guint)numSyms) {+	error(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;+  }+  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(getPos(), "Unexpected EOF in JBIG2 stream");+}++void JBIG2Stream::readHalftoneRegionSeg(Guint segNum, GBool imm,+					GBool lossless, Guint length,+					Guint *refSegs, Guint nRefSegs) {+  JBIG2Bitmap *bitmap;+  JBIG2Segment *seg;+  JBIG2PatternDict *patternDict;+  JBIG2Bitmap *skipBitmap;+  Guint *grayImg;+  JBIG2Bitmap *grayBitmap;+  JBIG2Bitmap *patternBitmap;+  Guint w, h, x, y, segInfoFlags, extCombOp;+  Guint flags, mmr, templ, enableSkip, combOp;+  Guint gridW, gridH, stepX, stepY, patW, patH;+  int atx[4], aty[4];+  int gridX, gridY, xx, yy, bit, j;+  Guint bpp, m, n, i;++  // region segment info field+  if (!readULong(&w) || !readULong(&h) ||+      !readULong(&x) || !readULong(&y) ||+      !readUByte(&segInfoFlags)) {+    goto eofError;+  }+  extCombOp = segInfoFlags & 7;++  // rest of the halftone region header+  if (!readUByte(&flags)) {+    goto eofError;+  }+  mmr = flags & 1;+  templ = (flags >> 1) & 3;+  enableSkip = (flags >> 3) & 1;+  combOp = (flags >> 4) & 7;+  if (!readULong(&gridW) || !readULong(&gridH) ||+      !readLong(&gridX) || !readLong(&gridY) ||+      !readUWord(&stepX) || !readUWord(&stepY)) {+    goto eofError;+  }+  if (w == 0 || h == 0 || w >= INT_MAX / h) {+    error(getPos(), "Bad bitmap size in JBIG2 halftone segment");+    return;+  }+  if (gridH == 0 || gridW >= INT_MAX / gridH) {+    error(getPos(), "Bad grid size in JBIG2 halftone segment");+    return;+  }++  // get pattern dictionary+  if (nRefSegs != 1) {+    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");+    return;+  }+  seg = findSegment(refSegs[0]);+  if (seg->getType() != jbig2SegPatternDict) {+    error(getPos(), "Bad symbol dictionary reference in JBIG2 halftone segment");+    return;+  }+  patternDict = (JBIG2PatternDict *)seg;+  bpp = 0;+  i = 1;+  while (i < patternDict->getSize()) {+    ++bpp;+    i <<= 1;+  }+  patW = patternDict->getBitmap(0)->getWidth();+  patH = patternDict->getBitmap(0)->getHeight();++  // set up the arithmetic decoder+  if (!mmr) {+    resetGenericStats(templ, NULL);+    arithDecoder->start();+  }++  // allocate the bitmap+  bitmap = new JBIG2Bitmap(segNum, w, h);+  if (flags & 0x80) { // HDEFPIXEL+    bitmap->clearToOne();+  } else {+    bitmap->clearToZero();+  }++  // compute the skip bitmap+  skipBitmap = NULL;+  if (enableSkip) {+    skipBitmap = new JBIG2Bitmap(0, gridW, gridH);+    skipBitmap->clearToZero();+    for (m = 0; m < gridH; ++m) {+      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(getPos(), "Unexpected EOF in JBIG2 stream");+}++void JBIG2Stream::readGenericRegionSeg(Guint segNum, GBool imm,+				       GBool lossless, Guint length) {+  JBIG2Bitmap *bitmap;+  Guint w, h, x, y, segInfoFlags, extCombOp;+  Guint flags, mmr, templ, tpgdOn;+  int atx[4], aty[4];++  // region segment info field+  if (!readULong(&w) || !readULong(&h) ||+      !readULong(&x) || !readULong(&y) ||+      !readUByte(&segInfoFlags)) {+    goto eofError;+  }+  extCombOp = segInfoFlags & 7;++  // rest of the generic region segment header+  if (!readUByte(&flags)) {+    goto eofError;+  }+  mmr = flags & 1;+  templ = (flags >> 1) & 3;+  tpgdOn = (flags >> 3) & 1;++  // AT flags+  if (!mmr) {+    if (templ == 0) {+      if (!readByte(&atx[0]) ||+	  !readByte(&aty[0]) ||+	  !readByte(&atx[1]) ||+	  !readByte(&aty[1]) ||+	  !readByte(&atx[2]) ||+	  !readByte(&aty[2]) ||+	  !readByte(&atx[3]) ||+	  !readByte(&aty[3])) {+	goto eofError;+      }+    } else {+      if (!readByte(&atx[0]) ||+	  !readByte(&aty[0])) {+	goto eofError;+      }+    }+  }++  // set up the arithmetic decoder+  if (!mmr) {+    resetGenericStats(templ, NULL);+    arithDecoder->start();+  }++  // read the bitmap+  bitmap = readGenericBitmap(mmr, w, h, templ, tpgdOn, gFalse,+			     NULL, atx, aty, mmr ? 0 : length - 18);++  // combine the region bitmap into the page bitmap+  if (imm) {+    if (pageH == 0xffffffff && y + h > curPageH) {+      pageBitmap->expand(y + h, pageDefPixel);+    }+    pageBitmap->combine(bitmap, x, y, extCombOp);+    delete bitmap;++  // store the region bitmap+  } else {+    bitmap->setSegNum(segNum);+    segments->append(bitmap);+  }++  return;++ eofError:+  error(getPos(), "Unexpected EOF in JBIG2 stream");+}++JBIG2Bitmap *JBIG2Stream::readGenericBitmap(GBool mmr, int w, int h,+					    int templ, GBool tpgdOn,+					    GBool useSkip, JBIG2Bitmap *skip,+					    int *atx, int *aty,+					    int mmrDataLength) {+  JBIG2Bitmap *bitmap;+  GBool ltp;+  Guint ltpCX, cx, cx0, cx1, cx2;+  JBIG2BitmapPtr cxPtr0, cxPtr1;+  JBIG2BitmapPtr atPtr0, atPtr1, atPtr2, atPtr3;+  int *refLine, *codingLine;+  int code1, code2, code3;+  int x, y, a0, pix, i, refI, codingI;++  bitmap = new JBIG2Bitmap(0, w, h);+  bitmap->clearToZero();++  //----- MMR decode++  if (mmr) {++    mmrDecoder->reset();+    refLine = (int *)gmallocn(w + 2, sizeof(int));+    codingLine = (int *)gmallocn(w + 2, sizeof(int));+    codingLine[0] = codingLine[1] = w;++    for (y = 0; y < h; ++y) {++      // copy coding line to ref line+      for (i = 0; codingLine[i] < w; ++i) {+	refLine[i] = codingLine[i];+      }+      refLine[i] = refLine[i + 1] = w;++      // decode a line+      refI = 0;     // b1 = refLine[refI]+      codingI = 0;  // a1 = codingLine[codingI]+      a0 = 0;+      do {+	code1 = mmrDecoder->get2DCode();+	switch (code1) {+	case twoDimPass:+	  if (refLine[refI] < w) {+	    a0 = refLine[refI + 1];+	    refI += 2;+	  }+	  break;+	case twoDimHoriz:+	  if (codingI & 1) {+	    code1 = 0;+	    do {+	      code1 += code3 = mmrDecoder->getBlackCode();+	    } while (code3 >= 64);+	    code2 = 0;+	    do {+	      code2 += code3 = mmrDecoder->getWhiteCode();+	    } while (code3 >= 64);+	  } else {+	    code1 = 0;+	    do {+	      code1 += code3 = mmrDecoder->getWhiteCode();+	    } while (code3 >= 64);+	    code2 = 0;+	    do {+	      code2 += code3 = mmrDecoder->getBlackCode();+	    } while (code3 >= 64);+	  }+	  if (code1 > 0 || code2 > 0) {+	    a0 = codingLine[codingI++] = a0 + code1;+	    a0 = codingLine[codingI++] = a0 + code2;+	    while (refLine[refI] <= a0 && refLine[refI] < w) {+	      refI += 2;+	    }+	  }+	  break;+	case twoDimVert0:+	  a0 = codingLine[codingI++] = refLine[refI];+	  if (refLine[refI] < w) {+	    ++refI;+	  }+	  break;+	case twoDimVertR1:+	  a0 = codingLine[codingI++] = refLine[refI] + 1;+	  if (refLine[refI] < w) {+	    ++refI;+	    while (refLine[refI] <= a0 && refLine[refI] < w) {+	      refI += 2;+	    }+	  }+	  break;+	case twoDimVertR2:+	  a0 = codingLine[codingI++] = refLine[refI] + 2;+	  if (refLine[refI] < w) {+	    ++refI;+	    while (refLine[refI] <= a0 && refLine[refI] < w) {+	      refI += 2;+	    }+	  }+	  break;+	case twoDimVertR3:+	  a0 = codingLine[codingI++] = refLine[refI] + 3;+	  if (refLine[refI] < w) {+	    ++refI;+	    while (refLine[refI] <= a0 && refLine[refI] < w) {+	      refI += 2;+	    }+	  }+	  break;+	case twoDimVertL1:+	  a0 = codingLine[codingI++] = refLine[refI] - 1;+	  if (refI > 0) {+	    --refI;+	  } else {+	    ++refI;+	  }+	  while (refLine[refI] <= a0 && refLine[refI] < w) {+	    refI += 2;+	  }+	  break;+	case twoDimVertL2:+	  a0 = codingLine[codingI++] = refLine[refI] - 2;+	  if (refI > 0) {+	    --refI;+	  } else {+	    ++refI;+	  }+	  while (refLine[refI] <= a0 && refLine[refI] < w) {+	    refI += 2;+	  }+	  break;+	case twoDimVertL3:+	  a0 = codingLine[codingI++] = refLine[refI] - 3;+	  if (refI > 0) {+	    --refI;+	  } else {+	    ++refI;+	  }+	  while (refLine[refI] <= a0 && refLine[refI] < w) {+	    refI += 2;+	  }+	  break;+	default:+	  error(getPos(), "Illegal code in JBIG2 MMR bitmap data");+	  break;+	}+      } while (a0 < w);+      codingLine[codingI++] = w;++      // convert the run lengths to a bitmap line+      i = 0;+      while (codingLine[i] < w) {+	for (x = codingLine[i]; x < codingLine[i+1]; ++x) {+	  bitmap->setPixel(x, y);+	}+	i += 2;+      }+    }++    if (mmrDataLength >= 0) {+      mmrDecoder->skipTo(mmrDataLength);+    } else {+      if (mmrDecoder->get24Bits() != 0x001001) {+	error(getPos(), "Missing EOFB in JBIG2 MMR bitmap data");+      }+    }++    gfree(refLine);+    gfree(codingLine);++  //----- arithmetic decode++  } else {+    // set up the typical row context+    ltpCX = 0; // make gcc happy+    if (tpgdOn) {+      switch (templ) {+      case 0:+	ltpCX = 0x3953; // 001 11001 0101 0011+	break;+      case 1:+	ltpCX = 0x079a; // 0011 11001 101 0+	break;+      case 2:+	ltpCX = 0x0e3; // 001 1100 01 1+	break;+      case 3:+	ltpCX = 0x18a; // 01100 0101 1+	break;+      }+    }++    ltp = 0;+    cx = cx0 = cx1 = cx2 = 0; // make gcc happy+    for (y = 0; y < h; ++y) {++      // check for a "typical" (duplicate) row+      if (tpgdOn) {+	if (arithDecoder->decodeBit(ltpCX, genericRegionStats)) {+	  ltp = !ltp;+	}+	if (ltp) {+	  bitmap->duplicateRow(y, y-1);+	  continue;+	}+      }++      switch (templ) {+      case 0:++	// set up the context+	bitmap->getPixelPtr(0, y-2, &cxPtr0);+	cx0 = bitmap->nextPixel(&cxPtr0);+	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);+	bitmap->getPixelPtr(0, y-1, &cxPtr1);+	cx1 = bitmap->nextPixel(&cxPtr1);+	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);+	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);+	cx2 = 0;+	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);+	bitmap->getPixelPtr(atx[1], y + aty[1], &atPtr1);+	bitmap->getPixelPtr(atx[2], y + aty[2], &atPtr2);+	bitmap->getPixelPtr(atx[3], y + aty[3], &atPtr3);++	// decode the row+	for (x = 0; x < w; ++x) {++	  // build the context+	  cx = (cx0 << 13) | (cx1 << 8) | (cx2 << 4) |+	       (bitmap->nextPixel(&atPtr0) << 3) |+	       (bitmap->nextPixel(&atPtr1) << 2) |+	       (bitmap->nextPixel(&atPtr2) << 1) |+	       bitmap->nextPixel(&atPtr3);++	  // check for a skipped pixel+	  if (useSkip && skip->getPixel(x, y)) {+	    pix = 0;++	  // decode the pixel+	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {+	    bitmap->setPixel(x, y);+	  }++	  // update the context+	  cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;+	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;+	  cx2 = ((cx2 << 1) | pix) & 0x0f;+	}+	break;++      case 1:++	// set up the context+	bitmap->getPixelPtr(0, y-2, &cxPtr0);+	cx0 = bitmap->nextPixel(&cxPtr0);+	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);+	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);+	bitmap->getPixelPtr(0, y-1, &cxPtr1);+	cx1 = bitmap->nextPixel(&cxPtr1);+	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);+	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);+	cx2 = 0;+	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);++	// decode the row+	for (x = 0; x < w; ++x) {++	  // build the context+	  cx = (cx0 << 9) | (cx1 << 4) | (cx2 << 1) |+	       bitmap->nextPixel(&atPtr0);++	  // check for a skipped pixel+	  if (useSkip && skip->getPixel(x, y)) {+	    pix = 0;++	  // decode the pixel+	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {+	    bitmap->setPixel(x, y);+	  }++	  // update the context+	  cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x0f;+	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;+	  cx2 = ((cx2 << 1) | pix) & 0x07;+	}+	break;++      case 2:++	// set up the context+	bitmap->getPixelPtr(0, y-2, &cxPtr0);+	cx0 = bitmap->nextPixel(&cxPtr0);+	cx0 = (cx0 << 1) | bitmap->nextPixel(&cxPtr0);+	bitmap->getPixelPtr(0, y-1, &cxPtr1);+	cx1 = bitmap->nextPixel(&cxPtr1);+	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);+	cx2 = 0;+	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);++	// decode the row+	for (x = 0; x < w; ++x) {++	  // build the context+	  cx = (cx0 << 7) | (cx1 << 3) | (cx2 << 1) |+	       bitmap->nextPixel(&atPtr0);++	  // check for a skipped pixel+	  if (useSkip && skip->getPixel(x, y)) {+	    pix = 0;++	  // decode the pixel+	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {+	    bitmap->setPixel(x, y);+	  }++	  // update the context+	  cx0 = ((cx0 << 1) | bitmap->nextPixel(&cxPtr0)) & 0x07;+	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x0f;+	  cx2 = ((cx2 << 1) | pix) & 0x03;+	}+	break;++      case 3:++	// set up the context+	bitmap->getPixelPtr(0, y-1, &cxPtr1);+	cx1 = bitmap->nextPixel(&cxPtr1);+	cx1 = (cx1 << 1) | bitmap->nextPixel(&cxPtr1);+	cx2 = 0;+	bitmap->getPixelPtr(atx[0], y + aty[0], &atPtr0);++	// decode the row+	for (x = 0; x < w; ++x) {++	  // build the context+	  cx = (cx1 << 5) | (cx2 << 1) |+	       bitmap->nextPixel(&atPtr0);++	  // check for a skipped pixel+	  if (useSkip && skip->getPixel(x, y)) {+	    pix = 0;++	  // decode the pixel+	  } else if ((pix = arithDecoder->decodeBit(cx, genericRegionStats))) {+	    bitmap->setPixel(x, y);+	  }++	  // update the context+	  cx1 = ((cx1 << 1) | bitmap->nextPixel(&cxPtr1)) & 0x1f;+	  cx2 = ((cx2 << 1) | pix) & 0x0f;+	}+	break;+      }+    }+  }++  return bitmap;+}++void JBIG2Stream::readGenericRefinementRegionSeg(Guint segNum, GBool imm,+						 GBool lossless, Guint length,+						 Guint *refSegs,+						 Guint nRefSegs) {+  JBIG2Bitmap *bitmap, *refBitmap;+  Guint w, h, x, y, segInfoFlags, extCombOp;+  Guint flags, templ, tpgrOn;+  int atx[2], aty[2];+  JBIG2Segment *seg;++  // region segment info field+  if (!readULong(&w) || !readULong(&h) ||+      !readULong(&x) || !readULong(&y) ||+      !readUByte(&segInfoFlags)) {+    goto eofError;+  }+  extCombOp = segInfoFlags & 7;++  // rest of the generic refinement region segment header+  if (!readUByte(&flags)) {+    goto eofError;+  }+  templ = flags & 1;+  tpgrOn = (flags >> 1) & 1;++  // AT flags+  if (!templ) {+    if (!readByte(&atx[0]) || !readByte(&aty[0]) ||+	!readByte(&atx[1]) || !readByte(&aty[1])) {+      goto eofError;+    }+  }++  // resize the page bitmap if needed+  if (nRefSegs == 0 || imm) {+    if (pageH == 0xffffffff && y + h > curPageH) {+      pageBitmap->expand(y + h, pageDefPixel);+    }+  }++  // get referenced bitmap+  if (nRefSegs > 1) {+    error(getPos(), "Bad reference in JBIG2 generic refinement segment");+    return;+  }+  if (nRefSegs == 1) {+    seg = findSegment(refSegs[0]);+    if (seg->getType() != jbig2SegBitmap) {+      error(getPos(), "Bad bitmap reference in JBIG2 generic refinement segment");+      return;+    }+    refBitmap = (JBIG2Bitmap *)seg;+  } else {+    refBitmap = pageBitmap->getSlice(x, y, w, h);+  }++  // set up the arithmetic decoder+  resetRefinementStats(templ, NULL);+  arithDecoder->start();++  // read+  bitmap = readGenericRefinementRegion(w, h, templ, tpgrOn,+				       refBitmap, 0, 0, atx, aty);++  // combine the region bitmap into the page bitmap+  if (imm) {+    pageBitmap->combine(bitmap, x, y, extCombOp);+    delete bitmap;++  // store the region bitmap+  } else {+    bitmap->setSegNum(segNum);+    segments->append(bitmap);+  }++  // delete the referenced bitmap+  if (nRefSegs == 1) {+    discardSegment(refSegs[0]);+  } else {+    delete refBitmap;+  }++  return;++ eofError:+  error(getPos(), "Unexpected EOF in JBIG2 stream");+}++JBIG2Bitmap *JBIG2Stream::readGenericRefinementRegion(int w, int h,+						      int templ, GBool tpgrOn,+						      JBIG2Bitmap *refBitmap,+						      int refDX, int refDY,+						      int *atx, int *aty) {+  JBIG2Bitmap *bitmap;+  GBool ltp;+  Guint ltpCX, cx, cx0, cx2, cx3, cx4, tpgrCX0, tpgrCX1, tpgrCX2;+  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);+      }++      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);+      }++      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;+  }+  pageDefPixel = (flags >> 2) & 1;+  defCombOp = (flags >> 3) & 3;++  // allocate the page bitmap+  if (pageH == 0xffffffff) {+    curPageH = striping & 0x7fff;+  } else {+    curPageH = pageH;+  }+  pageBitmap = new JBIG2Bitmap(0, pageW, curPageH);++  // default pixel value+  if (pageDefPixel) {+    pageBitmap->clearToOne();+  } else {+    pageBitmap->clearToZero();+  }++  return;++ eofError:+  error(getPos(), "Unexpected EOF in JBIG2 stream");+}++void JBIG2Stream::readEndOfStripeSeg(Guint length) {+  Guint i;++  // skip the segment+  for (i = 0; i < length; ++i) {+    curStr->getChar();+  }+}++void JBIG2Stream::readProfilesSeg(Guint length) {+  Guint i;++  // skip the segment+  for (i = 0; i < length; ++i) {+    curStr->getChar();+  }+}++void JBIG2Stream::readCodeTableSeg(Guint segNum, Guint length) {+  JBIG2HuffmanTable *huffTab;+  Guint flags, oob, prefixBits, rangeBits;+  int lowVal, highVal, val;+  Guint huffTabSize, i;++  if (!readUByte(&flags) || !readLong(&lowVal) || !readLong(&highVal)) {+    goto eofError;+  }+  oob = flags & 1;+  prefixBits = ((flags >> 1) & 7) + 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(getPos(), "Unexpected EOF in JBIG2 stream");+}++void JBIG2Stream::readExtensionSeg(Guint length) {+  Guint i;++  // skip the segment+  for (i = 0; i < length; ++i) {+    curStr->getChar();+  }+}++JBIG2Segment *JBIG2Stream::findSegment(Guint segNum) {+  JBIG2Segment *seg;+  int i;++  for (i = 0; i < globalSegments->getLength(); ++i) {+    seg = (JBIG2Segment *)globalSegments->get(i);+    if (seg->getSegNum() == segNum) {+      return seg;+    }+  }+  for (i = 0; i < segments->getLength(); ++i) {+    seg = (JBIG2Segment *)segments->get(i);+    if (seg->getSegNum() == segNum) {+      return seg;+    }+  }+  return NULL;+}++void JBIG2Stream::discardSegment(Guint segNum) {+  JBIG2Segment *seg;+  int i;++  for (i = 0; i < globalSegments->getLength(); ++i) {+    seg = (JBIG2Segment *)globalSegments->get(i);+    if (seg->getSegNum() == segNum) {+      globalSegments->del(i);+      return;+    }+  }+  for (i = 0; i < segments->getLength(); ++i) {+    seg = (JBIG2Segment *)segments->get(i);+    if (seg->getSegNum() == segNum) {+      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;+  }+  *x = (Guint)c0;+  return gTrue;+}++GBool JBIG2Stream::readByte(int *x) {+ int c0;++  if ((c0 = curStr->getChar()) == EOF) {+    return gFalse;+  }+  *x = c0;+  if (c0 & 0x80) {+    *x |= -1 - 0xff;+  }+  return gTrue;+}++GBool JBIG2Stream::readUWord(Guint *x) {+  int c0, c1;++  if ((c0 = curStr->getChar()) == EOF ||+      (c1 = curStr->getChar()) == EOF) {+    return gFalse;+  }+  *x = (Guint)((c0 << 8) | c1);+  return gTrue;+}++GBool JBIG2Stream::readULong(Guint *x) {+  int c0, c1, c2, c3;++  if ((c0 = curStr->getChar()) == EOF ||+      (c1 = curStr->getChar()) == EOF ||+      (c2 = curStr->getChar()) == EOF ||+      (c3 = curStr->getChar()) == EOF) {+    return gFalse;+  }+  *x = (Guint)((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);+  return gTrue;+}++GBool JBIG2Stream::readLong(int *x) {+  int c0, c1, c2, c3;++  if ((c0 = curStr->getChar()) == EOF ||+      (c1 = curStr->getChar()) == EOF ||+      (c2 = curStr->getChar()) == EOF ||+      (c3 = curStr->getChar()) == EOF) {+    return gFalse;+  }+  *x = ((c0 << 24) | (c1 << 16) | (c2 << 8) | c3);+  if (c0 & 0x80) {+    *x |= -1 - (int)0xffffffff;+  }+  return gTrue;+}
+ xpdf/JBIG2Stream.h view
@@ -0,0 +1,145 @@+//========================================================================+//+// 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 StreamKind getKind() { return strJBIG2; }+  virtual void reset();+  virtual void close();+  virtual int getChar();+  virtual int lookChar();+  virtual GString *getPSFilter(int psLevel, char *indent);+  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);+  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;++  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
+ xpdf/JPXStream.cc view
@@ -0,0 +1,3144 @@+//========================================================================+//+// 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 "Error.h"+#include "JArithmeticDecoder.h"+#include "JPXStream.h"++//~ to do:+//  - precincts+//  - 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?+//  - skip EPH markers (readTilePartData)+//  - handle tilePartToEOC in readTilePartData+//  - deal with multiple codeword segments (readTilePartData,+//    readCodeBlockData)+//  - progression orders 2, 3, and 4+//  - in coefficient decoding (readCodeBlockData):+//    - termination pattern: terminate after every coding pass+//    - error resilience segmentation symbol+//    - selective arithmetic coding bypass+//    - vertically causal context formation+//    - 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)++// number of bits to the right of the decimal point for the fixed+// point arithmetic used in the IDWT+#define fracBits 16++//------------------------------------------------------------------------++// 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)+{+  nComps = 0;+  bpc = NULL;+  width = height = 0;+  haveCS = gFalse;+  havePalette = gFalse;+  haveCompMap = gFalse;+  haveChannelDefn = gFalse;++  img.tiles = NULL;+  bitBuf = 0;+  bitBufLen = 0;+  bitBufSkip = gFalse;+  byteCount = 0;+}++JPXStream::~JPXStream() {+  close();+  delete str;+}++void JPXStream::reset() {+  str->reset();+  if (readBoxes()) {+    curY = img.yOffset;+  } else {+    // readBoxes reported an error, so we go immediately to EOF+    curY = img.ySize;+  }+  curX = img.xOffset;+  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 < 1; ++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->coeffs);+			  if (cb->arithDecoder) {+			    delete cb->arithDecoder;+			  }+			  if (cb->stats) {+			    delete cb->stats;+			  }+			}+			gfree(subband->cbs);+		      }+		    }+		    gfree(precinct->subbands);+		  }+		}+		gfree(img.tiles[i].tileComps[comp].resLevels[r].precincts);+	      }+	    }+	    gfree(img.tiles[i].tileComps[comp].resLevels);+	  }+	}+	gfree(img.tiles[i].tileComps);+      }+    }+    gfree(img.tiles);+    img.tiles = NULL;+  }+  FilterStream::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;++  do {+    if (curY >= img.ySize) {+      return;+    }+    tileIdx = ((curY - img.yTileOffset) / img.yTileSize) * img.nXTiles+              + (curX - 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 = jpxCeilDiv((curX - img.xTileOffset) % img.xTileSize, tileComp->hSep);+    ty = jpxCeilDiv((curY - img.yTileOffset) % img.yTileSize, tileComp->vSep);+    pix = (int)tileComp->data[ty * (tileComp->x1 - tileComp->x0) + tx];+    pixBits = tileComp->prec;+#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 = +      pixBits = palette.bpc[curComp];+    }+    if (++curComp == (Guint)(havePalette ? palette.nComps : img.nComps)) {+#endif+      curComp = 0;+      if (++curX == img.xSize) {+	curX = img.xOffset;+	++curY;+      }+    }+    if (pixBits == 8) {+      readBuf = (readBuf << 8) | (pix & 0xff);+    } else {+      readBuf = (readBuf << pixBits) | (pix & ((1 << pixBits) - 1));+    }+    readBufLen += pixBits;+  } while (readBufLen < 8);+}++GString *JPXStream::getPSFilter(int psLevel, char *indent) {+  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, i;+  int csMeth, csPrec, csPrec1, dummy2;+  StreamColorSpaceMode csMode1;+  GBool haveBPC, haveCSMode;++  csPrec = 0; // make gcc happy+  haveBPC = haveCSMode = gFalse;+  str->reset();+  if (str->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;+	      }+	      for (i = 0; i < dataLen - 7; ++i) {+		str->getChar();+	      }+	    }+	  } else {+	    for (i = 0; i < dataLen - 3; ++i) {+	      str->getChar();+	    }+	  }+	}+      } else if (boxType == 0x6A703263) { // codestream+	cover(3);+	if (!(haveBPC && haveCSMode)) {+	  getImageParams2(bitsPerComponent, csMode);+	}+	break;+      } else {+	cover(4);+	for (i = 0; i < dataLen; ++i) {+	  str->getChar();+	}+      }+    }+  }+  str->close();+}++// Get image parameters from the codestream.+void JPXStream::getImageParams2(int *bitsPerComponent,+				StreamColorSpaceMode *csMode) {+  int segType;+  Guint segLen, nComps1, bpc1, dummy, i;++  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) {+	for (i = 0; i < segLen - 2; ++i) {+	  str->getChar();+	}+      }+    }+  }+}++GBool JPXStream::readBoxes() {+  Guint boxType, boxLen, dataLen;+  Guint bpc1, compression, unknownColorspace, ipr;+  Guint i, j;++  haveImgHdr = 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 (str->lookChar() == 0xff) {+    cover(7);+    error(getPos(), "Naked JPEG 2000 codestream, missing JP2/JPX wrapper");+    readCodestream(0);+    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 gTrue;+  }++  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(&height) ||+	  !readULong(&width) ||+	  !readUWord(&nComps) ||+	  !readUByte(&bpc1) ||+	  !readUByte(&compression) ||+	  !readUByte(&unknownColorspace) ||+	  !readUByte(&ipr)) {+	error(getPos(), "Unexpected EOF in JPX stream");+	return gFalse;+      }+      if (compression != 7) {+	error(getPos(), "Unknown compression type in JPX stream");+	return gFalse;+      }+      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(getPos(), "Found bits per component box before image header box in JPX stream");+	return gFalse;+      }+      if (dataLen != nComps) {+	error(getPos(), "Invalid bits per component box in JPX stream");+	return gFalse;+      }+      for (i = 0; i < nComps; ++i) {+	if (!readUByte(&bpc[i])) {+	  error(getPos(), "Unexpected EOF in JPX stream");+	  return gFalse;+	}+      }+      break;+    case 0x636F6C72:		// color specification+      cover(11);+      if (!readColorSpecBox(dataLen)) {+	return gFalse;+      }+      break;+    case 0x70636c72:		// palette+      cover(12);+      if (!readUWord(&palette.nEntries) ||+	  !readUByte(&palette.nComps)) {+	error(getPos(), "Unexpected EOF in JPX stream");+	return gFalse;+      }+      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(getPos(), "Unexpected EOF in JPX stream");+	  return gFalse;+	}+	++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(getPos(), "Unexpected EOF in JPX stream");+	    return gFalse;+	  }+	}+      }+      havePalette = gTrue;+      break;+    case 0x636d6170:		// component mapping+      cover(13);+      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(getPos(), "Unexpected EOF in JPX stream");+	  return gFalse;+	}+      }+      haveCompMap = gTrue;+      break;+    case 0x63646566:		// channel definition+      cover(14);+      if (!readUWord(&channelDefn.nChannels)) {+	error(getPos(), "Unexpected EOF in JPX stream");+	return gFalse;+      }+      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(getPos(), "Unexpected EOF in JPX stream");+	  return gFalse;+	}+      }+      haveChannelDefn = gTrue;+      break;+    case 0x6A703263:		// contiguous codestream+      cover(15);+      if (!bpc) {+	error(getPos(), "JPX stream is missing the image header box");+      }+      if (!haveCS) {+	error(getPos(), "JPX stream has no supported color spec");+      }+      if (!readCodestream(dataLen)) {+	return gFalse;+      }+      break;+    default:+      cover(16);+      for (i = 0; i < dataLen; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Unexpected EOF in JPX stream");+	  return gFalse;+	}+      }+      break;+    }+  }+  return gTrue;+}++GBool JPXStream::readColorSpecBox(Guint dataLen) {+  JPXColorSpec newCS;+  Guint csApprox, csEnum;+  Guint i;+  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);+    for (i = 0; i < dataLen - 3; ++i) {+      if (str->getChar() == EOF) {+	goto err;+      }+    }+    break;+  }++  if (ok && (!haveCS || newCS.prec > cs.prec)) {+    cs = newCS;+    haveCS = gTrue;+  }++  return gTrue;++ err:+  error(getPos(), "Error in JPX color spec");+  return gFalse;+}++GBool JPXStream::readCodestream(Guint len) {+  JPXTile *tile;+  JPXTileComp *tileComp;+  int segType;+  GBool haveSIZ, haveCOD, haveQCD, haveSOT;+  Guint precinctSize, style;+  Guint segLen, capabilities, comp, i, j, r;++  //----- main header+  haveSIZ = haveCOD = haveQCD = haveSOT = gFalse;+  do {+    if (!readMarkerHdr(&segType, &segLen)) {+      error(getPos(), "Error in JPX codestream");+      return gFalse;+    }+    switch (segType) {+    case 0x4f:			// SOC - start of codestream+      // marker only+      cover(19);+      break;+    case 0x51:			// SIZ - image and tile size+      cover(20);+      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(getPos(), "Error in JPX SIZ marker segment");+	return gFalse;+      }+      if (haveImgHdr && img.nComps != nComps) {+	error(getPos(), "Different number of components in JPX SIZ marker segment");+	return gFalse;+      }+      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(getPos(), "Bad tile count in JPX SIZ marker segment");+	return gFalse;+      }+      img.tiles = (JPXTile *)gmallocn(img.nXTiles * img.nYTiles,+				      sizeof(JPXTile));+      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(getPos(), "Error in JPX SIZ marker segment");+	  return gFalse;+	}+	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 (!readUByte(&img.tiles[0].tileComps[0].style) ||+	  !readUByte(&img.tiles[0].progOrder) ||+	  !readUWord(&img.tiles[0].nLayers) ||+	  !readUByte(&img.tiles[0].multiComp) ||+	  !readUByte(&img.tiles[0].tileComps[0].nDecompLevels) ||+	  !readUByte(&img.tiles[0].tileComps[0].codeBlockW) ||+	  !readUByte(&img.tiles[0].tileComps[0].codeBlockH) ||+	  !readUByte(&img.tiles[0].tileComps[0].codeBlockStyle) ||+	  !readUByte(&img.tiles[0].tileComps[0].transform)) {+	error(getPos(), "Error in JPX COD marker segment");+	return gFalse;+      }+      img.tiles[0].tileComps[0].codeBlockW += 2;+      img.tiles[0].tileComps[0].codeBlockH += 2;+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {+	if (i != 0) {+	  img.tiles[i].progOrder = img.tiles[0].progOrder;+	  img.tiles[i].nLayers = img.tiles[0].nLayers;+	  img.tiles[i].multiComp = img.tiles[0].multiComp;+	}+	for (comp = 0; comp < img.nComps; ++comp) {+	  if (!(i == 0 && comp == 0)) {+	    img.tiles[i].tileComps[comp].style =+	        img.tiles[0].tileComps[0].style;+	    img.tiles[i].tileComps[comp].nDecompLevels =+	        img.tiles[0].tileComps[0].nDecompLevels;+	    img.tiles[i].tileComps[comp].codeBlockW =+	        img.tiles[0].tileComps[0].codeBlockW;+	    img.tiles[i].tileComps[comp].codeBlockH =+	        img.tiles[0].tileComps[0].codeBlockH;+	    img.tiles[i].tileComps[comp].codeBlockStyle =+	        img.tiles[0].tileComps[0].codeBlockStyle;+	    img.tiles[i].tileComps[comp].transform =+	        img.tiles[0].tileComps[0].transform;+	  }+	  img.tiles[i].tileComps[comp].resLevels =+	      (JPXResLevel *)gmallocn(+		     (img.tiles[i].tileComps[comp].nDecompLevels + 1),+		     sizeof(JPXResLevel));+	  for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {+	    img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;+	  }+	}+      }+      for (r = 0; r <= img.tiles[0].tileComps[0].nDecompLevels; ++r) {+	if (img.tiles[0].tileComps[0].style & 0x01) {+	  cover(91);+	  if (!readUByte(&precinctSize)) {+	    error(getPos(), "Error in JPX COD marker segment");+	    return gFalse;+	  }+	  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 <= img.tiles[i].tileComps[comp].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(getPos(), "JPX COC marker segment before COD segment");+	return gFalse;+      }+      if ((img.nComps > 256 && !readUWord(&comp)) ||+	  (img.nComps <= 256 && !readUByte(&comp)) ||+	  comp >= img.nComps ||+	  !readUByte(&style) ||+	  !readUByte(&img.tiles[0].tileComps[comp].nDecompLevels) ||+	  !readUByte(&img.tiles[0].tileComps[comp].codeBlockW) ||+	  !readUByte(&img.tiles[0].tileComps[comp].codeBlockH) ||+	  !readUByte(&img.tiles[0].tileComps[comp].codeBlockStyle) ||+	  !readUByte(&img.tiles[0].tileComps[comp].transform)) {+	error(getPos(), "Error in JPX COC marker segment");+	return gFalse;+      }+      img.tiles[0].tileComps[comp].style =+	  (img.tiles[0].tileComps[comp].style & ~1) | (style & 1);+      img.tiles[0].tileComps[comp].codeBlockW += 2;+      img.tiles[0].tileComps[comp].codeBlockH += 2;+      for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {+	if (i != 0) {+	  img.tiles[i].tileComps[comp].style =+	      img.tiles[0].tileComps[comp].style;+	  img.tiles[i].tileComps[comp].nDecompLevels =+	      img.tiles[0].tileComps[comp].nDecompLevels;+	  img.tiles[i].tileComps[comp].codeBlockW =+	      img.tiles[0].tileComps[comp].codeBlockW;+	  img.tiles[i].tileComps[comp].codeBlockH =+	      img.tiles[0].tileComps[comp].codeBlockH;+	  img.tiles[i].tileComps[comp].codeBlockStyle =+	      img.tiles[0].tileComps[comp].codeBlockStyle;+	  img.tiles[i].tileComps[comp].transform =+	      img.tiles[0].tileComps[comp].transform;+	}+	img.tiles[i].tileComps[comp].resLevels =+	    (JPXResLevel *)greallocn(+		     img.tiles[i].tileComps[comp].resLevels,+		     (img.tiles[i].tileComps[comp].nDecompLevels + 1),+		     sizeof(JPXResLevel));+	for (r = 0; r <= img.tiles[i].tileComps[comp].nDecompLevels; ++r) {+	  img.tiles[i].tileComps[comp].resLevels[r].precincts = NULL;+	}+      }+      for (r = 0; r <= img.tiles[0].tileComps[comp].nDecompLevels; ++r) {+	if (img.tiles[0].tileComps[comp].style & 0x01) {+	  if (!readUByte(&precinctSize)) {+	    error(getPos(), "Error in JPX COD marker segment");+	    return gFalse;+	  }+	  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 (!readUByte(&img.tiles[0].tileComps[0].quantStyle)) {+	error(getPos(), "Error in JPX QCD marker segment");+	return gFalse;+      }+      if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x00) {+	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(getPos(), "Error in JPX QCD marker segment");+	    return gFalse;+	  }+	}+      } 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(getPos(), "Error in JPX QCD marker segment");+	  return gFalse;+	}+      } else if ((img.tiles[0].tileComps[0].quantStyle & 0x1f) == 0x02) {+	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(getPos(), "Error in JPX QCD marker segment");+	    return gFalse;+	  }+	}+      } else {+	error(getPos(), "Error in JPX QCD marker segment");+	return gFalse;+      }+      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(getPos(), "JPX QCC marker segment before QCD segment");+	return gFalse;+      }+      if ((img.nComps > 256 && !readUWord(&comp)) ||+	  (img.nComps <= 256 && !readUByte(&comp)) ||+	  comp >= img.nComps ||+	  !readUByte(&img.tiles[0].tileComps[comp].quantStyle)) {+	error(getPos(), "Error in JPX QCC marker segment");+	return gFalse;+      }+      if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x00) {+	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(getPos(), "Error in JPX QCC marker segment");+	    return gFalse;+	  }+	}+      } 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(getPos(), "Error in JPX QCC marker segment");+	  return gFalse;+	}+      } else if ((img.tiles[0].tileComps[comp].quantStyle & 0x1f) == 0x02) {+	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(getPos(), "Error in JPX QCD marker segment");+	    return gFalse;+	  }+	}+      } else {+	error(getPos(), "Error in JPX QCC marker segment");+	return gFalse;+      }+      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+      fprintf(stderr, "RGN\n");+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PPM marker segment");+	  return gFalse;+	}+      }+#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(getPos(), "Error in JPX RGN marker segment");+	return gFalse;+      }+#endif+      break;+    case 0x5f:			// POC - progression order change+      cover(26);+#if 1 //~ progression order changes are unimplemented+      fprintf(stderr, "POC\n");+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PPM marker segment");+	  return gFalse;+	}+      }+#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(getPos(), "Error in JPX POC marker segment");+	  return gFalse;+	}+      }+#endif+      break;+    case 0x60:			// PPM - packed packet headers, main header+      cover(27);+#if 1 //~ packed packet headers are unimplemented+      fprintf(stderr, "PPM\n");+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PPM marker segment");+	  return gFalse;+	}+      }+#endif+      break;+    case 0x55:			// TLM - tile-part lengths+      // skipped+      cover(28);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX TLM marker segment");+	  return gFalse;+	}+      }+      break;+    case 0x57:			// PLM - packet length, main header+      // skipped+      cover(29);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PLM marker segment");+	  return gFalse;+	}+      }+      break;+    case 0x63:			// CRG - component registration+      // skipped+      cover(30);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX CRG marker segment");+	  return gFalse;+	}+      }+      break;+    case 0x64:			// COM - comment+      // skipped+      cover(31);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX COM marker segment");+	  return gFalse;+	}+      }+      break;+    case 0x90:			// SOT - start of tile+      cover(32);+      haveSOT = gTrue;+      break;+    default:+      cover(33);+      error(getPos(), "Unknown marker segment %02x in JPX stream", segType);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  break;+	}+      }+      break;+    }+  } while (!haveSOT);++  if (!haveSIZ) {+    error(getPos(), "Missing SIZ marker segment in JPX stream");+    return gFalse;+  }+  if (!haveCOD) {+    error(getPos(), "Missing COD marker segment in JPX stream");+    return gFalse;+  }+  if (!haveQCD) {+    error(getPos(), "Missing QCD marker segment in JPX stream");+    return gFalse;+  }++  //----- read the tile-parts+  while (1) {+    if (!readTilePart()) {+      return gFalse;+    }+    if (!readMarkerHdr(&segType, &segLen)) {+      error(getPos(), "Error in JPX codestream");+      return gFalse;+    }+    if (segType != 0x90) {	// SOT - start of tile+      break;+    }+  }++  if (segType != 0xd9) {	// EOC - end of codestream+    error(getPos(), "Missing EOC marker in JPX codestream");+    return gFalse;+  }++  //----- finish decoding the image+  for (i = 0; i < img.nXTiles * img.nYTiles; ++i) {+    tile = &img.tiles[i];+    for (comp = 0; comp < img.nComps; ++comp) {+      tileComp = &tile->tileComps[comp];+      inverseTransform(tileComp);+    }+    if (!inverseMultiCompAndDC(tile)) {+      return gFalse;+    }+  }++  //~ can free memory below tileComps here, and also tileComp.buf++  return gTrue;+}++GBool JPXStream::readTilePart() {+  JPXTile *tile;+  JPXTileComp *tileComp;+  JPXResLevel *resLevel;+  JPXPrecinct *precinct;+  JPXSubband *subband;+  JPXCodeBlock *cb;+  GBool haveSOD;+  Guint tileIdx, tilePartLen, tilePartIdx, nTileParts;+  GBool tilePartToEOC;+  Guint precinctSize, style;+  Guint n, nSBs, nx, ny, sbx0, sby0, comp, segLen;+  Guint i, j, k, cbX, cbY, r, pre, sb, cbi;+  int segType, level;++  // process the SOT marker segment+  if (!readUWord(&tileIdx) ||+      !readULong(&tilePartLen) ||+      !readUByte(&tilePartIdx) ||+      !readUByte(&nTileParts)) {+    error(getPos(), "Error in JPX SOT marker segment");+    return gFalse;+  }++  if (tileIdx >= img.nXTiles * img.nYTiles) {+    error(getPos(), "Weird tile index in JPX stream");+    return gFalse;+  }++  tilePartToEOC = tilePartLen == 0;+  tilePartLen -= 12; // subtract size of SOT segment++  haveSOD = gFalse;+  do {+    if (!readMarkerHdr(&segType, &segLen)) {+      error(getPos(), "Error in JPX tile-part codestream");+      return gFalse;+    }+    tilePartLen -= 2 + segLen;+    switch (segType) {+    case 0x52:			// COD - coding style default+      cover(34);+      if (!readUByte(&img.tiles[tileIdx].tileComps[0].style) ||+	  !readUByte(&img.tiles[tileIdx].progOrder) ||+	  !readUWord(&img.tiles[tileIdx].nLayers) ||+	  !readUByte(&img.tiles[tileIdx].multiComp) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[0].nDecompLevels) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockW) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockH) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[0].codeBlockStyle) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[0].transform)) {+	error(getPos(), "Error in JPX COD marker segment");+	return gFalse;+      }+      img.tiles[tileIdx].tileComps[0].codeBlockW += 2;+      img.tiles[tileIdx].tileComps[0].codeBlockH += 2;+      for (comp = 0; comp < img.nComps; ++comp) {+	if (comp != 0) {+	  img.tiles[tileIdx].tileComps[comp].style =+	      img.tiles[tileIdx].tileComps[0].style;+	  img.tiles[tileIdx].tileComps[comp].nDecompLevels =+	      img.tiles[tileIdx].tileComps[0].nDecompLevels;+	  img.tiles[tileIdx].tileComps[comp].codeBlockW =+	      img.tiles[tileIdx].tileComps[0].codeBlockW;+	  img.tiles[tileIdx].tileComps[comp].codeBlockH =+	      img.tiles[tileIdx].tileComps[0].codeBlockH;+	  img.tiles[tileIdx].tileComps[comp].codeBlockStyle =+	      img.tiles[tileIdx].tileComps[0].codeBlockStyle;+	  img.tiles[tileIdx].tileComps[comp].transform =+	      img.tiles[tileIdx].tileComps[0].transform;+	}+	img.tiles[tileIdx].tileComps[comp].resLevels =+	    (JPXResLevel *)greallocn(+		     img.tiles[tileIdx].tileComps[comp].resLevels,+		     (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),+		     sizeof(JPXResLevel));+	for (r = 0;+	     r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels;+	     ++r) {+	  img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;+	}+      }+      for (r = 0; r <= img.tiles[tileIdx].tileComps[0].nDecompLevels; ++r) {+	if (img.tiles[tileIdx].tileComps[0].style & 0x01) {+	  if (!readUByte(&precinctSize)) {+	    error(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 <= img.tiles[tileIdx].tileComps[comp].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 ((img.nComps > 256 && !readUWord(&comp)) ||+	  (img.nComps <= 256 && !readUByte(&comp)) ||+	  comp >= img.nComps ||+	  !readUByte(&style) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[comp].nDecompLevels) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockW) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockH) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[comp].codeBlockStyle) ||+	  !readUByte(&img.tiles[tileIdx].tileComps[comp].transform)) {+	error(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].codeBlockW += 2;+      img.tiles[tileIdx].tileComps[comp].codeBlockH += 2;+      img.tiles[tileIdx].tileComps[comp].resLevels =+	  (JPXResLevel *)greallocn(+		     img.tiles[tileIdx].tileComps[comp].resLevels,+		     (img.tiles[tileIdx].tileComps[comp].nDecompLevels + 1),+		     sizeof(JPXResLevel));+      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {+	img.tiles[tileIdx].tileComps[comp].resLevels[r].precincts = NULL;+      }+      for (r = 0; r <= img.tiles[tileIdx].tileComps[comp].nDecompLevels; ++r) {+	if (img.tiles[tileIdx].tileComps[comp].style & 0x01) {+	  if (!readUByte(&precinctSize)) {+	    error(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 (!readUByte(&img.tiles[tileIdx].tileComps[0].quantStyle)) {+	error(getPos(), "Error in JPX QCD marker segment");+	return gFalse;+      }+      if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x00) {+	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(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(getPos(), "Error in JPX QCD marker segment");+	  return gFalse;+	}+      } else if ((img.tiles[tileIdx].tileComps[0].quantStyle & 0x1f) == 0x02) {+	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(getPos(), "Error in JPX QCD marker segment");+	    return gFalse;+	  }+	}+      } else {+	error(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 ((img.nComps > 256 && !readUWord(&comp)) ||+	  (img.nComps <= 256 && !readUByte(&comp)) ||+	  comp >= img.nComps ||+	  !readUByte(&img.tiles[tileIdx].tileComps[comp].quantStyle)) {+	error(getPos(), "Error in JPX QCC marker segment");+	return gFalse;+      }+      if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f) == 0x00) {+	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(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(getPos(), "Error in JPX QCC marker segment");+	  return gFalse;+	}+      } else if ((img.tiles[tileIdx].tileComps[comp].quantStyle & 0x1f)+		 == 0x02) {+	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(getPos(), "Error in JPX QCD marker segment");+	    return gFalse;+	  }+	}+      } else {+	error(getPos(), "Error in JPX QCC marker segment");+	return gFalse;+      }+      break;+    case 0x5e:			// RGN - region of interest+      cover(38);+#if 1 //~ ROI is unimplemented+      fprintf(stderr, "RGN\n");+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PPM 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(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+      fprintf(stderr, "POC\n");+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PPM 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(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+      fprintf(stderr, "PPT\n");+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PPT marker segment");+	  return gFalse;+	}+      }+#endif+    case 0x58:			// PLT - packet length, tile-part header+      // skipped+      cover(41);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(getPos(), "Error in JPX PLT marker segment");+	  return gFalse;+	}+      }+      break;+    case 0x64:			// COM - comment+      // skipped+      cover(42);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  error(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(getPos(), "Unknown marker segment %02x in JPX tile-part stream",+	    segType);+      for (i = 0; i < segLen - 2; ++i) {+	if (str->getChar() == EOF) {+	  break;+	}+      }+      break;+    }+  } while (!haveSOD);++  //----- 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->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->hSep);+      tileComp->x1 = jpxCeilDiv(tile->x1, tileComp->hSep);+      tileComp->y1 = jpxCeilDiv(tile->y1, tileComp->hSep);+      tileComp->cbW = 1 << tileComp->codeBlockW;+      tileComp->cbH = 1 << tileComp->codeBlockH;+      tileComp->data = (int *)gmallocn((tileComp->x1 - tileComp->x0) *+				       (tileComp->y1 - tileComp->y0),+				       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];+	k = r == 0 ? tileComp->nDecompLevels+	           : tileComp->nDecompLevels - r + 1;+	resLevel->x0 = jpxCeilDivPow2(tileComp->x0, k);+	resLevel->y0 = jpxCeilDivPow2(tileComp->y0, k);+	resLevel->x1 = jpxCeilDivPow2(tileComp->x1, k);+	resLevel->y1 = jpxCeilDivPow2(tileComp->y1, k);+	if (r == 0) {+	  resLevel->bx0[0] = resLevel->x0;+	  resLevel->by0[0] = resLevel->y0;+	  resLevel->bx1[0] = resLevel->x1;+	  resLevel->by1[0] = resLevel->y1;+	} else {+	  resLevel->bx0[0] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);+	  resLevel->by0[0] = resLevel->y0;+	  resLevel->bx1[0] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);+	  resLevel->by1[0] = resLevel->y1;+	  resLevel->bx0[1] = resLevel->x0;+	  resLevel->by0[1] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);+	  resLevel->bx1[1] = resLevel->x1;+	  resLevel->by1[1] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);+	  resLevel->bx0[2] = jpxCeilDivPow2(tileComp->x0 - (1 << (k-1)), k);+	  resLevel->by0[2] = jpxCeilDivPow2(tileComp->y0 - (1 << (k-1)), k);+	  resLevel->bx1[2] = jpxCeilDivPow2(tileComp->x1 - (1 << (k-1)), k);+	  resLevel->by1[2] = jpxCeilDivPow2(tileComp->y1 - (1 << (k-1)), k);+	}+	resLevel->precincts = (JPXPrecinct *)gmallocn(1, sizeof(JPXPrecinct));+	for (pre = 0; pre < 1; ++pre) {+	  precinct = &resLevel->precincts[pre];+	  precinct->x0 = resLevel->x0;+	  precinct->y0 = resLevel->y0;+	  precinct->x1 = resLevel->x1;+	  precinct->y1 = resLevel->y1;+	  nSBs = r == 0 ? 1 : 3;+	  precinct->subbands =+	      (JPXSubband *)gmallocn(nSBs, sizeof(JPXSubband));+	  for (sb = 0; sb < nSBs; ++sb) {+	    subband = &precinct->subbands[sb];+	    subband->x0 = resLevel->bx0[sb];+	    subband->y0 = resLevel->by0[sb];+	    subband->x1 = resLevel->bx1[sb];+	    subband->y1 = resLevel->by1[sb];+	    subband->nXCBs = jpxCeilDivPow2(subband->x1,+					    tileComp->codeBlockW)+	                     - jpxFloorDivPow2(subband->x0,+					       tileComp->codeBlockW);+	    subband->nYCBs = jpxCeilDivPow2(subband->y1,+					    tileComp->codeBlockH)+	                     - jpxFloorDivPow2(subband->y0,+					       tileComp->codeBlockH);+	    n = subband->nXCBs > subband->nYCBs ? subband->nXCBs+	                                        : subband->nYCBs;+	    for (subband->maxTTLevel = 0, --n;+		 n;+		 ++subband->maxTTLevel, n >>= 1) ;+	    n = 0;+	    for (level = subband->maxTTLevel; level >= 0; --level) {+	      nx = jpxCeilDivPow2(subband->nXCBs, level);+	      ny = jpxCeilDivPow2(subband->nYCBs, level);+	      n += nx * ny;+	    }+	    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));+	    sbx0 = jpxFloorDivPow2(subband->x0, tileComp->codeBlockW);+	    sby0 = jpxFloorDivPow2(subband->y0, tileComp->codeBlockH);+	    cb = subband->cbs;+	    for (cbY = 0; cbY < subband->nYCBs; ++cbY) {+	      for (cbX = 0; cbX < subband->nXCBs; ++cbX) {+		cb->x0 = (sbx0 + cbX) << tileComp->codeBlockW;+		cb->x1 = cb->x0 + tileComp->cbW;+		if (subband->x0 > cb->x0) {+		  cb->x0 = subband->x0;+		}+		if (subband->x1 < cb->x1) {+		  cb->x1 = subband->x1;+		}+		cb->y0 = (sby0 + cbY) << tileComp->codeBlockH;+		cb->y1 = cb->y0 + tileComp->cbH;+		if (subband->y0 > cb->y0) {+		  cb->y0 = subband->y0;+		}+		if (subband->y1 < cb->y1) {+		  cb->y1 = subband->y1;+		}+		cb->seen = gFalse;+		cb->lBlock = 3;+		cb->nextPass = jpxPassCleanup;+		cb->nZeroBitPlanes = 0;+		cb->coeffs =+		    (JPXCoeff *)gmallocn((1 << (tileComp->codeBlockW+						+ tileComp->codeBlockH)),+					 sizeof(JPXCoeff));+		for (cbi = 0;+		     cbi < (Guint)(1 << (tileComp->codeBlockW+					 + tileComp->codeBlockH));+		     ++cbi) {+		  cb->coeffs[cbi].flags = 0;+		  cb->coeffs[cbi].len = 0;+		  cb->coeffs[cbi].mag = 0;+		}+		cb->arithDecoder = NULL;+		cb->stats = NULL;+		++cb;+	      }+	    }+	  }+	}+      }+    }+  }++  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];++  // 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];++    //----- packet header++    // setup+    startBitBuf(tilePartLen);++    // 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;+	      }++	      // length of compressed data+	      //~ deal with multiple codeword segments+	      for (n = cb->lBlock, i = cb->nCodingPasses >> 1;+		   i;+		   ++n, i >>= 1) ;+	      if (!readBits(n, &cb->dataLen)) {+		goto err;+	      }+	    }+	  }+	}+      }+    }+    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;+	    }+	    tilePartLen -= cb->dataLen;+	    cb->seen = gTrue;+	  }+	}+      }+    }++    //----- next packet++    switch (tile->progOrder) {+    case 0: // layer, resolution level, component, precinct+      cover(58);+      if (++tile->comp == img.nComps) {+	tile->comp = 0;+	if (++tile->res == tile->maxNDecompLevels + 1) {+	  tile->res = 0;+	  if (++tile->layer == tile->nLayers) {+	    tile->layer = 0;+	  }+	}+      }+      break;+    case 1: // resolution level, layer, component, precinct+      cover(59);+      if (++tile->comp == img.nComps) {+	tile->comp = 0;+	if (++tile->layer == tile->nLayers) {+	  tile->layer = 0;+	  if (++tile->res == tile->maxNDecompLevels + 1) {+	    tile->res = 0;+	  }+	}+      }+      break;+    case 2: // resolution level, precinct, component, layer+      //~ this isn't correct -- see B.12.1.3+      cover(60);+      if (++tile->layer == tile->nLayers) {+	tile->layer = 0;+	if (++tile->comp == img.nComps) {+	  tile->comp = 0;+	  if (++tile->res == tile->maxNDecompLevels + 1) {+	    tile->res = 0;+	  }+	}+      }+      break;+    case 3: // precinct, component, resolution level, layer+      //~ this isn't correct -- see B.12.1.4+      cover(61);+      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;+	  }+	}+      }+      break;+    case 4: // component, precinct, resolution level, layer+      //~ this isn't correct -- see B.12.1.5+      cover(62);+      if (++tile->layer == tile->nLayers) {+	tile->layer = 0;+	if (++tile->res == tile->maxNDecompLevels + 1) {+	  tile->res = 0;+	  if (++tile->comp == img.nComps) {+	    tile->comp = 0;+	  }+	}+      }+      break;+    }+  }++  return gTrue;++ err:+  error(getPos(), "Error in JPX stream");+  return gFalse;+}++GBool JPXStream::readCodeBlockData(JPXTileComp *tileComp,+				   JPXResLevel *resLevel,+				   JPXPrecinct *precinct,+				   JPXSubband *subband,+				   Guint res, Guint sb,+				   JPXCodeBlock *cb) {+  JPXCoeff *coeff0, *coeff1, *coeff;+  Guint horiz, vert, diag, all, cx, xorBit;+  int horizSign, vertSign;+  Guint i, x, y0, y1, y2;++  if (cb->arithDecoder) {+    cover(63);+    cb->arithDecoder->restart(cb->dataLen);+  } else {+    cover(64);+    cb->arithDecoder = new JArithmeticDecoder();+    cb->arithDecoder->setStream(str, cb->dataLen);+    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) {+    switch (cb->nextPass) {++    //----- significance propagation pass+    case jpxPassSigProp:+      cover(65);+      for (y0 = cb->y0, coeff0 = cb->coeffs;+	   y0 < cb->y1;+	   y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {+	for (x = cb->x0, coeff1 = coeff0;+	     x < cb->x1;+	     ++x, ++coeff1) {+	  for (y1 = 0, coeff = coeff1;+	       y1 < 4 && y0+y1 < cb->y1;+	       ++y1, coeff += tileComp->cbW) {+	    if (!(coeff->flags & jpxCoeffSignificant)) {+	      horiz = vert = diag = 0;+	      horizSign = vertSign = 2;+	      if (x > cb->x0) {+		if (coeff[-1].flags & jpxCoeffSignificant) {+		  ++horiz;+		  horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;+		}+		if (y0+y1 > cb->y0) {+		  diag += (coeff[-(int)tileComp->cbW - 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+		if (y0+y1 < cb->y1 - 1) {+		  diag += (coeff[tileComp->cbW - 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+	      }+	      if (x < cb->x1 - 1) {+		if (coeff[1].flags & jpxCoeffSignificant) {+		  ++horiz;+		  horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;+		}+		if (y0+y1 > cb->y0) {+		  diag += (coeff[-(int)tileComp->cbW + 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+		if (y0+y1 < cb->y1 - 1) {+		  diag += (coeff[tileComp->cbW + 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+	      }+	      if (y0+y1 > cb->y0) {+		if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {+		  ++vert;+		  vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)+		              ? -1 : 1;+		}+	      }+	      if (y0+y1 < cb->y1 - 1) {+		if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {+		  ++vert;+		  vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)+		              ? -1 : 1;+		}+	      }+	      cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];+	      if (cx != 0) {+		if (cb->arithDecoder->decodeBit(cx, cb->stats)) {+		  coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;+		  coeff->mag = (coeff->mag << 1) | 1;+		  cx = signContext[horizSign][vertSign][0];+		  xorBit = signContext[horizSign][vertSign][1];+		  if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {+		    coeff->flags |= jpxCoeffSign;+		  }+		}+		++coeff->len;+		coeff->flags |= jpxCoeffTouched;+	      }+	    }+	  }+	}+      }+      ++cb->nextPass;+      break;++    //----- magnitude refinement pass+    case jpxPassMagRef:+      cover(66);+      for (y0 = cb->y0, coeff0 = cb->coeffs;+	   y0 < cb->y1;+	   y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {+	for (x = cb->x0, coeff1 = coeff0;+	     x < cb->x1;+	     ++x, ++coeff1) {+	  for (y1 = 0, coeff = coeff1;+	       y1 < 4 && y0+y1 < cb->y1;+	       ++y1, coeff += tileComp->cbW) {+	    if ((coeff->flags & jpxCoeffSignificant) &&+		!(coeff->flags & jpxCoeffTouched)) {+	      if (coeff->flags & jpxCoeffFirstMagRef) {+		all = 0;+		if (x > cb->x0) {+		  all += (coeff[-1].flags >> jpxCoeffSignificantB) & 1;+		  if (y0+y1 > cb->y0) {+		    all += (coeff[-(int)tileComp->cbW - 1].flags+			    >> jpxCoeffSignificantB) & 1;+		  }+		  if (y0+y1 < cb->y1 - 1) {+		    all += (coeff[tileComp->cbW - 1].flags+			    >> jpxCoeffSignificantB) & 1;+		  }+		}+		if (x < cb->x1 - 1) {+		  all += (coeff[1].flags >> jpxCoeffSignificantB) & 1;+		  if (y0+y1 > cb->y0) {+		    all += (coeff[-(int)tileComp->cbW + 1].flags+			    >> jpxCoeffSignificantB) & 1;+		  }+		  if (y0+y1 < cb->y1 - 1) {+		    all += (coeff[tileComp->cbW + 1].flags+			    >> jpxCoeffSignificantB) & 1;+		  }+		}+		if (y0+y1 > cb->y0) {+		  all += (coeff[-(int)tileComp->cbW].flags+			  >> jpxCoeffSignificantB) & 1;+		}+		if (y0+y1 < cb->y1 - 1) {+		  all += (coeff[tileComp->cbW].flags+			  >> jpxCoeffSignificantB) & 1;+		}+		cx = all ? 15 : 14;+	      } else {+		cx = 16;+	      }+	      coeff->mag = (coeff->mag << 1) |+		           cb->arithDecoder->decodeBit(cx, cb->stats);+	      ++coeff->len;+	      coeff->flags |= jpxCoeffTouched;+	      coeff->flags &= ~jpxCoeffFirstMagRef;+	    }+	  }+	}+      }+      ++cb->nextPass;+      break;++    //----- cleanup pass+    case jpxPassCleanup:+      cover(67);+      for (y0 = cb->y0, coeff0 = cb->coeffs;+	   y0 < cb->y1;+	   y0 += 4, coeff0 += 4 << tileComp->codeBlockW) {+	for (x = cb->x0, coeff1 = coeff0;+	     x < cb->x1;+	     ++x, ++coeff1) {+	  y1 = 0;+	  if (y0 + 3 < cb->y1 &&+	      !(coeff1->flags & jpxCoeffTouched) &&+	      !(coeff1[tileComp->cbW].flags & jpxCoeffTouched) &&+	      !(coeff1[2 * tileComp->cbW].flags & jpxCoeffTouched) &&+	      !(coeff1[3 * tileComp->cbW].flags & jpxCoeffTouched) &&+	      (x == cb->x0 || y0 == cb->y0 ||+	       !(coeff1[-(int)tileComp->cbW - 1].flags+		 & jpxCoeffSignificant)) &&+	      (y0 == cb->y0 ||+	       !(coeff1[-(int)tileComp->cbW].flags+		 & jpxCoeffSignificant)) &&+	      (x == cb->x1 - 1 || y0 == cb->y0 ||+	       !(coeff1[-(int)tileComp->cbW + 1].flags+		 & jpxCoeffSignificant)) &&+	      (x == cb->x0 ||+	       (!(coeff1[-1].flags & jpxCoeffSignificant) &&+		!(coeff1[tileComp->cbW - 1].flags+		  & jpxCoeffSignificant) &&+		!(coeff1[2 * tileComp->cbW - 1].flags+		  & jpxCoeffSignificant) && +		!(coeff1[3 * tileComp->cbW - 1].flags+		  & jpxCoeffSignificant))) &&+	      (x == cb->x1 - 1 ||+	       (!(coeff1[1].flags & jpxCoeffSignificant) &&+		!(coeff1[tileComp->cbW + 1].flags+		  & jpxCoeffSignificant) &&+		!(coeff1[2 * tileComp->cbW + 1].flags+		  & jpxCoeffSignificant) &&+		!(coeff1[3 * tileComp->cbW + 1].flags+		  & jpxCoeffSignificant))) &&+	      (x == cb->x0 || y0+4 == cb->y1 ||+	       !(coeff1[4 * tileComp->cbW - 1].flags & jpxCoeffSignificant)) &&+	      (y0+4 == cb->y1 ||+	       !(coeff1[4 * tileComp->cbW].flags & jpxCoeffSignificant)) &&+	      (x == cb->x1 - 1 || y0+4 == cb->y1 ||+	       !(coeff1[4 * tileComp->cbW + 1].flags+		 & jpxCoeffSignificant))) {+	    if (cb->arithDecoder->decodeBit(jpxContextRunLength, cb->stats)) {+	      y1 = cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);+	      y1 = (y1 << 1) |+		   cb->arithDecoder->decodeBit(jpxContextUniform, cb->stats);+	      for (y2 = 0, coeff = coeff1;+		   y2 < y1;+		   ++y2, coeff += tileComp->cbW) {+		++coeff->len;+	      }+	      coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;+	      coeff->mag = (coeff->mag << 1) | 1;+	      ++coeff->len;+	      cx = signContext[2][2][0];+	      xorBit = signContext[2][2][1];+	      if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {+		coeff->flags |= jpxCoeffSign;+	      }+	      ++y1;+	    } else {+	      for (y1 = 0, coeff = coeff1;+		   y1 < 4;+		   ++y1, coeff += tileComp->cbW) {+		++coeff->len;+	      }+	      y1 = 4;+	    }+	  }+	  for (coeff = &coeff1[y1 << tileComp->codeBlockW];+	       y1 < 4 && y0 + y1 < cb->y1;+	       ++y1, coeff += tileComp->cbW) {+	    if (!(coeff->flags & jpxCoeffTouched)) {+	      horiz = vert = diag = 0;+	      horizSign = vertSign = 2;+	      if (x > cb->x0) {+		if (coeff[-1].flags & jpxCoeffSignificant) {+		  ++horiz;+		  horizSign += (coeff[-1].flags & jpxCoeffSign) ? -1 : 1;+		}+		if (y0+y1 > cb->y0) {+		  diag += (coeff[-(int)tileComp->cbW - 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+		if (y0+y1 < cb->y1 - 1) {+		  diag += (coeff[tileComp->cbW - 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+	      }+	      if (x < cb->x1 - 1) {+		if (coeff[1].flags & jpxCoeffSignificant) {+		  ++horiz;+		  horizSign += (coeff[1].flags & jpxCoeffSign) ? -1 : 1;+		}+		if (y0+y1 > cb->y0) {+		  diag += (coeff[-(int)tileComp->cbW + 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+		if (y0+y1 < cb->y1 - 1) {+		  diag += (coeff[tileComp->cbW + 1].flags+			   >> jpxCoeffSignificantB) & 1;+		}+	      }+	      if (y0+y1 > cb->y0) {+		if (coeff[-(int)tileComp->cbW].flags & jpxCoeffSignificant) {+		  ++vert;+		  vertSign += (coeff[-(int)tileComp->cbW].flags & jpxCoeffSign)+		              ? -1 : 1;+		}+	      }+	      if (y0+y1 < cb->y1 - 1) {+		if (coeff[tileComp->cbW].flags & jpxCoeffSignificant) {+		  ++vert;+		  vertSign += (coeff[tileComp->cbW].flags & jpxCoeffSign)+		              ? -1 : 1;+		}+	      }+	      cx = sigPropContext[horiz][vert][diag][res == 0 ? 1 : sb];+	      if (cb->arithDecoder->decodeBit(cx, cb->stats)) {+		coeff->flags |= jpxCoeffSignificant | jpxCoeffFirstMagRef;+		coeff->mag = (coeff->mag << 1) | 1;+		cx = signContext[horizSign][vertSign][0];+		xorBit = signContext[horizSign][vertSign][1];+		if (cb->arithDecoder->decodeBit(cx, cb->stats) ^ xorBit) {+		  coeff->flags |= jpxCoeffSign;+		}+	      }+	      ++coeff->len;+	    } else {+	      coeff->flags &= ~jpxCoeffTouched;+	    }+	  }+	}+      }+      cb->nextPass = jpxPassSigProp;+      break;+    }+  }++  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;+  JPXCoeff *coeff0, *coeff;+  Guint qStyle, guard, eps, shift;+  int shift2;+  double mu;+  int val;+  int *dataPtr;+  Guint nx0, ny0, nx1, ny1;+  Guint r, cbX, cbY, x, y;++  cover(68);++  //----- (NL)LL subband (resolution level 0)++  resLevel = &tileComp->resLevels[0];+  precinct = &resLevel->precincts[0];+  subband = &precinct->subbands[0];++  // i-quant parameters+  qStyle = tileComp->quantStyle & 0x1f;+  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;+  }++  // copy (NL)LL into the upper-left corner of the data array, doing+  // the fixed point adjustment and dequantization along the way+  cb = subband->cbs;+  for (cbY = 0; cbY < subband->nYCBs; ++cbY) {+    for (cbX = 0; cbX < subband->nXCBs; ++cbX) {+      for (y = cb->y0, coeff0 = cb->coeffs;+	   y < cb->y1;+	   ++y, coeff0 += tileComp->cbW) {+	dataPtr = &tileComp->data[(y - subband->y0)+				  * (tileComp->x1 - tileComp->x0)+				  + (cb->x0 - subband->x0)];+	for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {+	  val = (int)coeff->mag;+	  if (val != 0) {+	    shift2 = shift - (cb->nZeroBitPlanes + coeff->len);+	    if (shift2 > 0) {+	      cover(94);+	      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;+	      }+	    } else {+	      cover(98);+	      val = (int)((double)val * mu);+	    }+	    if (coeff->flags & jpxCoeffSign) {+	      cover(99);+	      val = -val;+	    }+	  }+	  *dataPtr++ = val;+	}+      }+      ++cb;+    }+  }++  //----- IDWT for each level++  for (r = 1; r <= tileComp->nDecompLevels; ++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+    if (r == tileComp->nDecompLevels) {+      cover(72);+      nx0 = tileComp->x0;+      ny0 = tileComp->y0;+      nx1 = tileComp->x1;+      ny1 = tileComp->y1;+    } else {+      cover(73);+      nx0 = tileComp->resLevels[r+1].x0;+      ny0 = tileComp->resLevels[r+1].y0;+      nx1 = tileComp->resLevels[r+1].x1;+      ny1 = tileComp->resLevels[r+1].y1;+    }+    inverseTransformLevel(tileComp, r, resLevel, nx0, ny0, nx1, ny1);+  }+}++// Do one level of the inverse transform:+// - take (n)LL from the tile-component data array+// - take (n)HL/LH/HH from <resLevel>+// - leave the resulting (n-1)LL in the tile-component data array+void JPXStream::inverseTransformLevel(JPXTileComp *tileComp,+				      Guint r, JPXResLevel *resLevel,+				      Guint nx0, Guint ny0,+				      Guint nx1, Guint ny1) {+  JPXPrecinct *precinct;+  JPXSubband *subband;+  JPXCodeBlock *cb;+  JPXCoeff *coeff0, *coeff;+  Guint qStyle, guard, eps, shift, t;+  int shift2;+  double mu;+  int val;+  int *dataPtr;+  Guint xo, yo;+  Guint x, y, sb, cbX, cbY;+  int xx, yy;++  //----- interleave++  // spread out LL+  for (yy = resLevel->y1 - 1; yy >= (int)resLevel->y0; --yy) {+    for (xx = resLevel->x1 - 1; xx >= (int)resLevel->x0; --xx) {+      tileComp->data[(2 * yy - ny0) * (tileComp->x1 - tileComp->x0)+		     + (2 * xx - nx0)] =+	  tileComp->data[(yy - resLevel->y0) * (tileComp->x1 - tileComp->x0)+			 + (xx - resLevel->x0)];+    }+  }++  // i-quant parameters+  qStyle = tileComp->quantStyle & 0x1f;+  guard = (tileComp->quantStyle >> 5) & 7;++  // interleave HL/LH/HH+  precinct = &resLevel->precincts[0];+  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;+    }++    // copy the subband coefficients into the data array, doing the+    // fixed point adjustment and dequantization along the way+    xo = (sb & 1) ? 0 : 1;+    yo = (sb > 0) ? 1 : 0;+    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;+	     y < cb->y1;+	     ++y, coeff0 += tileComp->cbW) {+	  dataPtr = &tileComp->data[(2 * y + yo - ny0)+				    * (tileComp->x1 - tileComp->x0)+				    + (2 * cb->x0 + xo - nx0)];+	  for (x = cb->x0, coeff = coeff0; x < cb->x1; ++x, ++coeff) {+	    val = (int)coeff->mag;+	    if (val != 0) {+	      shift2 = shift - (cb->nZeroBitPlanes + coeff->len);+	      if (shift2 > 0) {+		cover(74);+		val = (val << shift2) + (1 << (shift2 - 1));+	      } else {+		cover(75);+		val >>= -shift2;+	      }+	      if (qStyle == 0) {+		cover(76);+		if (tileComp->transform == 0) {+		  val &= -1 << fracBits;+		}+	      } else {+		cover(77);+		val = (int)((double)val * mu);+	      }+	      if (coeff->flags & jpxCoeffSign) {+		cover(78);+		val = -val;+	      }+	    }+	    *dataPtr = val;+	    dataPtr += 2;+	  }+	}+	++cb;+      }+    }+  }++  //----- horizontal (row) transforms+  dataPtr = tileComp->data;+  for (y = 0; y < ny1 - ny0; ++y) {+    inverseTransform1D(tileComp, dataPtr, 1, nx0, nx1);+    dataPtr += tileComp->x1 - tileComp->x0;+  }++  //----- vertical (column) transforms+  dataPtr = tileComp->data;+  for (x = 0; x < nx1 - nx0; ++x) {+    inverseTransform1D(tileComp, dataPtr,+		       tileComp->x1 - tileComp->x0, ny0, ny1);+    ++dataPtr;+  }+}++void JPXStream::inverseTransform1D(JPXTileComp *tileComp,+				   int *data, Guint stride,+				   Guint i0, Guint i1) {+  int *buf;+  Guint offset, end, i;++  //----- special case for length = 1+  if (i1 - i0 == 1) {+    cover(79);+    if (i0 & 1) {+      cover(104);+      *data >>= 1;+    }++  } else {+    cover(80);++    // choose an offset: this makes even buf[] indexes correspond to+    // odd values of i, and vice versa+    offset = 3 + (i0 & 1);+    end = offset + i1 - i0;++    //----- gather+    buf = tileComp->buf;+    for (i = 0; i < i1 - i0; ++i) {+      buf[offset + i] = data[i * stride];+    }++    //----- extend right+    buf[end] = buf[end - 2];+    if (i1 - i0 == 2) {+      cover(81);+      buf[end+1] = buf[offset + 1];+      buf[end+2] = buf[offset];+      buf[end+3] = buf[offset + 1];+    } else {+      cover(82);+      buf[end+1] = buf[end - 3];+      if (i1 - i0 == 3) {+	cover(105);+	buf[end+2] = buf[offset + 1];+	buf[end+3] = buf[offset + 2];+      } else {+	cover(106);+	buf[end+2] = buf[end - 4];+	if (i1 - i0 == 4) {+	  cover(107);+	  buf[end+3] = buf[offset + 1];+	} else {+	  cover(108);+	  buf[end+3] = buf[end - 5];+	}+      }+    }++    //----- extend left+    buf[offset - 1] = buf[offset + 1];+    buf[offset - 2] = buf[offset + 2];+    buf[offset - 3] = buf[offset + 3];+    if (offset == 4) {+      cover(83);+      buf[0] = buf[offset + 4];+    }++    //----- 9-7 irreversible filter++    if (tileComp->transform == 0) {+      cover(84);+      // step 1 (even)+      for (i = 1; i <= end + 2; i += 2) {+	buf[i] = (int)(idwtKappa * buf[i]);+      }+      // step 2 (odd)+      for (i = 0; i <= end + 3; i += 2) {+	buf[i] = (int)(idwtIKappa * buf[i]);+      }+      // step 3 (even)+      for (i = 1; i <= end + 2; i += 2) {+	buf[i] = (int)(buf[i] - idwtDelta * (buf[i-1] + buf[i+1]));+      }+      // step 4 (odd)+      for (i = 2; i <= end + 1; i += 2) {+	buf[i] = (int)(buf[i] - idwtGamma * (buf[i-1] + buf[i+1]));+      }+      // step 5 (even)+      for (i = 3; i <= end; i += 2) {+	buf[i] = (int)(buf[i] - idwtBeta * (buf[i-1] + buf[i+1]));+      }+      // step 6 (odd)+      for (i = 4; i <= end - 1; i += 2) {+	buf[i] = (int)(buf[i] - idwtAlpha * (buf[i-1] + buf[i+1]));+      }++    //----- 5-3 reversible filter++    } else {+      cover(85);+      // step 1 (even)+      for (i = 3; i <= end; i += 2) {+	buf[i] -= (buf[i-1] + buf[i+1] + 2) >> 2;+      }+      // step 2 (odd)+      for (i = 4; i < end; i += 2) {+	buf[i] += (buf[i-1] + buf[i+1]) >> 1;+      }+    }++    //----- scatter+    for (i = 0; i < i1 - i0; ++i) {+      data[i * stride] = buf[offset + i];+    }+  }+}++// 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].y1 - tile->tileComps[0].y0; ++y) {+	for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++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].y1 - tile->tileComps[0].y0; ++y) {+	for (x = 0; x < tile->tileComps[0].x1 - tile->tileComps[0].x0; ++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->y1 - tileComp->y0; ++y) {+	for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {+	  coeff = *dataPtr;+	  if (tileComp->transform == 0) {+	    cover(109);+	    coeff >>= fracBits;+	  }+	  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->y1 - tileComp->y0; ++y) {+	for (x = 0; x < tileComp->x1 - tileComp->x0; ++x) {+	  coeff = *dataPtr;+	  if (tileComp->transform == 0) {+	    cover(112);+	    coeff >>= fracBits;+	  }+	  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(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 = str->getChar()) == EOF) {+	return gFalse;+      }+    } while (c != 0xff);+    do {+      if ((c = str->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 = str->getChar()) == EOF) {+    return gFalse;+  }+  *x = (Guint)c0;+  return gTrue;+}++GBool JPXStream::readByte(int *x) {+ int c0;++  if ((c0 = str->getChar()) == EOF) {+    return gFalse;+  }+  *x = c0;+  if (c0 & 0x80) {+    *x |= -1 - 0xff;+  }+  return gTrue;+}++GBool JPXStream::readUWord(Guint *x) {+  int c0, c1;++  if ((c0 = str->getChar()) == EOF ||+      (c1 = str->getChar()) == EOF) {+    return gFalse;+  }+  *x = (Guint)((c0 << 8) | c1);+  return gTrue;+}++GBool JPXStream::readULong(Guint *x) {+  int c0, c1, c2, c3;++  if ((c0 = str->getChar()) == EOF ||+      (c1 = str->getChar()) == EOF ||+      (c2 = str->getChar()) == EOF ||+      (c3 = str->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 = str->getChar()) == EOF) {+      return gFalse;+    }+    y = (y << 8) + c;+  }+  if (signd) {+    if (y & (1 << (8 * nBytes - 1))) {+      y |= -1 << (8 * nBytes);+    }+  }+  *x = y;+  return gTrue;+}++GBool JPXStream::readBits(int nBits, Guint *x) {+  int c;++  while (bitBufLen < nBits) {+    if (byteCount == 0 || (c = str->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::startBitBuf(Guint byteCountA) {+  bitBufLen = 0;+  bitBufSkip = gFalse;+  byteCount = byteCountA;+}++Guint JPXStream::finishBitBuf() {+  if (bitBufSkip) {+    str->getChar();+    --byteCount;+  }+  return byteCount;+}
+ xpdf/JPXStream.h view
@@ -0,0 +1,351 @@+//========================================================================+//+// 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 JPXCoeff {+  Gushort flags;		// flag bits+  Gushort len;			// number of significant bits in mag+  Guint mag;			// magnitude value+};++// coefficient flags+#define jpxCoeffSignificantB  0+#define jpxCoeffTouchedB      1+#define jpxCoeffFirstMagRefB  2+#define jpxCoeffSignB         7+#define jpxCoeffSignificant   (1 << jpxCoeffSignificantB)+#define jpxCoeffTouched       (1 << jpxCoeffTouchedB)+#define jpxCoeffFirstMagRef   (1 << jpxCoeffFirstMagRefB)+#define jpxCoeffSign          (1 << jpxCoeffSignB)++//------------------------------------------------------------------------++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;		// pkt data length++  //----- coefficient data+  JPXCoeff *coeffs;		// the coefficients+  JArithmeticDecoder		// arithmetic decoder+    *arithDecoder;+  JArithmeticDecoderStats	// arithmetic decoder stats+    *stats;+};++//------------------------------------------------------------------------++struct JPXSubband {+  //----- computed+  Guint x0, y0, x1, y1;		// bounds+  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 {+  //----- computed+  Guint x0, y0, x1, y1;		// bounds of the precinct++  //----- 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)++  //----- computed+  Guint x0, y0, x1, y1;		// bounds of the tile-comp (for this res level)+  Guint bx0[3], by0[3],		// subband bounds+        bx1[3], by1[3];++  //---- 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 cbW;			// code-block width+  Guint cbH;			// code-block height++  //----- 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 {+  //----- 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++  //----- 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 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)+};++//------------------------------------------------------------------------++class JPXStream: public FilterStream {+public:++  JPXStream(Stream *strA);+  virtual ~JPXStream();+  virtual StreamKind getKind() { return strJPX; }+  virtual void reset();+  virtual void close();+  virtual int getChar();+  virtual int lookChar();+  virtual GString *getPSFilter(int psLevel, char *indent);+  virtual GBool isBinary(GBool last = gTrue);+  virtual void getImageParams(int *bitsPerComponent,+			      StreamColorSpaceMode *csMode);++private:++  void fillReadBuf();+  void getImageParams2(int *bitsPerComponent, StreamColorSpaceMode *csMode);+  GBool readBoxes();+  GBool readColorSpecBox(Guint dataLen);+  GBool 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,+			     Guint nx0, Guint ny0,+			     Guint nx1, Guint ny1);+  void inverseTransform1D(JPXTileComp *tileComp,+			  int *data, Guint stride,+			  Guint i0, Guint i1);+  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);+  GBool readBits(int nBits, Guint *x);+  void startBitBuf(Guint byteCountA);+  Guint finishBitBuf();++  Guint nComps;			// number of components+  Guint *bpc;			// bits per component, for each component+  Guint width, height;		// image size+  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
+ xpdf/Lexer.cc view
@@ -0,0 +1,485 @@+//========================================================================+//+// 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 "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, done;+  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 '.':+    neg = gFalse;+    xi = 0;+    if (c == '-') {+      neg = gTrue;+    } else if (c == '.') {+      goto doReal;+    } else {+      xi = c - '0';+    }+    while (1) {+      c = lookChar();+      if (isdigit(c)) {+	getChar();+	xi = xi * 10 + (c - '0');+      } else if (c == '.') {+	getChar();+	goto doReal;+      } else {+	break;+      }+    }+    if (neg)+      xi = -xi;+    obj->initInt(xi);+    break;+  doReal:+    xf = xi;+    scale = 0.1;+    while (1) {+      c = lookChar();+      if (c == '-') {+	// ignore minus signs in the middle of numbers to match+	// Adobe's behavior+	error(getPos(), "Badly formatted number");+	getChar();+	continue;+      }+      if (!isdigit(c)) {+	break;+      }+      getChar();+      xf = xf + scale * (c - '0');+      scale *= 0.1;+    }+    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:+#if 0+      // This breaks some PDF files, e.g., ones from Photoshop.+      case '\r':+      case '\n':+#endif+	error(getPos(), "Unterminated string");+	done = gTrue;+	break;++      case '(':+	++numParen;+	c2 = c;+	break;++      case ')':+	if (--numParen == 0) {+	  done = gTrue;+	} else {+	  c2 = c;+	}+	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(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;+    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 {+	  goto notEscChar;+	}+	getChar();+	c <<= 4;+	c2 = getChar();+	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(getPos(), "Illegal digit in hex char in name");+	}+      }+     notEscChar:+      if (++n == tokBufSize) {+	error(getPos(), "Name token too long");+	break;+      }+      *p++ = c;+    }+    *p = '\0';+    obj->initName(tokBuf);+    break;++  // array punctuation+  case '[':+  case ']':+    tokBuf[0] = 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(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(getPos(), "Illegal character <%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(getPos(), "Illegal character '>'");+      obj->initError();+    }+    break;++  // error+  case ')':+  case '{':+  case '}':+    error(getPos(), "Illegal character '%c'", c);+    obj->initError();+    break;++  // command+  default:+    p = tokBuf;+    *p++ = c;+    n = 1;+    while ((c = lookChar()) != EOF && !specialChars[c]) {+      getChar();+      if (++n == tokBufSize) {+	error(getPos(), "Command token too long");+	break;+      }+      *p++ = 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;+    }+  }+}++GBool Lexer::isSpace(int c) {+  return c >= 0 && c <= 0xff && specialChars[c] == 1;+}
+ xpdf/Lexer.h view
@@ -0,0 +1,80 @@+//========================================================================+//+// 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 over one character.+  void skipChar() { getChar(); }++  // Get stream.+  Stream *getStream()+    { return curStr.isNone() ? (Stream *)NULL : curStr.getStream(); }++  // Get current position in file.  This is only used for error+  // messages, so it returns an int instead of a Guint.+  int getPos()+    { return curStr.isNone() ? -1 : (int)curStr.streamGetPos(); }++  // Set position in file.+  void setPos(Guint 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
+ xpdf/Link.cc view
@@ -0,0 +1,784 @@+//========================================================================+//+// 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 "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;++  if (!obj->isDict()) {+    error(-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();++  // unknown action+  } else if (obj2.isName()) {+    action = new LinkUnknown(obj2.getName());++  // action is missing or wrong type+  } else {+    error(-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(-1, "Illegal file spec in link");+    }+    obj1.free();++  // error+  } else {+    error(-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(-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(-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(-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(-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(-1, "Bad annotation destination position");+	goto err1;+      }+      obj2.free();+    }++  // Fit link+  } else if (obj1.isName("Fit")) {+    if (a->getLength() < 2) {+      error(-1, "Annotation destination array is too short");+      goto err2;+    }+    kind = destFit;++  // FitH link+  } else if (obj1.isName("FitH")) {+    if (a->getLength() < 3) {+      error(-1, "Annotation destination array is too short");+      goto err2;+    }+    kind = destFitH;+    if (!a->get(2, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    top = obj2.getNum();+    obj2.free();++  // FitV link+  } else if (obj1.isName("FitV")) {+    if (a->getLength() < 3) {+      error(-1, "Annotation destination array is too short");+      goto err2;+    }+    kind = destFitV;+    if (!a->get(2, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    left = obj2.getNum();+    obj2.free();++  // FitR link+  } else if (obj1.isName("FitR")) {+    if (a->getLength() < 6) {+      error(-1, "Annotation destination array is too short");+      goto err2;+    }+    kind = destFitR;+    if (!a->get(2, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    left = obj2.getNum();+    obj2.free();+    if (!a->get(3, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    bottom = obj2.getNum();+    obj2.free();+    if (!a->get(4, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    right = obj2.getNum();+    obj2.free();+    if (!a->get(5, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    top = obj2.getNum();+    obj2.free();++  // FitB link+  } else if (obj1.isName("FitB")) {+    if (a->getLength() < 2) {+      error(-1, "Annotation destination array is too short");+      goto err2;+    }+    kind = destFitB;++  // FitBH link+  } else if (obj1.isName("FitBH")) {+    if (a->getLength() < 3) {+      error(-1, "Annotation destination array is too short");+      goto err2;+    }+    kind = destFitBH;+    if (!a->get(2, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    top = obj2.getNum();+    obj2.free();++  // FitBV link+  } else if (obj1.isName("FitBV")) {+    if (a->getLength() < 3) {+      error(-1, "Annotation destination array is too short");+      goto err2;+    }+    kind = destFitBV;+    if (!a->get(2, &obj2)->isNum()) {+      error(-1, "Bad annotation destination position");+      kind = destFit;+    }+    left = obj2.getNum();+    obj2.free();++  // unknown link kind+  } else {+    error(-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(-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(-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(-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(-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()->copy();+    if (baseURI && baseURI->getLength() > 0) {+      n = strcspn(uri2->getCString(), "/:");+      if (n == uri2->getLength() || uri2->getChar(n) == '/') {+	uri = baseURI->copy();+	c = uri->getChar(uri->getLength() - 1);+	if (c == '/' || c == '?') {+	  if (uri2->getChar(0) == '/') {+	    uri2->del(0);+	  }+	} else {+	  if (uri2->getChar(0) != '/') {+	    uri->append('/');+	  }+	}+	uri->append(uri2);+	delete uri2;+      } else {+	uri = uri2;+      }+    } else {+      uri = uri2;+    }+  } else {+    error(-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(-1, "Movie action is missing both the Annot and T keys");+  }+}++LinkMovie::~LinkMovie() {+  if (title) {+    delete title;+  }+}++//------------------------------------------------------------------------+// 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(-1, "Annotation rectangle is wrong type");+    goto err2;+  }+  if (!obj1.arrayGet(0, &obj2)->isNum()) {+    error(-1, "Bad annotation rectangle");+    goto err1;+  }+  x1 = obj2.getNum();+  obj2.free();+  if (!obj1.arrayGet(1, &obj2)->isNum()) {+    error(-1, "Bad annotation rectangle");+    goto err1;+  }+  y1 = obj2.getNum();+  obj2.free();+  if (!obj1.arrayGet(2, &obj2)->isNum()) {+    error(-1, "Bad annotation rectangle");+    goto err1;+  }+  x2 = obj2.getNum();+  obj2.free();+  if (!obj1.arrayGet(3, &obj2)->isNum()) {+    error(-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;+  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()) {+	if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {+	  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;+	  }+	}+	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;+}
+ xpdf/Link.h view
@@ -0,0 +1,369 @@+//========================================================================+//+// 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+  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;	// for destXYZ links, which position+  GBool changeZoom;		//   components to change+  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();++  virtual GBool isOk() { return name != NULL; }++  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();++  virtual GBool isOk() { return annotRef.num >= 0 || title != NULL; }++  virtual LinkActionKind getKind() { return actionMovie; }+  GBool hasAnnotRef() { return annotRef.num >= 0; }+  Ref *getAnnotRef() { return &annotRef; }+  GString *getTitle() { return title; }++private:++  Ref annotRef;+  GString *title;+};++//------------------------------------------------------------------------+// 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
+ xpdf/NameToCharCode.cc view
@@ -0,0 +1,116 @@+//========================================================================+//+// 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 "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(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(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(char *name) {+  char *p;+  unsigned int h;++  h = 0;+  for (p = name; *p; ++p) {+    h = 17 * h + (int)(*p & 0xff);+  }+  return (int)(h % size);+}
+ xpdf/NameToCharCode.h view
@@ -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(char *name, CharCode c);+  CharCode lookup(char *name);++private:++  int hash(char *name);++  NameToCharCodeEntry *tab;+  int size;+  int len;+};++#endif
+ xpdf/NameToUnicodeTable.h view
@@ -0,0 +1,1097 @@+//========================================================================+//+// NameToUnicodeTable.h+//+// Copyright 2001-2004 Glyph & Cog, LLC+//+//========================================================================++static struct {+  Unicode u;+  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"},+  {0xf7e6, "AEsmall"},+  {0x00c1, "Aacute"},+  {0xf7e1, "Aacutesmall"},+  {0x0102, "Abreve"},+  {0x00c2, "Acircumflex"},+  {0xf7e2, "Acircumflexsmall"},+  {0xf6c9, "Acute"},+  {0xf7b4, "Acutesmall"},+  {0x00c4, "Adieresis"},+  {0xf7e4, "Adieresissmall"},+  {0x00c0, "Agrave"},+  {0xf7e0, "Agravesmall"},+  {0x0391, "Alpha"},+  {0x0386, "Alphatonos"},+  {0x0100, "Amacron"},+  {0x0104, "Aogonek"},+  {0x00c5, "Aring"},+  {0x01fa, "Aringacute"},+  {0xf7e5, "Aringsmall"},+  {0xf761, "Asmall"},+  {0x00c3, "Atilde"},+  {0xf7e3, "Atildesmall"},+  {0x0042, "B"},+  {0x0392, "Beta"},+  {0xf6f4, "Brevesmall"},+  {0xf762, "Bsmall"},+  {0x0043, "C"},+  {0x0106, "Cacute"},+  {0xf6ca, "Caron"},+  {0xf6f5, "Caronsmall"},+  {0x010c, "Ccaron"},+  {0x00c7, "Ccedilla"},+  {0xf7e7, "Ccedillasmall"},+  {0x0108, "Ccircumflex"},+  {0x010a, "Cdotaccent"},+  {0xf7b8, "Cedillasmall"},+  {0x03a7, "Chi"},+  {0xf6f6, "Circumflexsmall"},+  {0xf763, "Csmall"},+  {0x0044, "D"},+  {0x010e, "Dcaron"},+  {0x0110, "Dcroat"},+  {0x2206, "Delta"},+  {0xf6cb, "Dieresis"},+  {0xf6cc, "DieresisAcute"},+  {0xf6cd, "DieresisGrave"},+  {0xf7a8, "Dieresissmall"},+  {0xf6f7, "Dotaccentsmall"},+  {0xf764, "Dsmall"},+  {0x0045, "E"},+  {0x00c9, "Eacute"},+  {0xf7e9, "Eacutesmall"},+  {0x0114, "Ebreve"},+  {0x011a, "Ecaron"},+  {0x00ca, "Ecircumflex"},+  {0xf7ea, "Ecircumflexsmall"},+  {0x00cb, "Edieresis"},+  {0xf7eb, "Edieresissmall"},+  {0x0116, "Edotaccent"},+  {0x00c8, "Egrave"},+  {0xf7e8, "Egravesmall"},+  {0x0112, "Emacron"},+  {0x014a, "Eng"},+  {0x0118, "Eogonek"},+  {0x0395, "Epsilon"},+  {0x0388, "Epsilontonos"},+  {0xf765, "Esmall"},+  {0x0397, "Eta"},+  {0x0389, "Etatonos"},+  {0x00d0, "Eth"},+  {0xf7f0, "Ethsmall"},+  {0x20ac, "Euro"},+  {0x0046, "F"},+  {0xf766, "Fsmall"},+  {0x0047, "G"},+  {0x0393, "Gamma"},+  {0x011e, "Gbreve"},+  {0x01e6, "Gcaron"},+  {0x011c, "Gcircumflex"},+  {0x0122, "Gcommaaccent"},+  {0x0120, "Gdotaccent"},+  {0xf6ce, "Grave"},+  {0xf760, "Gravesmall"},+  {0xf767, "Gsmall"},+  {0x0048, "H"},+  {0x25cf, "H18533"},+  {0x25aa, "H18543"},+  {0x25ab, "H18551"},+  {0x25a1, "H22073"},+  {0x0126, "Hbar"},+  {0x0124, "Hcircumflex"},+  {0xf768, "Hsmall"},+  {0xf6cf, "Hungarumlaut"},+  {0xf6f8, "Hungarumlautsmall"},+  {0x0049, "I"},+  {0x0132, "IJ"},+  {0x00cd, "Iacute"},+  {0xf7ed, "Iacutesmall"},+  {0x012c, "Ibreve"},+  {0x00ce, "Icircumflex"},+  {0xf7ee, "Icircumflexsmall"},+  {0x00cf, "Idieresis"},+  {0xf7ef, "Idieresissmall"},+  {0x0130, "Idotaccent"},+  {0x2111, "Ifraktur"},+  {0x00cc, "Igrave"},+  {0xf7ec, "Igravesmall"},+  {0x012a, "Imacron"},+  {0x012e, "Iogonek"},+  {0x0399, "Iota"},+  {0x03aa, "Iotadieresis"},+  {0x038a, "Iotatonos"},+  {0xf769, "Ismall"},+  {0x0128, "Itilde"},+  {0x004a, "J"},+  {0x0134, "Jcircumflex"},+  {0xf76a, "Jsmall"},+  {0x004b, "K"},+  {0x039a, "Kappa"},+  {0x0136, "Kcommaaccent"},+  {0xf76b, "Ksmall"},+  {0x004c, "L"},+  {0xf6bf, "LL"},+  {0x0139, "Lacute"},+  {0x039b, "Lambda"},+  {0x013d, "Lcaron"},+  {0x013b, "Lcommaaccent"},+  {0x013f, "Ldot"},+  {0x0141, "Lslash"},+  {0xf6f9, "Lslashsmall"},+  {0xf76c, "Lsmall"},+  {0x004d, "M"},+  {0xf6d0, "Macron"},+  {0xf7af, "Macronsmall"},+  {0xf76d, "Msmall"},+  {0x039c, "Mu"},+  {0x004e, "N"},+  {0x0143, "Nacute"},+  {0x0147, "Ncaron"},+  {0x0145, "Ncommaaccent"},+  {0xf76e, "Nsmall"},+  {0x00d1, "Ntilde"},+  {0xf7f1, "Ntildesmall"},+  {0x039d, "Nu"},+  {0x004f, "O"},+  {0x0152, "OE"},+  {0xf6fa, "OEsmall"},+  {0x00d3, "Oacute"},+  {0xf7f3, "Oacutesmall"},+  {0x014e, "Obreve"},+  {0x00d4, "Ocircumflex"},+  {0xf7f4, "Ocircumflexsmall"},+  {0x00d6, "Odieresis"},+  {0xf7f6, "Odieresissmall"},+  {0xf6fb, "Ogoneksmall"},+  {0x00d2, "Ograve"},+  {0xf7f2, "Ogravesmall"},+  {0x01a0, "Ohorn"},+  {0x0150, "Ohungarumlaut"},+  {0x014c, "Omacron"},+  {0x2126, "Omega"},+  {0x038f, "Omegatonos"},+  {0x039f, "Omicron"},+  {0x038c, "Omicrontonos"},+  {0x00d8, "Oslash"},+  {0x01fe, "Oslashacute"},+  {0xf7f8, "Oslashsmall"},+  {0xf76f, "Osmall"},+  {0x00d5, "Otilde"},+  {0xf7f5, "Otildesmall"},+  {0x0050, "P"},+  {0x03a6, "Phi"},+  {0x03a0, "Pi"},+  {0x03a8, "Psi"},+  {0xf770, "Psmall"},+  {0x0051, "Q"},+  {0xf771, "Qsmall"},+  {0x0052, "R"},+  {0x0154, "Racute"},+  {0x0158, "Rcaron"},+  {0x0156, "Rcommaaccent"},+  {0x211c, "Rfraktur"},+  {0x03a1, "Rho"},+  {0xf6fc, "Ringsmall"},+  {0xf772, "Rsmall"},+  {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"},+  {0x0160, "Scaron"},+  {0xf6fd, "Scaronsmall"},+  {0x015e, "Scedilla"},+  {0x015c, "Scircumflex"},+  {0x0218, "Scommaaccent"},+  {0x03a3, "Sigma"},+  {0xf773, "Ssmall"},+  {0x0054, "T"},+  {0x03a4, "Tau"},+  {0x0166, "Tbar"},+  {0x0164, "Tcaron"},+  {0x0162, "Tcommaaccent"},+  {0x0398, "Theta"},+  {0x00de, "Thorn"},+  {0xf7fe, "Thornsmall"},+  {0xf6fe, "Tildesmall"},+  {0xf774, "Tsmall"},+  {0x0055, "U"},+  {0x00da, "Uacute"},+  {0xf7fa, "Uacutesmall"},+  {0x016c, "Ubreve"},+  {0x00db, "Ucircumflex"},+  {0xf7fb, "Ucircumflexsmall"},+  {0x00dc, "Udieresis"},+  {0xf7fc, "Udieresissmall"},+  {0x00d9, "Ugrave"},+  {0xf7f9, "Ugravesmall"},+  {0x01af, "Uhorn"},+  {0x0170, "Uhungarumlaut"},+  {0x016a, "Umacron"},+  {0x0172, "Uogonek"},+  {0x03a5, "Upsilon"},+  {0x03d2, "Upsilon1"},+  {0x03ab, "Upsilondieresis"},+  {0x038e, "Upsilontonos"},+  {0x016e, "Uring"},+  {0xf775, "Usmall"},+  {0x0168, "Utilde"},+  {0x0056, "V"},+  {0xf776, "Vsmall"},+  {0x0057, "W"},+  {0x1e82, "Wacute"},+  {0x0174, "Wcircumflex"},+  {0x1e84, "Wdieresis"},+  {0x1e80, "Wgrave"},+  {0xf777, "Wsmall"},+  {0x0058, "X"},+  {0x039e, "Xi"},+  {0xf778, "Xsmall"},+  {0x0059, "Y"},+  {0x00dd, "Yacute"},+  {0xf7fd, "Yacutesmall"},+  {0x0176, "Ycircumflex"},+  {0x0178, "Ydieresis"},+  {0xf7ff, "Ydieresissmall"},+  {0x1ef2, "Ygrave"},+  {0xf779, "Ysmall"},+  {0x005a, "Z"},+  {0x0179, "Zacute"},+  {0x017d, "Zcaron"},+  {0xf6ff, "Zcaronsmall"},+  {0x017b, "Zdotaccent"},+  {0x0396, "Zeta"},+  {0xf77a, "Zsmall"},+  {0x0022, "\""},+  {0x005c, "\\"},+  {0x005d, "]"},+  {0x005e, "^"},+  {0x005f, "_"},+  {0x0060, "`"},+  {0x0061, "a"},+  {0x00e1, "aacute"},+  {0x0103, "abreve"},+  {0x00e2, "acircumflex"},+  {0x00b4, "acute"},+  {0x0301, "acutecomb"},+  {0x00e4, "adieresis"},+  {0x00e6, "ae"},+  {0x01fd, "aeacute"},+  {0x2015, "afii00208"},+  {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"},+  {0x2135, "aleph"},+  {0x03b1, "alpha"},+  {0x03ac, "alphatonos"},+  {0x0101, "amacron"},+  {0x0026, "ampersand"},+  {0xf726, "ampersandsmall"},+  {0x2220, "angle"},+  {0x2329, "angleleft"},+  {0x232a, "angleright"},+  {0x0387, "anoteleia"},+  {0x0105, "aogonek"},+  {0x2248, "approxequal"},+  {0x00e5, "aring"},+  {0x01fb, "aringacute"},+  {0x2194, "arrowboth"},+  {0x21d4, "arrowdblboth"},+  {0x21d3, "arrowdbldown"},+  {0x21d0, "arrowdblleft"},+  {0x21d2, "arrowdblright"},+  {0x21d1, "arrowdblup"},+  {0x2193, "arrowdown"},+  {0xf8e7, "arrowhorizex"},+  {0x2190, "arrowleft"},+  {0x2192, "arrowright"},+  {0x2191, "arrowup"},+  {0x2195, "arrowupdn"},+  {0x21a8, "arrowupdnbse"},+  {0xf8e6, "arrowvertex"},+  {0x005e, "asciicircum"},+  {0x007e, "asciitilde"},+  {0x002a, "asterisk"},+  {0x2217, "asteriskmath"},+  {0xf6e9, "asuperior"},+  {0x0040, "at"},+  {0x00e3, "atilde"},+  {0x0062, "b"},+  {0x005c, "backslash"},+  {0x007c, "bar"},+  {0x03b2, "beta"},+  {0x2588, "block"},+  {0xf8f4, "braceex"},+  {0x007b, "braceleft"},+  {0xf8f3, "braceleftbt"},+  {0xf8f2, "braceleftmid"},+  {0xf8f1, "bracelefttp"},+  {0x007d, "braceright"},+  {0xf8fe, "bracerightbt"},+  {0xf8fd, "bracerightmid"},+  {0xf8fc, "bracerighttp"},+  {0x005b, "bracketleft"},+  {0xf8f0, "bracketleftbt"},+  {0xf8ef, "bracketleftex"},+  {0xf8ee, "bracketlefttp"},+  {0x005d, "bracketright"},+  {0xf8fb, "bracketrightbt"},+  {0xf8fa, "bracketrightex"},+  {0xf8f9, "bracketrighttp"},+  {0x02d8, "breve"},+  {0x00a6, "brokenbar"},+  {0xf6ea, "bsuperior"},+  {0x2022, "bullet"},+  {0x0063, "c"},+  {0x0107, "cacute"},+  {0x02c7, "caron"},+  {0x21b5, "carriagereturn"},+  {0x010d, "ccaron"},+  {0x00e7, "ccedilla"},+  {0x0109, "ccircumflex"},+  {0x010b, "cdotaccent"},+  {0x00b8, "cedilla"},+  {0x00a2, "cent"},+  {0xf6df, "centinferior"},+  {0xf7a2, "centoldstyle"},+  {0xf6e0, "centsuperior"},+  {0x03c7, "chi"},+  {0x25cb, "circle"},+  {0x2297, "circlemultiply"},+  {0x2295, "circleplus"},+  {0x02c6, "circumflex"},+  {0x2663, "club"},+  {0x003a, "colon"},+  {0x20a1, "colonmonetary"},+  {0x002c, "comma"},+  {0xf6c3, "commaaccent"},+  {0xf6e1, "commainferior"},+  {0xf6e2, "commasuperior"},+  {0x2245, "congruent"},+  {0x00a9, "copyright"},+  {0x00a9, "copyrightsans"},+  {0x00a9, "copyrightserif"},+  {0x00a4, "currency"},+  {0xf6d1, "cyrBreve"},+  {0xf6d2, "cyrFlex"},+  {0xf6d4, "cyrbreve"},+  {0xf6d5, "cyrflex"},+  {0x0064, "d"},+  {0x2020, "dagger"},+  {0x2021, "daggerdbl"},+  {0xf6d3, "dblGrave"},+  {0xf6d6, "dblgrave"},+  {0x010f, "dcaron"},+  {0x0111, "dcroat"},+  {0x00b0, "degree"},+  {0x03b4, "delta"},+  {0x2666, "diamond"},+  {0x00a8, "dieresis"},+  {0xf6d7, "dieresisacute"},+  {0xf6d8, "dieresisgrave"},+  {0x0385, "dieresistonos"},+  {0x00f7, "divide"},+  {0x2593, "dkshade"},+  {0x2584, "dnblock"},+  {0x0024, "dollar"},+  {0xf6e3, "dollarinferior"},+  {0xf724, "dollaroldstyle"},+  {0xf6e4, "dollarsuperior"},+  {0x20ab, "dong"},+  {0x02d9, "dotaccent"},+  {0x0323, "dotbelowcomb"},+  {0x0131, "dotlessi"},+  {0xf6be, "dotlessj"},+  {0x22c5, "dotmath"},+  {0xf6eb, "dsuperior"},+  {0x0065, "e"},+  {0x00e9, "eacute"},+  {0x0115, "ebreve"},+  {0x011b, "ecaron"},+  {0x00ea, "ecircumflex"},+  {0x00eb, "edieresis"},+  {0x0117, "edotaccent"},+  {0x00e8, "egrave"},+  {0x0038, "eight"},+  {0x2088, "eightinferior"},+  {0xf738, "eightoldstyle"},+  {0x2078, "eightsuperior"},+  {0x2208, "element"},+  {0x2026, "ellipsis"},+  {0x0113, "emacron"},+  {0x2014, "emdash"},+  {0x2205, "emptyset"},+  {0x2013, "endash"},+  {0x014b, "eng"},+  {0x0119, "eogonek"},+  {0x03b5, "epsilon"},+  {0x03ad, "epsilontonos"},+  {0x003d, "equal"},+  {0x2261, "equivalence"},+  {0x212e, "estimated"},+  {0xf6ec, "esuperior"},+  {0x03b7, "eta"},+  {0x03ae, "etatonos"},+  {0x00f0, "eth"},+  {0x0021, "exclam"},+  {0x203c, "exclamdbl"},+  {0x00a1, "exclamdown"},+  {0xf7a1, "exclamdownsmall"},+  {0x0021, "exclamleft"},+  {0xf721, "exclamsmall"},+  {0x2203, "existential"},+  {0x0066, "f"},+  {0x2640, "female"},+  {0xfb00, "ff"},+  {0xfb03, "ffi"},+  {0xfb04, "ffl"},+  {0xfb01, "fi"},+  {0x2012, "figuredash"},+  {0x25a0, "filledbox"},+  {0x25ac, "filledrect"},+  {0x0035, "five"},+  {0x215d, "fiveeighths"},+  {0x2085, "fiveinferior"},+  {0xf735, "fiveoldstyle"},+  {0x2075, "fivesuperior"},+  {0xfb02, "fl"},+  {0x0192, "florin"},+  {0x0034, "four"},+  {0x2084, "fourinferior"},+  {0xf734, "fouroldstyle"},+  {0x2074, "foursuperior"},+  {0x2044, "fraction"},+  {0x20a3, "franc"},+  {0x0067, "g"},+  {0x03b3, "gamma"},+  {0x011f, "gbreve"},+  {0x01e7, "gcaron"},+  {0x011d, "gcircumflex"},+  {0x0123, "gcommaaccent"},+  {0x0121, "gdotaccent"},+  {0x00df, "germandbls"},+  {0x2207, "gradient"},+  {0x0060, "grave"},+  {0x0300, "gravecomb"},+  {0x003e, "greater"},+  {0x2265, "greaterequal"},+  {0x00ab, "guillemotleft"},+  {0x00bb, "guillemotright"},+  {0x2039, "guilsinglleft"},+  {0x203a, "guilsinglright"},+  {0x0068, "h"},+  {0x0127, "hbar"},+  {0x0125, "hcircumflex"},+  {0x2665, "heart"},+  {0x0309, "hookabovecomb"},+  {0x2302, "house"},+  {0x02dd, "hungarumlaut"},+  {0x002d, "hyphen"},+  {0xf6e5, "hypheninferior"},+  {0xf6e6, "hyphensuperior"},+  {0x0069, "i"},+  {0x00ed, "iacute"},+  {0x012d, "ibreve"},+  {0x00ee, "icircumflex"},+  {0x00ef, "idieresis"},+  {0x00ec, "igrave"},+  {0x0133, "ij"},+  {0x012b, "imacron"},+  {0x221e, "infinity"},+  {0x222b, "integral"},+  {0x2321, "integralbt"},+  {0xf8f5, "integralex"},+  {0x2320, "integraltp"},+  {0x2229, "intersection"},+  {0x25d8, "invbullet"},+  {0x25d9, "invcircle"},+  {0x263b, "invsmileface"},+  {0x012f, "iogonek"},+  {0x03b9, "iota"},+  {0x03ca, "iotadieresis"},+  {0x0390, "iotadieresistonos"},+  {0x03af, "iotatonos"},+  {0xf6ed, "isuperior"},+  {0x0129, "itilde"},+  {0x006a, "j"},+  {0x0135, "jcircumflex"},+  {0x006b, "k"},+  {0x03ba, "kappa"},+  {0x0137, "kcommaaccent"},+  {0x0138, "kgreenlandic"},+  {0x006c, "l"},+  {0x013a, "lacute"},+  {0x03bb, "lambda"},+  {0x013e, "lcaron"},+  {0x013c, "lcommaaccent"},+  {0x0140, "ldot"},+  {0x003c, "less"},+  {0x2264, "lessequal"},+  {0x258c, "lfblock"},+  {0x20a4, "lira"},+  {0xf6c0, "ll"},+  {0x2227, "logicaland"},+  {0x00ac, "logicalnot"},+  {0x2228, "logicalor"},+  {0x017f, "longs"},+  {0x25ca, "lozenge"},+  {0x0142, "lslash"},+  {0xf6ee, "lsuperior"},+  {0x2591, "ltshade"},+  {0x006d, "m"},+  {0x00af, "macron"},+  {0x2642, "male"},+  {0x2212, "minus"},+  {0x2032, "minute"},+  {0xf6ef, "msuperior"},+  {0x00b5, "mu"},+  {0x00d7, "multiply"},+  {0x266a, "musicalnote"},+  {0x266b, "musicalnotedbl"},+  {0x006e, "n"},+  {0x0144, "nacute"},+  {0x0149, "napostrophe"},+  {0x00a0, "nbspace"},+  {0x0148, "ncaron"},+  {0x0146, "ncommaaccent"},+  {0x0039, "nine"},+  {0x2089, "nineinferior"},+  {0xf739, "nineoldstyle"},+  {0x2079, "ninesuperior"},+  {0x00a0, "nonbreakingspace"},+  {0x2209, "notelement"},+  {0x2260, "notequal"},+  {0x2284, "notsubset"},+  {0x207f, "nsuperior"},+  {0x00f1, "ntilde"},+  {0x03bd, "nu"},+  {0x0023, "numbersign"},+  {0x006f, "o"},+  {0x00f3, "oacute"},+  {0x014f, "obreve"},+  {0x00f4, "ocircumflex"},+  {0x00f6, "odieresis"},+  {0x0153, "oe"},+  {0x02db, "ogonek"},+  {0x00f2, "ograve"},+  {0x01a1, "ohorn"},+  {0x0151, "ohungarumlaut"},+  {0x014d, "omacron"},+  {0x03c9, "omega"},+  {0x03d6, "omega1"},+  {0x03ce, "omegatonos"},+  {0x03bf, "omicron"},+  {0x03cc, "omicrontonos"},+  {0x0031, "one"},+  {0x2024, "onedotenleader"},+  {0x215b, "oneeighth"},+  {0xf6dc, "onefitted"},+  {0x00bd, "onehalf"},+  {0x2081, "oneinferior"},+  {0xf731, "oneoldstyle"},+  {0x00bc, "onequarter"},+  {0x00b9, "onesuperior"},+  {0x2153, "onethird"},+  {0x25e6, "openbullet"},+  {0x00aa, "ordfeminine"},+  {0x00ba, "ordmasculine"},+  {0x221f, "orthogonal"},+  {0x00f8, "oslash"},+  {0x01ff, "oslashacute"},+  {0xf6f0, "osuperior"},+  {0x00f5, "otilde"},+  {0x0070, "p"},+  {0x00b6, "paragraph"},+  {0x0028, "parenleft"},+  {0xf8ed, "parenleftbt"},+  {0xf8ec, "parenleftex"},+  {0x208d, "parenleftinferior"},+  {0x207d, "parenleftsuperior"},+  {0xf8eb, "parenlefttp"},+  {0x0029, "parenright"},+  {0xf8f8, "parenrightbt"},+  {0xf8f7, "parenrightex"},+  {0x208e, "parenrightinferior"},+  {0x207e, "parenrightsuperior"},+  {0xf8f6, "parenrighttp"},+  {0x2202, "partialdiff"},+  {0x0025, "percent"},+  {0x002e, "period"},+  {0x00b7, "periodcentered"},+  {0xf6e7, "periodinferior"},+  {0xf6e8, "periodsuperior"},+  {0x22a5, "perpendicular"},+  {0x2030, "perthousand"},+  {0x20a7, "peseta"},+  {0x03c6, "phi"},+  {0x03d5, "phi1"},+  {0x03c0, "pi"},+  {0x002b, "plus"},+  {0x00b1, "plusminus"},+  {0x211e, "prescription"},+  {0x220f, "product"},+  {0x2282, "propersubset"},+  {0x2283, "propersuperset"},+  {0x221d, "proportional"},+  {0x03c8, "psi"},+  {0x0071, "q"},+  {0x003f, "question"},+  {0x00bf, "questiondown"},+  {0xf7bf, "questiondownsmall"},+  {0xf73f, "questionsmall"},+  {0x0022, "quotedbl"},+  {0x201e, "quotedblbase"},+  {0x201c, "quotedblleft"},+  {0x201d, "quotedblright"},+  {0x2018, "quoteleft"},+  {0x201b, "quotereversed"},+  {0x2019, "quoteright"},+  {0x201a, "quotesinglbase"},+  {0x0027, "quotesingle"},+  {0x0072, "r"},+  {0x0155, "racute"},+  {0x221a, "radical"},+  {0xf8e5, "radicalex"},+  {0x0159, "rcaron"},+  {0x0157, "rcommaaccent"},+  {0x2286, "reflexsubset"},+  {0x2287, "reflexsuperset"},+  {0x00ae, "registered"},+  {0x00ae, "registersans"},+  {0x00ae, "registerserif"},+  {0x2310, "revlogicalnot"},+  {0x03c1, "rho"},+  {0x02da, "ring"},+  {0xf6f1, "rsuperior"},+  {0x2590, "rtblock"},+  {0xf6dd, "rupiah"},+  {0x0073, "s"},+  {0x015b, "sacute"},+  {0x0161, "scaron"},+  {0x015f, "scedilla"},+  {0x015d, "scircumflex"},+  {0x0219, "scommaaccent"},+  {0x2033, "second"},+  {0x00a7, "section"},+  {0x003b, "semicolon"},+  {0x0037, "seven"},+  {0x215e, "seveneighths"},+  {0x2087, "seveninferior"},+  {0xf737, "sevenoldstyle"},+  {0x2077, "sevensuperior"},+  {0x2592, "shade"},+  {0x03c3, "sigma"},+  {0x03c2, "sigma1"},+  {0x223c, "similar"},+  {0x0036, "six"},+  {0x2086, "sixinferior"},+  {0xf736, "sixoldstyle"},+  {0x2076, "sixsuperior"},+  {0x002f, "slash"},+  {0x263a, "smileface"},+  {0x0020, "space"},+  {0x2660, "spade"},+  {0xf6f2, "ssuperior"},+  {0x00a3, "sterling"},+  {0x220b, "suchthat"},+  {0x2211, "summation"},+  {0x263c, "sun"},+  {0x0074, "t"},+  {0x03c4, "tau"},+  {0x0167, "tbar"},+  {0x0165, "tcaron"},+  {0x0163, "tcommaaccent"},+  {0x2234, "therefore"},+  {0x03b8, "theta"},+  {0x03d1, "theta1"},+  {0x00fe, "thorn"},+  {0x0033, "three"},+  {0x215c, "threeeighths"},+  {0x2083, "threeinferior"},+  {0xf733, "threeoldstyle"},+  {0x00be, "threequarters"},+  {0xf6de, "threequartersemdash"},+  {0x00b3, "threesuperior"},+  {0x02dc, "tilde"},+  {0x0303, "tildecomb"},+  {0x0384, "tonos"},+  {0x2122, "trademark"},+  {0x2122, "trademarksans"},+  {0x2122, "trademarkserif"},+  {0x25bc, "triagdn"},+  {0x25c4, "triaglf"},+  {0x25ba, "triagrt"},+  {0x25b2, "triagup"},+  {0xf6f3, "tsuperior"},+  {0x0032, "two"},+  {0x2025, "twodotenleader"},+  {0x2082, "twoinferior"},+  {0xf732, "twooldstyle"},+  {0x00b2, "twosuperior"},+  {0x2154, "twothirds"},+  {0x0075, "u"},+  {0x00fa, "uacute"},+  {0x016d, "ubreve"},+  {0x00fb, "ucircumflex"},+  {0x00fc, "udieresis"},+  {0x00f9, "ugrave"},+  {0x01b0, "uhorn"},+  {0x0171, "uhungarumlaut"},+  {0x016b, "umacron"},+  {0x005f, "underscore"},+  {0x2017, "underscoredbl"},+  {0x222a, "union"},+  {0x2200, "universal"},+  {0x0173, "uogonek"},+  {0x2580, "upblock"},+  {0x03c5, "upsilon"},+  {0x03cb, "upsilondieresis"},+  {0x03b0, "upsilondieresistonos"},+  {0x03cd, "upsilontonos"},+  {0x016f, "uring"},+  {0x0169, "utilde"},+  {0x0076, "v"},+  {0x0077, "w"},+  {0x1e83, "wacute"},+  {0x0175, "wcircumflex"},+  {0x1e85, "wdieresis"},+  {0x2118, "weierstrass"},+  {0x1e81, "wgrave"},+  {0x0078, "x"},+  {0x03be, "xi"},+  {0x0079, "y"},+  {0x00fd, "yacute"},+  {0x0177, "ycircumflex"},+  {0x00ff, "ydieresis"},+  {0x00a5, "yen"},+  {0x1ef3, "ygrave"},+  {0x007a, "z"},+  {0x017a, "zacute"},+  {0x017e, "zcaron"},+  {0x017c, "zdotaccent"},+  {0x0030, "zero"},+  {0x2080, "zeroinferior"},+  {0xf730, "zerooldstyle"},+  {0x2070, "zerosuperior"},+  {0x03b6, "zeta"},+  {0x007b, "{"},+  {0x007c, "|"},+  {0x007d, "}"},+  {0x007e, "~"},+  { 0, NULL }+};
+ xpdf/Object.cc view
@@ -0,0 +1,231 @@+//========================================================================+//+// Object.cc+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stddef.h>+#include "Object.h"+#include "Array.h"+#include "Dict.h"+#include "Error.h"+#include "Stream.h"+#include "XRef.h"++//------------------------------------------------------------------------+// Object+//------------------------------------------------------------------------++char *objTypeNames[numObjTypes] = {+  "boolean",+  "integer",+  "real",+  "string",+  "name",+  "null",+  "array",+  "dictionary",+  "stream",+  "ref",+  "cmd",+  "error",+  "eof",+  "none"+};++#ifdef DEBUG_MEM+int Object::numAlloc[numObjTypes] =+  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};+#endif++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:+    stream->incRef();+    break;+  case objCmd:+    obj->cmd = copyString(cmd);+    break;+  default:+    break;+  }+#ifdef DEBUG_MEM+  ++numAlloc[type];+#endif+  return obj;+}++Object *Object::fetch(XRef *xref, Object *obj) {+  return (type == objRef && xref) ?+         xref->fetch(ref.num, ref.gen, obj) : 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:+    if (!stream->decRef()) {+      delete stream;+    }+    break;+  case objCmd:+    gfree(cmd);+    break;+  default:+    break;+  }+#ifdef DEBUG_MEM+  --numAlloc[type];+#endif+  type = objNone;+}++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;+  int 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: %6d\n", objTypeNames[i], numAlloc[i]);+    }+  }+#endif+}
+ xpdf/Object.h view
@@ -0,0 +1,303 @@+//========================================================================+//+// 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 "GString.h"++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+#define initObj(t) ++numAlloc[type = t]+#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(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);++  // 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(char *nameA)+    { return type == objName && !strcmp(name, nameA); }+  GBool isDict(char *dictType);+  GBool isStream(char *dictType);+  GBool isCmd(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);+  Object *arrayGetNF(int i, Object *obj);++  // Dict accessors.+  int dictGetLength();+  void dictAdd(char *key, Object *val);+  GBool dictIs(char *dictType);+  Object *dictLookup(char *key, Object *obj);+  Object *dictLookupNF(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();+  char *streamGetLine(char *buf, int size);+  Guint streamGetPos();+  void streamSetPos(Guint pos, int dir = 0);+  Dict *streamGetDict();++  // Output.+  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+  static int			// number of each type of object+    numAlloc[numObjTypes];	//   currently allocated+#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)+  { return array->get(i, obj); }++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(char *dictType)+  { return dict->is(dictType); }++inline GBool Object::isDict(char *dictType)+  { return type == objDict && dictIs(dictType); }++inline Object *Object::dictLookup(char *key, Object *obj)+  { return dict->lookup(key, obj); }++inline Object *Object::dictLookupNF(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 char *Object::streamGetLine(char *buf, int size)+  { return stream->getLine(buf, size); }++inline Guint Object::streamGetPos()+  { return stream->getPos(); }++inline void Object::streamSetPos(Guint pos, int dir)+  { stream->setPos(pos, dir); }++inline Dict *Object::streamGetDict()+  { return stream->getDict(); }++#endif
+ xpdf/Outline.cc view
@@ -0,0 +1,151 @@+//========================================================================+//+// Outline.cc+//+// Copyright 2002-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include "gmem.h"+#include "GString.h"+#include "GList.h"+#include "Link.h"+#include "PDFDocEncoding.h"+#include "Outline.h"++//------------------------------------------------------------------------++Outline::Outline(Object *outlineObj, XRef *xref) {+  Object first, last;++  items = NULL;+  if (!outlineObj->isDict()) {+    return;+  }+  items = OutlineItem::readItemList(outlineObj->dictLookupNF("First", &first),+				    outlineObj->dictLookupNF("Last", &last),+				    xref);+  first.free();+  last.free();+}++Outline::~Outline() {+  if (items) {+    deleteGList(items, OutlineItem);+  }+}++//------------------------------------------------------------------------++OutlineItem::OutlineItem(Dict *dict, XRef *xrefA) {+  Object obj1;+  GString *s;+  int i;++  xref = xrefA;+  title = NULL;+  action = NULL;+  kids = NULL;++  if (dict->lookup("Title", &obj1)->isString()) {+    s = obj1.getString();+    if ((s->getChar(0) & 0xff) == 0xfe &&+	(s->getChar(1) & 0xff) == 0xff) {+      titleLen = (s->getLength() - 2) / 2;+      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));+      for (i = 0; i < titleLen; ++i) {+	title[i] = ((s->getChar(2 + 2*i) & 0xff) << 8) |+	           (s->getChar(3 + 2*i) & 0xff);+      }+    } else {+      titleLen = s->getLength();+      title = (Unicode *)gmallocn(titleLen, sizeof(Unicode));+      for (i = 0; i < titleLen; ++i) {+	title[i] = pdfDocEncoding[s->getChar(i) & 0xff];+      }+    }+  } else {+    titleLen = 0;+  }+  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();++  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();+}++OutlineItem::~OutlineItem() {+  close();+  if (title) {+    gfree(title);+  }+  if (action) {+    delete action;+  }+  firstRef.free();+  lastRef.free();+  nextRef.free();+}++GList *OutlineItem::readItemList(Object *firstItemRef, Object *lastItemRef,+				 XRef *xrefA) {+  GList *items;+  OutlineItem *item;+  Object obj;+  Object *p;++  items = new GList();+  p = firstItemRef;+  while (p->isRef()) {+    if (!p->fetch(xrefA, &obj)->isDict()) {+      obj.free();+      break;+    }+    item = new OutlineItem(obj.getDict(), xrefA);+    obj.free();+    items->append(item);+    if (p->getRef().num == lastItemRef->getRef().num &&+	p->getRef().gen == lastItemRef->getRef().gen) {+      break;+    }+    p = &item->nextRef;+  }+  return items;+}++void OutlineItem::open() {+  if (!kids) {+    kids = readItemList(&firstRef, &lastRef, xref);+  }+}++void OutlineItem::close() {+  if (kids) {+    deleteGList(kids, OutlineItem);+    kids = NULL;+  }+}
+ xpdf/Outline.h view
@@ -0,0 +1,76 @@+//========================================================================+//+// Outline.h+//+// Copyright 2002-2003 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 Outline {+public:++  Outline(Object *outlineObj, XRef *xref);+  ~Outline();++  GList *getItems() { return items; }++private:++  GList *items;			// NULL if document has no outline+				//   [OutlineItem]+};++//------------------------------------------------------------------------++class OutlineItem {+public:++  OutlineItem(Dict *dict, XRef *xrefA);+  ~OutlineItem();++  static GList *readItemList(Object *firstItemRef, Object *lastItemRef,+			     XRef *xrefA);++  void open();+  void close();++  Unicode *getTitle() { return title; }+  int getTitleLength() { return titleLen; }+  LinkAction *getAction() { return action; }+  GBool isOpen() { return startsOpen; }+  GBool hasKids() { return firstRef.isRef(); }+  GList *getKids() { return kids; }++private:++  XRef *xref;+  Unicode *title;+  int titleLen;+  LinkAction *action;+  Object firstRef;+  Object lastRef;+  Object nextRef;+  GBool startsOpen;+  GList *kids;			// NULL unless this item is open [OutlineItem]+};++#endif
+ xpdf/OutputDev.cc view
@@ -0,0 +1,131 @@+//========================================================================+//+// OutputDev.cc+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stddef.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, 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);+  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) {+  int i, j;++  if (inlineImg) {+    str->reset();+    j = height * ((width + 7) / 8);+    for (i = 0; i < j; ++i)+      str->getChar();+    str->close();+  }+}++void OutputDev::drawImage(GfxState *state, Object *ref, Stream *str,+			  int width, int height, GfxImageColorMap *colorMap,+			  int *maskColors, GBool inlineImg) {+  int i, j;++  if (inlineImg) {+    str->reset();+    j = height * ((width * colorMap->getNumPixelComps() *+		   colorMap->getBits() + 7) / 8);+    for (i = 0; i < j; ++i)+      str->getChar();+    str->close();+  }+}++void OutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,+				int width, int height,+				GfxImageColorMap *colorMap,+				Stream *maskStr,+				int maskWidth, int maskHeight,+				GBool maskInvert) {+  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);+}++void OutputDev::drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,+				    int width, int height,+				    GfxImageColorMap *colorMap,+				    Stream *maskStr,+				    int maskWidth, int maskHeight,+				    GfxImageColorMap *maskColorMap) {+  drawImage(state, ref, str, width, height, colorMap, NULL, gFalse);+}++#if OPI_SUPPORT+void OutputDev::opiBegin(GfxState *state, Dict *opiDict) {+}++void OutputDev::opiEnd(GfxState *state, Dict *opiDict) {+}+#endif
+ xpdf/OutputDev.h view
@@ -0,0 +1,250 @@+//========================================================================+//+// 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 GfxState;+struct GfxColor;+class GfxColorSpace;+class GfxImageColorMap;+class GfxFunctionShading;+class GfxAxialShading;+class GfxRadialShading;+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 functionShadedFill(), axialShadedFill(), and+  // radialShadedFill()?  If this returns false, these shaded fills+  // will be reduced to a series of other drawing operations.+  virtual GBool useShadedFills() { return gFalse; }++  // Does this device use drawForm()?  If this returns false,+  // form-type XObjects will be interpreted (i.e., unrolled).+  virtual GBool useDrawForm() { return gFalse; }++  // 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; }++  //----- 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, Catalog *catalog,+			       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() {}++  // Dump page contents to display.+  virtual void dump() {}++  //----- 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, 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 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) {}++  //----- path painting+  virtual void stroke(GfxState *state) {}+  virtual void fill(GfxState *state) {}+  virtual void eoFill(GfxState *state) {}+  virtual void tilingPatternFill(GfxState *state, Object *str,+				 int paintType, Dict *resDict,+				 double *mat, double *bbox,+				 int x0, int y0, int x1, int y1,+				 double xStep, double yStep) {}+  virtual GBool functionShadedFill(GfxState *state,+				   GfxFunctionShading *shading)+    { return gFalse; }+  virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading)+    { return gFalse; }+  virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading)+    { 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) {}++  //----- image drawing+  virtual void drawImageMask(GfxState *state, Object *ref, Stream *str,+			     int width, int height, GBool invert,+			     GBool inlineImg);+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,+			 int width, int height, GfxImageColorMap *colorMap,+			 int *maskColors, GBool inlineImg);+  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,+			       int width, int height,+			       GfxImageColorMap *colorMap,+			       Stream *maskStr, int maskWidth, int maskHeight,+			       GBool maskInvert);+  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,+				   int width, int height,+				   GfxImageColorMap *colorMap,+				   Stream *maskStr,+				   int maskWidth, int maskHeight,+				   GfxImageColorMap *maskColorMap);++#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, Catalog *catalog) {}++#if 1 //~tmp: turn off anti-aliasing temporarily+  virtual GBool getVectorAntialias() { return gFalse; }+  virtual void setVectorAntialias(GBool vaa) {}+#endif++private:++  double defCTM[6];		// default coordinate transform matrix+  double defICTM[6];		// inverse of default CTM+};++#endif
+ xpdf/PDFCore.cc view
@@ -0,0 +1,2044 @@+//========================================================================+//+// PDFCore.cc+//+// Copyright 2004 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <math.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 "TextOutputDev.h"+#include "CoreOutputDev.h"+#include "PDFCore.h"++//------------------------------------------------------------------------+// PDFCorePage+//------------------------------------------------------------------------++PDFCorePage::PDFCorePage(int pageA, int wA, int hA, int tileWA, int tileHA) {+  page = pageA;+  tiles = new GList();+  w = wA;+  h = hA;+  tileW = tileWA;+  tileH = tileHA;+  links = NULL;+  text = NULL;+}++PDFCorePage::~PDFCorePage() {+  deleteGList(tiles, PDFCoreTile);+  if (links) {+    delete links;+  }+  if (text) {+    delete text;+  }+}++//------------------------------------------------------------------------+// PDFCoreTile+//------------------------------------------------------------------------++PDFCoreTile::PDFCoreTile(int xDestA, int yDestA) {+  xMin = 0;+  yMin = 0;+  xMax = 0;+  yMax = 0;+  xDest = xDestA;+  yDest = yDestA;+  bitmap = NULL;+}++PDFCoreTile::~PDFCoreTile() {+  if (bitmap) {+    delete bitmap;+  }+}+++//------------------------------------------------------------------------+// PDFCore+//------------------------------------------------------------------------++PDFCore::PDFCore(SplashColorMode colorModeA, int bitmapRowPadA,+		 GBool reverseVideoA, SplashColorPtr paperColorA,+		 GBool incrementalUpdate) {+  int i;++  doc = NULL;+  continuousMode = globalParams->getContinuousView();+  drawAreaWidth = drawAreaHeight = 0;+  maxPageW = totalDocH = 0;+  pageY = NULL;+  topPage = 0;+  scrollX = scrollY = 0;+  zoom = defZoom;+  dpi = 0;+  rotate = 0;++  selectPage = 0;+  selectULX = selectLRX = 0;+  selectULY = selectLRY = 0;+  dragging = gFalse;+  lastDragLeft = lastDragTop = gTrue;++  historyCur = pdfHistorySize - 1;+  historyBLen = historyFLen = 0;+  for (i = 0; i < pdfHistorySize; ++i) {+    history[i].fileName = NULL;+  }+++  pages = new GList();+  curTile = NULL;++  splashColorCopy(paperColor, paperColorA);+  out = new CoreOutputDev(colorModeA, bitmapRowPadA,+			  reverseVideoA, paperColorA, incrementalUpdate,+			  &redrawCbk, this);+  out->startDoc(NULL);+}++PDFCore::~PDFCore() {+  int i;++  if (doc) {+    delete doc;+  }+  for (i = 0; i < pdfHistorySize; ++i) {+    if (history[i].fileName) {+      delete history[i].fileName;+    }+  }+  gfree(pageY);+  deleteGList(pages, PDFCorePage);+  delete out;+}++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::loadFile2(PDFDoc *newDoc) {+  int err;+  double w, h, t;+  int i;++  // open the PDF file+  if (!newDoc->isOk()) {+    err = newDoc->getErrorCode();+    delete newDoc;+    return err;+  }++  // replace old document+  if (doc) {+    delete doc;+  }+  doc = newDoc;+  if (out) {+    out->startDoc(doc->getXRef());+  }++  // nothing displayed yet+  topPage = -99;+  while (pages->getLength() > 0) {+    delete (PDFCorePage *)pages->del(0);+  }++  // compute the max unscaled page size+  maxUnscaledPageW = maxUnscaledPageH = 0;+  for (i = 1; i <= doc->getNumPages(); ++i) {+    w = doc->getPageCropWidth(i);+    h = doc->getPageCropHeight(i);+    if (doc->getPageRotate(i) == 90 || doc->getPageRotate(i) == 270) {+      t = w; w = h; h = t;+    }+    if (w > maxUnscaledPageW) {+      maxUnscaledPageW = w;+    }+    if (h > maxUnscaledPageH) {+      maxUnscaledPageH = h;+    }+  }++  return errNone;+}++void PDFCore::clear() {+  if (!doc) {+    return;+  }++  // no document+  delete doc;+  doc = NULL;+  out->clear();++  // no page displayed+  topPage = -99;+  while (pages->getLength() > 0) {+    delete (PDFCorePage *)pages->del(0);+  }++  // redraw+  scrollX = scrollY = 0;+  redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, gTrue);+  updateScrollbars();+}++PDFDoc *PDFCore::takeDoc(GBool redraw) {+  PDFDoc *docA;++  if (!doc) {+    return NULL;+  }++  // no document+  docA = doc;+  doc = NULL;+  out->clear();++  // no page displayed+  topPage = -99;+  while (pages->getLength() > 0) {+    delete (PDFCorePage *)pages->del(0);+  }++  // redraw+  scrollX = scrollY = 0;+  if (redraw) {+    redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, gTrue);+    updateScrollbars();+  }++  return docA;+}++void PDFCore::displayPage(int topPageA, double zoomA, int rotateA,+			  GBool scrollToTop, GBool addToHist) {+  int scrollXA, scrollYA;++  scrollXA = scrollX;+  if (continuousMode) {+    scrollYA = -1;+  } else if (scrollToTop) {+    scrollYA = 0;+  } else {+    scrollYA = scrollY;+  }+  if (zoomA != zoom) {+    scrollXA = 0;+    scrollYA = continuousMode ? -1 : 0;+  }++  dragging = gFalse;+  lastDragLeft = lastDragTop = gTrue;++  update(topPageA, scrollXA, scrollYA, zoomA, rotateA, gTrue, addToHist);+}++void PDFCore::displayDest(LinkDest *dest, double zoomA, int rotateA,+			  GBool addToHist) {+  Ref pageRef;+  int topPageA;+  int dx, dy, scrollXA, scrollYA;++  if (dest->isPageRef()) {+    pageRef = dest->getPageRef();+    topPageA = doc->findPage(pageRef.num, pageRef.gen);+  } else {+    topPageA = dest->getPageNum();+  }+  if (topPageA <= 0 || topPageA > doc->getNumPages()) {+    topPageA = 1;+  }+  scrollXA = scrollX;+  scrollYA = continuousMode ? -1 : scrollY;+  switch (dest->getKind()) {+  case destXYZ:+    cvtUserToDev(topPageA, dest->getLeft(), dest->getTop(), &dx, &dy);+    scrollXA = dest->getChangeLeft() ? dx : scrollX;+    if (dest->getChangeTop()) {+      scrollYA = dy;+    } else {+      if (topPage <= 0) {+	scrollYA = 0;+      } else if (continuousMode) {+	scrollYA = scrollY - pageY[topPage - 1];+      } else {+	scrollYA = scrollY;+      }+    }+    if (continuousMode && topPage > 0) {+      scrollYA += pageY[topPageA - 1];+    }+    //~ what is the zoom parameter?+    break;+  case destFit:+  case destFitB:+    //~ do fit+    scrollXA = 0;+    scrollYA = continuousMode ? -1 : 0;+    break;+  case destFitH:+  case destFitBH:+    //~ do fit+    cvtUserToDev(topPageA, 0, dest->getTop(), &dx, &dy);+    if (continuousMode && topPage > 0) {+      dy += pageY[topPageA - 1];+    }+    scrollXA = 0;+    scrollYA = dy;+    break;+  case destFitV:+  case destFitBV:+    //~ do fit+    cvtUserToDev(topPageA, dest->getLeft(), 0, &dx, &dy);+    scrollXA = dx;+    scrollYA = continuousMode ? -1 : 0;+    break;+  case destFitR:+    //~ do fit+    cvtUserToDev(topPageA, dest->getLeft(), dest->getTop(), &dx, &dy);+    if (continuousMode && topPage > 0) {+      dy += pageY[topPageA - 1];+    }+    scrollXA = dx;+    scrollYA = dy;+    break;+  }+  update(topPageA, scrollXA, scrollYA, zoom, rotate, gFalse,+	 addToHist && topPageA != topPage);+}++void PDFCore::update(int topPageA, int scrollXA, int scrollYA,+		     double zoomA, int rotateA, GBool force, GBool addToHist) {+  double hDPI, vDPI, dpiA, uw, uh, ut;+  int w, h, t, x0, x1, y0, y1, x, y;+  int rot;+  int pg0, pg1;+  PDFCoreTile *tile;+  PDFCorePage *page;+  PDFHistory *hist;+  SplashColor xorColor;+  GBool needUpdate;+  int i, j;++  // check for document and valid page number+  if (!doc) {+    // save the new settings+    zoom = zoomA;+    rotate = rotateA;+    return;+  }+  if (topPageA <= 0 || topPageA > doc->getNumPages()) {+    return;+  }++  needUpdate = gFalse;++  // check for changes to the PDF file+  if ((force || (!continuousMode && topPage != topPageA)) &&+      checkForNewFile()) {+    if (loadFile(doc->getFileName()) == errNone) {+      if (topPageA > doc->getNumPages()) {+	topPageA = doc->getNumPages();+      }+      needUpdate = gTrue;+    }+  }++  // compute the DPI+  if (continuousMode) {+    uw = maxUnscaledPageW;+    uh = maxUnscaledPageH;+    rot = rotateA;+  } else {+    uw = doc->getPageCropWidth(topPageA);+    uh = doc->getPageCropHeight(topPageA);+    rot = rotateA + doc->getPageRotate(topPageA);+    if (rot >= 360) {+      rot -= 360;+    } else if (rot < 0) {+      rot += 360;+    }+  }+  if (rot == 90 || rot == 270) {+    ut = uw; uw = uh; uh = ut;+  }+  if (zoomA == zoomPage) {+    hDPI = (drawAreaWidth / uw) * 72;+    if (continuousMode) {+      vDPI = ((drawAreaHeight - continuousModePageSpacing) / uh) * 72;+    } else {+      vDPI = (drawAreaHeight / uh) * 72;+    }+    dpiA = (hDPI < vDPI) ? hDPI : vDPI;+  } else if (zoomA == zoomWidth) {+    dpiA = (drawAreaWidth / uw) * 72;+  } else {+    dpiA = 0.01 * zoomA * 72;+  }+  // this can happen if the window hasn't been sized yet+  if (dpiA <= 0) {+    dpiA = 1;+  }++  // if the display properties have changed, create a new PDFCorePage+  // object+  if (force || pages->getLength() == 0 ||+      (!continuousMode && topPageA != topPage) ||+      zoomA != zoom || dpiA != dpi || rotateA != rotate) {+    needUpdate = gTrue;+    setSelection(0, 0, 0, 0, 0);+    while (pages->getLength() > 0) {+      delete (PDFCorePage *)pages->del(0);+    }+    zoom = zoomA;+    rotate = rotateA;+    dpi = dpiA;+    if (continuousMode) {+      maxPageW = totalDocH = 0;+      pageY = (int *)greallocn(pageY, doc->getNumPages(), sizeof(int));+      for (i = 1; i <= doc->getNumPages(); ++i) {+	pageY[i-1] = totalDocH;+	w = (int)((doc->getPageCropWidth(i) * dpi) / 72 + 0.5);+	h = (int)((doc->getPageCropHeight(i) * dpi) / 72 + 0.5);+	rot = rotate + doc->getPageRotate(i);+	if (rot >= 360) {+	  rot -= 360;+	} else if (rot < 0) {+	  rot += 360;+	}+	if (rot == 90 || rot == 270) {+	  t = w; w = h; h = t;+	}+	if (w > maxPageW) {+	  maxPageW = w;+	}+	totalDocH += h;+	if (i < doc->getNumPages()) {+	  totalDocH += continuousModePageSpacing;+	}+      }+    } else {+      rot = rotate + doc->getPageRotate(topPageA);+      if (rot >= 360) {+	rot -= 360;+      } else if (rot < 0) {+	rot += 360;+      }+      addPage(topPageA, rot);+    }+  } else {+    // erase the selection+    if (selectULX != selectLRX && selectULY != selectLRY) {+      xorColor[0] = xorColor[1] = xorColor[2] = 0xff;+      xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY,+		   new SplashSolidColor(xorColor));+    }+  }+  if (continuousMode) {+    page = NULL; // make gcc happy+  } else {+    page = (PDFCorePage *)pages->get(0);+  }+  topPage = topPageA;++  // adjust the scroll position+  scrollX = scrollXA;+  if (continuousMode && scrollYA < 0) {+    scrollY = pageY[topPage - 1];+  } else {+    scrollY = scrollYA;+  }+  w = continuousMode ? maxPageW : page->w;+  if (scrollX > w - drawAreaWidth) {+    scrollX = w - drawAreaWidth;+  }+  if (scrollX < 0) {+    scrollX = 0;+  }+  h = continuousMode ? totalDocH : page->h;+  if (scrollY > h - drawAreaHeight) {+    scrollY = h - drawAreaHeight;+  }+  if (scrollY < 0) {+    scrollY = 0;+  }++  // find topPage, and the first and last pages to be rasterized+  if (continuousMode) {+    //~ should use a binary search+    for (i = 2; i <= doc->getNumPages(); ++i) {+      if (pageY[i-1] > scrollY - drawAreaHeight / 2) {+	break;+      }+    }+    pg0 = i - 1;+    for (i = pg0 + 1; i <= doc->getNumPages(); ++i) {+      if (pageY[i-1] > scrollY) {+	break;+      }+    }+    topPage = i - 1;+    for (i = topPage + 1; i <= doc->getNumPages(); ++i) {+      if (pageY[i-1] > scrollY + drawAreaHeight + drawAreaHeight / 2) {+	break;+      }+    }+    pg1 = i - 1;++    // delete pages that are no longer needed and insert new pages+    // objects that are needed+    while (pages->getLength() > 0 &&+	   ((PDFCorePage *)pages->get(0))->page < pg0) {+      delete (PDFCorePage *)pages->del(0);+    }+    i = pages->getLength() - 1;+    while (i > 0 && ((PDFCorePage *)pages->get(i))->page > pg1) {+      delete (PDFCorePage *)pages->del(i--);+    }+    j = pages->getLength() > 0 ? ((PDFCorePage *)pages->get(0))->page - 1+                               : pg1;+    for (i = pg0; i <= j; ++i) {+      rot = rotate + doc->getPageRotate(i);+      if (rot >= 360) {+	rot -= 360;+      } else if (rot < 0) {+	rot += 360;+      }+      addPage(i, rot);+    }      +    j = ((PDFCorePage *)pages->get(pages->getLength() - 1))->page;+    for (i = j + 1; i <= pg1; ++i) {+      rot = rotate + doc->getPageRotate(i);+      if (rot >= 360) {+	rot -= 360;+      } else if (rot < 0) {+	rot += 360;+      }+      addPage(i, rot);+    }++  } else {+    pg0 = pg1 = topPage;+  }++  // delete tiles that are no longer needed+  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    j = 0;+    while (j < page->tiles->getLength()) {+      tile = (PDFCoreTile *)page->tiles->get(j);+      if (continuousMode) {+	y0 = pageY[page->page - 1] + tile->yMin;+	y1 = pageY[page->page - 1] + tile->yMax;+      } else {+	y0 = tile->yMin;+	y1 = tile->yMax;+      }+      if (tile->xMax < scrollX - drawAreaWidth / 2 ||+	  tile->xMin > scrollX + drawAreaWidth + drawAreaWidth / 2 ||+	  y1 < scrollY - drawAreaHeight / 2 ||+	  y0 > scrollY + drawAreaHeight + drawAreaHeight / 2) {+	delete (PDFCoreTile *)page->tiles->del(j);+      } else {+	++j;+      }+    }+  }++  // update page positions+  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    page->xDest = -scrollX;+    if (continuousMode) {+      page->yDest = pageY[page->page - 1] - scrollY;+    } else {+      page->yDest = -scrollY;+    }+    if (continuousMode) {+      if (page->w < maxPageW) {+	page->xDest += (maxPageW - page->w) / 2;+      }+      if (maxPageW < drawAreaWidth) {+	page->xDest += (drawAreaWidth - maxPageW) / 2;+      }+    } else if (page->w < drawAreaWidth) {+      page->xDest += (drawAreaWidth - page->w) / 2;+    }+    if (continuousMode && totalDocH < drawAreaHeight) {+      page->yDest += (drawAreaHeight - totalDocH) / 2;+    } else if (!continuousMode && page->h < drawAreaHeight) {+      page->yDest += (drawAreaHeight - page->h) / 2;+    }+  }++  // rasterize any new tiles+  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    x0 = page->xDest;+    x1 = x0 + page->w - 1;+    if (x0 < -drawAreaWidth / 2) {+      x0 = -drawAreaWidth / 2;+    }+    if (x1 > drawAreaWidth + drawAreaWidth / 2) {+      x1 = drawAreaWidth + drawAreaWidth / 2;+    }+    x0 = ((x0 - page->xDest) / page->tileW) * page->tileW;+    x1 = ((x1 - page->xDest) / page->tileW) * page->tileW;+    y0 = page->yDest;+    y1 = y0 + page->h - 1;+    if (y0 < -drawAreaHeight / 2) {+      y0 = -drawAreaHeight / 2;+    }+    if (y1 > drawAreaHeight + drawAreaHeight / 2) {+      y1 = drawAreaHeight + drawAreaHeight / 2;+    }+    y0 = ((y0 - page->yDest) / page->tileH) * page->tileH;+    y1 = ((y1 - page->yDest) / page->tileH) * page->tileH;+    for (y = y0; y <= y1; y += page->tileH) {+      for (x = x0; x <= x1; x += page->tileW) {+	needTile(page, x, y);+      }+    }+  }++  // update tile positions+  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    for (j = 0; j < page->tiles->getLength(); ++j) {+      tile = (PDFCoreTile *)page->tiles->get(j);+      tile->xDest = tile->xMin - scrollX;+      if (continuousMode) {+	tile->yDest = tile->yMin + pageY[page->page - 1] - scrollY;+      } else {+	tile->yDest = tile->yMin - scrollY;+      }+      if (continuousMode) {+	if (page->w < maxPageW) {+	  tile->xDest += (maxPageW - page->w) / 2;+	}+	if (maxPageW < drawAreaWidth) {+	  tile->xDest += (drawAreaWidth - maxPageW) / 2;+	}+      } else if (page->w < drawAreaWidth) {+	tile->xDest += (drawAreaWidth - page->w) / 2;+      }+      if (continuousMode && totalDocH < drawAreaHeight) {+	tile->yDest += (drawAreaHeight - totalDocH) / 2;+      } else if (!continuousMode && page->h < drawAreaHeight) {+	tile->yDest += (drawAreaHeight - page->h) / 2;+      }+    }+  }++  // redraw the selection+  if (selectULX != selectLRX && selectULY != selectLRY) {+    xorColor[0] = xorColor[1] = xorColor[2] = 0xff;+    xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY,+		 new SplashSolidColor(xorColor));+  }++  // redraw the window+  redrawWindow(0, 0, drawAreaWidth, drawAreaHeight, needUpdate);+  updateScrollbars();++  // add to history+  if (addToHist) {+    if (++historyCur == pdfHistorySize) {+      historyCur = 0;+    }+    hist = &history[historyCur];+    if (hist->fileName) {+      delete hist->fileName;+    }+    if (doc->getFileName()) {+      hist->fileName = doc->getFileName()->copy();+    } else {+      hist->fileName = NULL;+    }+    hist->page = topPage;+    if (historyBLen < pdfHistorySize) {+      ++historyBLen;+    }+    historyFLen = 0;+  }+}++void PDFCore::addPage(int pg, int rot) {+  PDFCorePage *page;+  int w, h, t, tileW, tileH, i;++  w = (int)((doc->getPageCropWidth(pg) * dpi) / 72 + 0.5);+  h = (int)((doc->getPageCropHeight(pg) * dpi) / 72 + 0.5);+  if (rot == 90 || rot == 270) {+    t = w; w = h; h = t;+  }+  tileW = 2 * drawAreaWidth;+  if (tileW < 1500) {+    tileW = 1500;+  }+  if (tileW > w) {+    tileW = w;+  }+  tileH = 2 * drawAreaHeight;+  if (tileH < 1500) {+    tileH = 1500;+  }+  if (tileH > h) {+    tileH = h;+  }+  page = new PDFCorePage(pg, w, h, tileW, tileH);+  for (i = 0;+       i < pages->getLength() && pg > ((PDFCorePage *)pages->get(i))->page;+       ++i) ;+  pages->insert(i, page);+}++void PDFCore::needTile(PDFCorePage *page, int x, int y) {+  PDFCoreTile *tile;+  TextOutputDev *textOut;+  int xDest, yDest, sliceW, sliceH;+  int i;++  for (i = 0; i < page->tiles->getLength(); ++i) {+    tile = (PDFCoreTile *)page->tiles->get(i);+    if (x == tile->xMin && y == tile->yMin) {+      return;+    }+  }++  setBusyCursor(gTrue);++  sliceW = page->tileW;+  if (x + sliceW > page->w) {+    sliceW = page->w - x;+  }+  sliceH = page->tileH;+  if (y + sliceH > page->h) {+    sliceH = page->h - y;+  }++  xDest = x - scrollX;+  if (continuousMode) {+    yDest = y + pageY[page->page - 1] - scrollY;+  } else {+    yDest = y - scrollY;+  }+  if (continuousMode) {+    if (page->w < maxPageW) {+      xDest += (maxPageW - page->w) / 2;+    }+    if (maxPageW < drawAreaWidth) {+      xDest += (drawAreaWidth - maxPageW) / 2;+    }+  } else if (page->w < drawAreaWidth) {+    xDest += (drawAreaWidth - page->w) / 2;+  }+  if (continuousMode && totalDocH < drawAreaHeight) {+    yDest += (drawAreaHeight - totalDocH) / 2;+  } else if (!continuousMode && page->h < drawAreaHeight) {+    yDest += (drawAreaHeight - page->h) / 2;+  }+  curTile = tile = newTile(xDest, yDest);+  curPage = page;+  tile->xMin = x;+  tile->yMin = y;+  tile->xMax = x + sliceW;+  tile->yMax = y + sliceH;+  tile->edges = 0;+  if (tile->xMin == 0) {+    tile->edges |= pdfCoreTileLeftEdge;+  }+  if (tile->xMax == page->w) {+    tile->edges |= pdfCoreTileRightEdge;+  }+  if (continuousMode) {+    if (tile->yMin == 0) {+      tile->edges |= pdfCoreTileTopSpace;+      if (page->page == 1) {+	tile->edges |= pdfCoreTileTopEdge;+      }+    }+    if (tile->yMax == page->h) {+      tile->edges |= pdfCoreTileBottomSpace;+      if (page->page == doc->getNumPages()) {+	tile->edges |= pdfCoreTileBottomEdge;+      }+    }+  } else {+    if (tile->yMin == 0) {+      tile->edges |= pdfCoreTileTopEdge;+    }+    if (tile->yMax == page->h) {+      tile->edges |= pdfCoreTileBottomEdge;+    }+  }+  doc->displayPageSlice(out, page->page, dpi, dpi, rotate,+			gFalse, gTrue, gFalse, x, y, sliceW, sliceH);+  tile->bitmap = out->takeBitmap();+  memcpy(tile->ctm, out->getDefCTM(), 6 * sizeof(double));+  memcpy(tile->ictm, out->getDefICTM(), 6 * sizeof(double));+  if (!page->links) {+    page->links = doc->getLinks(page->page);+  }+  if (!page->text) {+    if ((textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse))) {+      doc->displayPage(textOut, page->page, dpi, dpi, rotate,+		       gFalse, gTrue, gFalse);+      page->text = textOut->takeText();+      delete textOut;+    }+  }+  page->tiles->append(tile);+  curTile = NULL;+  curPage = NULL;++  setBusyCursor(gFalse);+}++GBool PDFCore::gotoNextPage(int inc, GBool top) {+  int pg, scrollYA;++  if (!doc || doc->getNumPages() == 0 || topPage >= doc->getNumPages()) {+    return gFalse;+  }+  if ((pg = topPage + inc) > doc->getNumPages()) {+    pg = doc->getNumPages();+  }+  if (continuousMode) {+    scrollYA = -1;+  } else if (top) {+    scrollYA = 0;+  } else {+    scrollYA = scrollY;+  }+  update(pg, scrollX, scrollYA, zoom, rotate, gFalse, gTrue);+  return gTrue;+}++GBool PDFCore::gotoPrevPage(int dec, GBool top, GBool bottom) {+  int pg, scrollYA;++  if (!doc || doc->getNumPages() == 0 || topPage <= 1) {+    return gFalse;+  }+  if ((pg = topPage - dec) < 1) {+    pg = 1;+  }+  if (continuousMode) {+    scrollYA = -1;+  } else if (top) {+    scrollYA = 0;+  } else if (bottom) {+    scrollYA = ((PDFCorePage *)pages->get(0))->h - drawAreaHeight;+    if (scrollYA < 0) {+      scrollYA = 0;+    }+  } else {+    scrollYA = scrollY;+  }+  update(pg, scrollX, scrollYA, zoom, rotate, gFalse, gTrue);+  return gTrue;+}++GBool PDFCore::gotoNamedDestination(GString *dest) {+  LinkDest *d;++  if (!doc) {+    return gFalse;+  }+  if (!(d = doc->findDest(dest))) {+    return gFalse;+  }+  displayDest(d, zoom, rotate, gTrue);+  delete d;+  return gTrue;+}++GBool PDFCore::goForward() {+  int pg;++  if (historyFLen == 0) {+    return gFalse;+  }+  if (++historyCur == pdfHistorySize) {+    historyCur = 0;+  }+  --historyFLen;+  ++historyBLen;+  if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {+    if (loadFile(history[historyCur].fileName) != errNone) {+      return gFalse;+    }+  }+  pg = history[historyCur].page;+  update(pg, scrollX, continuousMode ? -1 : scrollY,+	 zoom, rotate, gFalse, gFalse);+  return gTrue;+}++GBool PDFCore::goBackward() {+  int pg;++  if (historyBLen <= 1) {+    return gFalse;+  }+  if (--historyCur < 0) {+    historyCur = pdfHistorySize - 1;+  }+  --historyBLen;+  ++historyFLen;+  if (!doc || history[historyCur].fileName->cmp(doc->getFileName()) != 0) {+    if (loadFile(history[historyCur].fileName) != errNone) {+      return gFalse;+    }+  }+  pg = history[historyCur].page;+  update(pg, scrollX, continuousMode ? -1 : scrollY,+	 zoom, rotate, gFalse, gFalse);+  return gTrue;+}++void PDFCore::scrollLeft(int nCols) {+  scrollTo(scrollX - nCols, scrollY);+}++void PDFCore::scrollRight(int nCols) {+  scrollTo(scrollX + nCols, scrollY);+}++void PDFCore::scrollUp(int nLines) {+  scrollTo(scrollX, scrollY - nLines);+}++void PDFCore::scrollUpPrevPage(int nLines) {+  if (!continuousMode && scrollY == 0) {+    gotoPrevPage(1, gFalse, gTrue);+  } else {+    scrollTo(scrollX, scrollY - nLines);+  }+}++void PDFCore::scrollDown(int nLines) {+  scrollTo(scrollX, scrollY + nLines);+}++void PDFCore::scrollDownNextPage(int nLines) {+  if (!continuousMode &&+      scrollY >= ((PDFCorePage *)pages->get(0))->h - drawAreaHeight) {+    gotoNextPage(1, gTrue);+  } else {+    scrollTo(scrollX, scrollY + nLines);+  }+}++void PDFCore::scrollPageUp() {+  if (!continuousMode && scrollY == 0) {+    gotoPrevPage(1, gFalse, gTrue);+  } else {+    scrollTo(scrollX, scrollY - drawAreaHeight);+  }+}++void PDFCore::scrollPageDown() {+  if (!continuousMode &&+      scrollY >= ((PDFCorePage *)pages->get(0))->h - drawAreaHeight) {+    gotoNextPage(1, gTrue);+  } else {+    scrollTo(scrollX, scrollY + drawAreaHeight);+  }+}++void PDFCore::scrollTo(int x, int y) {+  update(topPage, x, y < 0 ? 0 : y, zoom, rotate, gFalse, gFalse);+}++void PDFCore::scrollToLeftEdge() {+  update(topPage, 0, scrollY, zoom, rotate, gFalse, gFalse);+}++void PDFCore::scrollToRightEdge() {+  PDFCorePage *page;++  page = (PDFCorePage *)pages->get(0);+  update(topPage, page->w - drawAreaWidth, scrollY,+	 zoom, rotate, gFalse, gFalse);+}++void PDFCore::scrollToTopEdge() {+  int y;++  y = continuousMode ? pageY[topPage - 1] : 0;+  update(topPage, scrollX, y, zoom, rotate, gFalse, gFalse);+}++void PDFCore::scrollToBottomEdge() {+  PDFCorePage *page;+  int y, i;++  for (i = pages->getLength() - 1; i > 0; --i) {+    page = (PDFCorePage *)pages->get(i);+    if (page->yDest < drawAreaHeight) {+      break;+    }+  }+  page = (PDFCorePage *)pages->get(i);+  if (continuousMode) {+    y = pageY[page->page - 1] + page->h - drawAreaHeight;+  } else {+    y = page->h - drawAreaHeight;+  }+  update(topPage, scrollX, y, zoom, rotate, gFalse, gFalse);+}++void PDFCore::scrollToTopLeft() {+  int y;++  y = continuousMode ? pageY[topPage - 1] : 0;+  update(topPage, 0, y, zoom, rotate, gFalse, gFalse);+}++void PDFCore::scrollToBottomRight() {+  PDFCorePage *page;+  int x, y, i;++  for (i = pages->getLength() - 1; i > 0; --i) {+    page = (PDFCorePage *)pages->get(i);+    if (page->yDest < drawAreaHeight) {+      break;+    }+  }+  page = (PDFCorePage *)pages->get(i);+  x = page->w - drawAreaWidth;+  if (continuousMode) {+    y = pageY[page->page - 1] + page->h - drawAreaHeight;+  } else {+    y = page->h - drawAreaHeight;+  }+  update(topPage, x, y, zoom, rotate, gFalse, gFalse);+}++void PDFCore::zoomToRect(int pg, double ulx, double uly,+			 double lrx, double lry) {+  int x0, y0, x1, y1, u, sx, sy;+  double rx, ry, newZoom, t;+  PDFCorePage *p;++  cvtUserToDev(pg, ulx, uly, &x0, &y0);+  cvtUserToDev(pg, lrx, lry, &x1, &y1);+  if (x0 > x1) {+    u = x0; x0 = x1; x1 = u;+  }+  if (y0 > y1) {+    u = y0; y0 = y1; y1 = u;+  }+  rx = (double)drawAreaWidth / (double)(x1 - x0);+  ry = (double)drawAreaHeight / (double)(y1 - y0);+  if (rx < ry) {+    newZoom = rx * (dpi / (0.01 * 72));+    sx = (int)(rx * x0);+    t = (drawAreaHeight * (x1 - x0)) / drawAreaWidth;+    sy = (int)(rx * (y0 + y1 - t) / 2);+    if (continuousMode) {+      if ((p = findPage(pg)) && p->w < maxPageW) {+	sx += (int)(0.5 * rx * (maxPageW - p->w));+      }+      u = (pg - 1) * continuousModePageSpacing;+      sy += (int)(rx * (pageY[pg - 1] - u)) + u;+    }+  } else {+    newZoom = ry * (dpi / (0.01 * 72));+    t = (drawAreaWidth * (y1 - y0)) / drawAreaHeight;+    sx = (int)(ry * (x0 + x1 - t) / 2);+    sy = (int)(ry * y0);+    if (continuousMode) {+      if ((p = findPage(pg)) && p->w < maxPageW) {+	sx += (int)(0.5 * rx * (maxPageW - p->w));+      }+      u = (pg - 1) * continuousModePageSpacing;+      sy += (int)(ry * (pageY[pg - 1] - u)) + u;+    }+  }+  update(pg, sx, sy, newZoom, rotate, gFalse, gFalse);+}++void PDFCore::zoomCentered(double zoomA) {+  int sx, sy, rot, hAdjust, vAdjust, i;+  double dpi1, dpi2, pageW, pageH;+  PDFCorePage *page;++  if (zoomA == zoomPage) {+    if (continuousMode) {+      pageW = (rotate == 90 || rotate == 270) ? maxUnscaledPageH+	                                      : maxUnscaledPageW;+      pageH = (rotate == 90 || rotate == 270) ? maxUnscaledPageW+	                                      : maxUnscaledPageH;+      dpi1 = 72.0 * (double)drawAreaWidth / pageW;+      dpi2 = 72.0 * (double)(drawAreaHeight - continuousModePageSpacing) /+	     pageH;+      if (dpi2 < dpi1) {+	dpi1 = dpi2;+      }+    } else {+      // in single-page mode, sx=sy=0 -- so dpi1 is irrelevant+      dpi1 = dpi;+    }+    sx = 0;++  } else if (zoomA == zoomWidth) {+    if (continuousMode) {+      pageW = (rotate == 90 || rotate == 270) ? maxUnscaledPageH+	                                      : maxUnscaledPageW;+    } else {+      rot = rotate + doc->getPageRotate(topPage);+      if (rot >= 360) {+	rot -= 360;+      } else if (rot < 0) {+	rot += 360;+      }+      pageW = (rot == 90 || rot == 270) ? doc->getPageCropHeight(topPage)+	                                : doc->getPageCropWidth(topPage);+    }+    dpi1 = 72.0 * (double)drawAreaWidth / pageW;+    sx = 0;++  } else if (zoomA <= 0) {+    return;++  } else {+    dpi1 = 72.0 * zoomA / 100.0;+    if ((page = (PDFCorePage *)pages->get(0)) && page->xDest > 0) {+      hAdjust = page->xDest;+    } else {+      hAdjust = 0;+    }+    sx = (int)((scrollX - hAdjust + drawAreaWidth / 2) * (dpi1 / dpi)) -+         drawAreaWidth / 2;+    if (sx < 0) {+      sx = 0;+    }+  }++  if (continuousMode) {+    // we can't just multiply scrollY by dpi1/dpi -- the rounding+    // errors add up (because the pageY values are integers) -- so+    // we compute the pageY values at the new zoom level instead+    sy = 0;+    for (i = 1; i < topPage; ++i) {+      rot = rotate + doc->getPageRotate(i);+      if (rot >= 360) {+	rot -= 360;+      } else if (rot < 0) {+	rot += 360;+      }+      if (rot == 90 || rot == 270) {+	sy += (int)((doc->getPageCropWidth(i) * dpi1) / 72 + 0.5);+      } else {+	sy += (int)((doc->getPageCropHeight(i) * dpi1) / 72 + 0.5);+      }+    }+    vAdjust = (topPage - 1) * continuousModePageSpacing;+    sy = sy + (int)((scrollY - pageY[topPage - 1] + drawAreaHeight / 2)+		    * (dpi1 / dpi))+         + vAdjust - drawAreaHeight / 2;+  } else {+    sy = (int)((scrollY + drawAreaHeight / 2) * (dpi1 / dpi))+         - drawAreaHeight / 2;+  }++  update(topPage, sx, sy, zoomA, rotate, gFalse, gFalse);+}++// Zoom so that the current page(s) fill the window width.  Maintain+// the vertical center.+void PDFCore::zoomToCurrentWidth() {+  double w, maxW, dpi1;+  int sx, sy, vAdjust, rot, i;++  // compute the maximum page width of visible pages+  rot = rotate + doc->getPageRotate(topPage);+  if (rot >= 360) {+    rot -= 360;+  } else if (rot < 0) {+    rot += 360;+  }+  if (rot == 90 || rot == 270) {+    maxW = doc->getPageCropHeight(topPage);+  } else {+    maxW = doc->getPageCropWidth(topPage);+  }+  if (continuousMode) {+    for (i = topPage + 1;+	 i < doc->getNumPages() && pageY[i-1] < scrollY + drawAreaHeight;+	 ++i) {+      rot = rotate + doc->getPageRotate(i);+      if (rot >= 360) {+	rot -= 360;+      } else if (rot < 0) {+	rot += 360;+      }+      if (rot == 90 || rot == 270) {+	w = doc->getPageCropHeight(i);+      } else {+	w = doc->getPageCropWidth(i);+      }+      if (w > maxW) {+	maxW = w;+      }+    }+  }++  // compute the resolution+  dpi1 = (drawAreaWidth / maxW) * 72;++  // compute the horizontal scroll position+  if (continuousMode) {+    sx = ((int)(maxPageW * dpi1 / dpi) - drawAreaWidth) / 2;+  } else {+    sx = 0;+  }++  // compute the vertical scroll position+  if (continuousMode) {+    // we can't just multiply scrollY by dpi1/dpi -- the rounding+    // errors add up (because the pageY values are integers) -- so+    // we compute the pageY values at the new zoom level instead+    sy = 0;+    for (i = 1; i < topPage; ++i) {+      rot = rotate + doc->getPageRotate(i);+      if (rot >= 360) {+	rot -= 360;+      } else if (rot < 0) {+	rot += 360;+      }+      if (rot == 90 || rot == 270) {+	sy += (int)((doc->getPageCropWidth(i) * dpi1) / 72 + 0.5);+      } else {+	sy += (int)((doc->getPageCropHeight(i) * dpi1) / 72 + 0.5);+      }+    }+    vAdjust = (topPage - 1) * continuousModePageSpacing;+    sy = sy + (int)((scrollY - pageY[topPage - 1] + drawAreaHeight / 2)+		    * (dpi1 / dpi))+         + vAdjust - drawAreaHeight / 2;+  } else {+    sy = (int)((scrollY + drawAreaHeight / 2) * (dpi1 / dpi))+         - drawAreaHeight / 2;+  }++  update(topPage, sx, sy, (dpi1 * 100) / 72, rotate, gFalse, gFalse);+}++void PDFCore::setContinuousMode(GBool cm) {+  if (continuousMode != cm) {+    continuousMode = cm;+    update(topPage, scrollX, -1, zoom, rotate, gTrue, gFalse);+  }+}++void PDFCore::setSelection(int newSelectPage,+			   int newSelectULX, int newSelectULY,+			   int newSelectLRX, int newSelectLRY) {+  int x0, y0, x1, y1, py;+  GBool haveSel, newHaveSel;+  GBool needRedraw, needScroll;+  GBool moveLeft, moveRight, moveTop, moveBottom;+  SplashColor xorColor;+  PDFCorePage *page;+++  haveSel = selectULX != selectLRX && selectULY != selectLRY;+  newHaveSel = newSelectULX != newSelectLRX && newSelectULY != newSelectLRY;++  // erase old selection on off-screen bitmap+  needRedraw = gFalse;+  if (haveSel) {+    xorColor[0] = xorColor[1] = xorColor[2] = 0xff;+    xorRectangle(selectPage, selectULX, selectULY, selectLRX, selectLRY,+		 new SplashSolidColor(xorColor));+    needRedraw = gTrue;+  }++  // draw new selection on off-screen bitmap+  if (newHaveSel) {+    xorColor[0] = xorColor[1] = xorColor[2] = 0xff;+    xorRectangle(newSelectPage, newSelectULX, newSelectULY,+		 newSelectLRX, newSelectLRY,+		 new SplashSolidColor(xorColor));+    needRedraw = gTrue;+  }++  // check which edges moved+  if (!haveSel || newSelectPage != selectPage) {+    moveLeft = moveTop = moveRight = moveBottom = gTrue;+  } else {+    moveLeft = newSelectULX != selectULX;+    moveTop = newSelectULY != selectULY;+    moveRight = newSelectLRX != selectLRX;+    moveBottom = newSelectLRY != selectLRY;+  }++  // redraw currently visible part of bitmap+  if (needRedraw) {+    if (!haveSel) {+      page = findPage(newSelectPage);+      x0 = newSelectULX;+      y0 = newSelectULY;+      x1 = newSelectLRX;+      y1 = newSelectLRY;+      redrawWindow(page->xDest + x0, page->yDest + y0,+		   x1 - x0 + 1, y1 - y0 + 1, gFalse);+    } else if (!newHaveSel) {+      if ((page = findPage(selectPage))) {+	x0 = selectULX;+	y0 = selectULY;+	x1 = selectLRX;+	y1 = selectLRY;+	redrawWindow(page->xDest + x0, page->yDest + y0,+		     x1 - x0 + 1, y1 - y0 + 1, gFalse);+      }+    } else {+      page = findPage(newSelectPage);+      if (moveLeft) {+	x0 = newSelectULX < selectULX ? newSelectULX : selectULX;+	y0 = newSelectULY < selectULY ? newSelectULY : selectULY;+	x1 = newSelectULX > selectULX ? newSelectULX : selectULX;+	y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY;+	redrawWindow(page->xDest + x0, page->yDest + y0,+		     x1 - x0 + 1, y1 - y0 + 1, gFalse);+      }+      if (moveRight) {+	x0 = newSelectLRX < selectLRX ? newSelectLRX : selectLRX;+	y0 = newSelectULY < selectULY ? newSelectULY : selectULY;+	x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX;+	y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY;+	redrawWindow(page->xDest + x0, page->yDest + y0,+		     x1 - x0 + 1, y1 - y0 + 1, gFalse);+      }+      if (moveTop) {+	x0 = newSelectULX < selectULX ? newSelectULX : selectULX;+	y0 = newSelectULY < selectULY ? newSelectULY : selectULY;+	x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX;+	y1 = newSelectULY > selectULY ? newSelectULY : selectULY;+	redrawWindow(page->xDest + x0, page->yDest + y0,+		     x1 - x0 + 1, y1 - y0 + 1, gFalse);+      }+      if (moveBottom) {+	x0 = newSelectULX < selectULX ? newSelectULX : selectULX;+	y0 = newSelectLRY < selectLRY ? newSelectLRY : selectLRY;+	x1 = newSelectLRX > selectLRX ? newSelectLRX : selectLRX;+	y1 = newSelectLRY > selectLRY ? newSelectLRY : selectLRY;+	redrawWindow(page->xDest + x0, page->yDest + y0,+		     x1 - x0 + 1, y1 - y0 + 1, gFalse);+      }+    }+  }++  // switch to new selection coords+  selectPage = newSelectPage;+  selectULX = newSelectULX;+  selectULY = newSelectULY;+  selectLRX = newSelectLRX;+  selectLRY = newSelectLRY;++  // scroll if necessary+  if (newHaveSel) {+    page = findPage(selectPage);+    needScroll = gFalse;+    x0 = scrollX;+    y0 = scrollY;+    if (moveLeft && page->xDest + selectULX < 0) {+      x0 += page->xDest + selectULX;+      needScroll = gTrue;+    } else if (moveRight && page->xDest + selectLRX >= drawAreaWidth) {+      x0 += page->xDest + selectLRX - drawAreaWidth;+      needScroll = gTrue;+    } else if (moveLeft && page->xDest + selectULX >= drawAreaWidth) {+      x0 += page->xDest + selectULX - drawAreaWidth;+      needScroll = gTrue;+    } else if (moveRight && page->xDest + selectLRX < 0) {+      x0 += page->xDest + selectLRX;+      needScroll = gTrue;+    }+    py = continuousMode ? pageY[selectPage - 1] : 0;+    if (moveTop && py + selectULY < y0) {+      y0 = py + selectULY;+      needScroll = gTrue;+    } else if (moveBottom && py + selectLRY >= y0 + drawAreaHeight) {+      y0 = py + selectLRY - drawAreaHeight;+      needScroll = gTrue;+    } else if (moveTop && py + selectULY >= y0 + drawAreaHeight) {+      y0 = py + selectULY - drawAreaHeight;+      needScroll = gTrue;+    } else if (moveBottom && py + selectLRY < y0) {+      y0 = py + selectLRY;+      needScroll = gTrue;+    }+    if (needScroll) {+      scrollTo(x0, y0);+    }+  }+}++void PDFCore::moveSelection(int pg, int x, int y) {+  int newSelectULX, newSelectULY, newSelectLRX, newSelectLRY;++  // don't allow selections to span multiple pages+  if (pg != selectPage) {+    return;+  }++  // move appropriate edges of selection+  if (lastDragLeft) {+    if (x < selectLRX) {+      newSelectULX = x;+      newSelectLRX = selectLRX;+    } else {+      newSelectULX = selectLRX;+      newSelectLRX = x;+      lastDragLeft = gFalse;+    }+  } else {+    if (x > selectULX) {+      newSelectULX = selectULX;+      newSelectLRX = x;+    } else {+      newSelectULX = x;+      newSelectLRX = selectULX;+      lastDragLeft = gTrue;+    }+  }+  if (lastDragTop) {+    if (y < selectLRY) {+      newSelectULY = y;+      newSelectLRY = selectLRY;+    } else {+      newSelectULY = selectLRY;+      newSelectLRY = y;+      lastDragTop = gFalse;+    }+  } else {+    if (y > selectULY) {+      newSelectULY = selectULY;+      newSelectLRY = y;+    } else {+      newSelectULY = y;+      newSelectLRY = selectULY;+      lastDragTop = gTrue;+    }+  }++  // redraw the selection+  setSelection(selectPage, newSelectULX, newSelectULY,+	       newSelectLRX, newSelectLRY);+}++void PDFCore::xorRectangle(int pg, int x0, int y0, int x1, int y1,+			   SplashPattern *pattern, PDFCoreTile *oneTile) {+  Splash *splash;+  SplashPath *path;+  PDFCorePage *page;+  PDFCoreTile *tile;+  SplashCoord xx0, yy0, xx1, yy1;+  int xi, yi, wi, hi;+  int i;++  if ((page = findPage(pg))) {+    for (i = 0; i < page->tiles->getLength(); ++i) {+      tile = (PDFCoreTile *)page->tiles->get(i);+      if (!oneTile || tile == oneTile) {+	splash = new Splash(tile->bitmap, gFalse);+	splash->setFillPattern(pattern->copy());+	xx0 = (SplashCoord)(x0 - tile->xMin);+	yy0 = (SplashCoord)(y0 - tile->yMin);+	xx1 = (SplashCoord)(x1 - tile->xMin);+	yy1 = (SplashCoord)(y1 - tile->yMin);+	path = new SplashPath();+	path->moveTo(xx0, yy0);+	path->lineTo(xx1, yy0);+	path->lineTo(xx1, yy1);+	path->lineTo(xx0, yy1);+	path->close();+	splash->xorFill(path, gTrue);+	delete path;+	delete splash;+	xi = x0 - tile->xMin;+	wi = x1 - x0;+	if (xi < 0) {+	  wi += xi;+	  xi = 0;+	}+	if (xi + wi > tile->bitmap->getWidth()) {+	  wi = tile->bitmap->getWidth() - xi;+	}+	yi = y0 - tile->yMin;+	hi = y1 - y0;+	if (yi < 0) {+	  hi += yi;+	  yi = 0;+	}+	if (yi + hi > tile->bitmap->getHeight()) {+	  hi = tile->bitmap->getHeight() - yi;+	}+	updateTileData(tile, xi, yi, wi, hi, gTrue);+      }+    }+  }+  delete pattern;+}++GBool PDFCore::getSelection(int *pg, double *ulx, double *uly,+			    double *lrx, double *lry) {+  if (selectULX == selectLRX || selectULY == selectLRY) {+    return gFalse;+  }+  *pg = selectPage;+  cvtDevToUser(selectPage, selectULX, selectULY, ulx, uly);+  cvtDevToUser(selectPage, selectLRX, selectLRY, lrx, lry);+  return gTrue;+}++GString *PDFCore::extractText(int pg, double xMin, double yMin,+			      double xMax, double yMax) {+  PDFCorePage *page;+  TextOutputDev *textOut;+  int x0, y0, x1, y1, t;+  GString *s;++  if (!doc->okToCopy()) {+    return NULL;+  }+  if ((page = findPage(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;+    }+    s = page->text->getText(x0, y0, x1, y1);+  } else {+    textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);+    if (textOut->isOk()) {+      doc->displayPage(textOut, pg, dpi, dpi, rotate, gFalse, gTrue, gFalse);+      textOut->cvtUserToDev(xMin, yMin, &x0, &y0);+      textOut->cvtUserToDev(xMax, yMax, &x1, &y1);+      if (x0 > x1) {+	t = x0; x0 = x1; x1 = t;+      }+      if (y0 > y1) {+	t = y0; y0 = y1; y1 = t;+      }+      s = textOut->getText(x0, y0, x1, y1);+    } else {+      s = new GString();+    }+    delete textOut;+  }+  return s;+}++GBool PDFCore::find(char *s, GBool caseSensitive, GBool next, GBool backward,+		    GBool onePageOnly) {+  Unicode *u;+  int len, i;+  GBool ret;++  // convert to Unicode+  len = 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, onePageOnly);++  gfree(u);+  return ret;+}++GBool PDFCore::findU(Unicode *u, int len, GBool caseSensitive,+		     GBool next, GBool backward, GBool onePageOnly) {+  TextOutputDev *textOut;+  double xMin, yMin, xMax, yMax;+  PDFCorePage *page;+  PDFCoreTile *tile;+  int pg;+  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;+  xMin = yMin = xMax = yMax = 0;+  pg = topPage;+  if (next) {+    startAtLast = gTrue;+  } else if (selectULX != selectLRX && selectULY != selectLRY) {+    pg = selectPage;+    if (backward) {+      xMin = selectULX - 1;+      yMin = selectULY - 1;+    } else {+      xMin = selectULX + 1;+      yMin = selectULY + 1;+    }+  } else {+    startAtTop = gTrue;+  }+  if (!(page = findPage(pg))) {+    displayPage(pg, zoom, rotate, gTrue, gFalse);+    page = findPage(pg);+  }+  if (page->text->findText(u, len, startAtTop, gTrue, startAtLast, gFalse,+			   caseSensitive, backward,+			   &xMin, &yMin, &xMax, &yMax)) {+    goto found;+  }++  if (!onePageOnly) {++    // search following/previous pages+    textOut = new TextOutputDev(NULL, gTrue, gFalse, 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,+			    &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,+			    &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;+      xMax = selectLRX;+      yMax = selectLRY;+    }+    if (page->text->findText(u, len, gTrue, gFalse, gFalse, stopAtLast,+			     caseSensitive, backward,+			     &xMin, &yMin, &xMax, &yMax)) {+      goto found;+    }+  }++  // not found+ notFound:+  setBusyCursor(gFalse);+  return gFalse;++  // found on a different page+ foundPage:+  update(pg, scrollX, continuousMode ? -1 : 0, zoom, rotate, gFalse, gTrue);+  page = findPage(pg);+  if (!page->text->findText(u, len, gTrue, gTrue, gFalse, gFalse,+			    caseSensitive, backward,+			    &xMin, &yMin, &xMax, &yMax)) {+    // this can happen if coalescing is bad+    goto notFound;+  }++  // found: change the selection+ found:+  tile = (PDFCoreTile *)page->tiles->get(0);+  setSelection(pg, (int)floor(xMin), (int)floor(yMin),+	       (int)ceil(xMax), (int)ceil(yMax));++  setBusyCursor(gFalse);+  return gTrue;+}+++GBool PDFCore::cvtWindowToUser(int xw, int yw,+			       int *pg, double *xu, double *yu) {+  PDFCorePage *page;+  PDFCoreTile *tile;+  int i;++  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    if (xw >= page->xDest && xw < page->xDest + page->w &&+	yw >= page->yDest && yw < page->yDest + page->h) {+      tile = (PDFCoreTile *)page->tiles->get(0);+      *pg = page->page;+      xw -= tile->xDest;+      yw -= tile->yDest;+      *xu = tile->ictm[0] * xw + tile->ictm[2] * yw + tile->ictm[4];+      *yu = tile->ictm[1] * xw + tile->ictm[3] * yw + tile->ictm[5];+      return gTrue;+    }+  }+  *pg = 0;+  *xu = *yu = 0;+  return gFalse;+}++GBool PDFCore::cvtWindowToDev(int xw, int yw, int *pg, int *xd, int *yd) {+  PDFCorePage *page;+  int i;++  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    if (xw >= page->xDest && xw < page->xDest + page->w &&+	yw >= page->yDest && yw < page->yDest + page->h) {+      *pg = page->page;+      *xd = xw - page->xDest;+      *yd = yw - page->yDest;+      return gTrue;+    }+  }+  *pg = 0;+  *xd = *yd = 0;+  return gFalse;+}++void PDFCore::cvtUserToWindow(int pg, double xu, double yu, int *xw, int *yw) {+  PDFCorePage *page;+  PDFCoreTile *tile;++  if ((page = findPage(pg)) &&+      page->tiles->getLength() > 0) {+    tile = (PDFCoreTile *)page->tiles->get(0);+  } else if (curTile && curPage->page == pg) {+    tile = curTile;+  } else {+    tile = NULL;+  }+  if (tile) {+    *xw = tile->xDest + (int)(tile->ctm[0] * xu + tile->ctm[2] * yu ++			      tile->ctm[4] + 0.5);+    *yw = tile->yDest + (int)(tile->ctm[1] * xu + tile->ctm[3] * yu ++			      tile->ctm[5] + 0.5);+  } else {+    // this should never happen+    *xw = *yw = 0;+  }+}++void PDFCore::cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd) {+  PDFCorePage *page;+  PDFCoreTile *tile;+  double ctm[6];++  if ((page = findPage(pg)) &&+      page->tiles->getLength() > 0) {+    tile = (PDFCoreTile *)page->tiles->get(0);+  } else if (curTile && curPage->page == pg) {+    tile = curTile;+  } else {+    tile = NULL;+  }+  if (tile) {+    *xd = (int)(tile->xMin + tile->ctm[0] * xu ++		tile->ctm[2] * yu + tile->ctm[4] + 0.5);+    *yd = (int)(tile->yMin + tile->ctm[1] * xu ++		tile->ctm[3] * yu + tile->ctm[5] + 0.5);+  } else {+    doc->getCatalog()->getPage(pg)->getDefaultCTM(ctm, dpi, dpi, rotate,+						  gFalse, out->upsideDown());+    *xd = (int)(ctm[0] * xu + ctm[2] * yu + ctm[4] + 0.5);+    *yd = (int)(ctm[1] * xu + ctm[3] * yu + ctm[5] + 0.5);+  }+}++void PDFCore::cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw) {+  PDFCorePage *page;++  if ((page = findPage(pg))) {+    *xw = page->xDest + xd;+    *yw = page->yDest + yd;+  } else {+    // this should never happen+    *xw = *yw = 0;+  }+}++void PDFCore::cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu) {+  PDFCorePage *page;+  PDFCoreTile *tile;++  if ((page = findPage(pg)) &&+      page->tiles->getLength() > 0) {+    tile = (PDFCoreTile *)page->tiles->get(0);+  } else if (curTile && curPage->page == pg) {+    tile = curTile;+  } else {+    tile = NULL;+  }+  if (tile) {+    xd -= tile->xMin;+    yd -= tile->yMin;+    *xu = tile->ictm[0] * xd + tile->ictm[2] * yd + tile->ictm[4];+    *yu = tile->ictm[1] * xd + tile->ictm[3] * yd + tile->ictm[5];+  } else {+    // this should never happen+    *xu = *yu = 0;+  }+}++void PDFCore::setReverseVideo(GBool reverseVideoA) {+  out->setReverseVideo(reverseVideoA);+  update(topPage, scrollX, scrollY, zoom, rotate, gTrue, gFalse);+}++LinkAction *PDFCore::findLink(int pg, double x, double y) {+  PDFCorePage *page;++  if ((page = findPage(pg))) {+    return page->links ? page->links->find(x, y) : (LinkAction *)NULL;+  }+  return NULL;+}++PDFCorePage *PDFCore::findPage(int pg) {+  PDFCorePage *page;+  int i;++  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    if (page->page == pg) {+      return page;+    }+  }+  return NULL;+}++void PDFCore::redrawCbk(void *data, int x0, int y0, int x1, int y1,+			GBool composited) {+  PDFCore *core = (PDFCore *)data;++  core->curTile->bitmap = core->out->getBitmap();++  // the default CTM is set by the Gfx constructor; tile->ctm is+  // needed by the coordinate conversion functions (which may be+  // called during redraw)+  memcpy(core->curTile->ctm, core->out->getDefCTM(), 6 * sizeof(double));+  memcpy(core->curTile->ictm, core->out->getDefICTM(), 6 * sizeof(double));++  // the bitmap created by Gfx and SplashOutputDev can be a slightly+  // different size due to rounding errors+  if (x1 >= core->curTile->xMax) {+    x1 = core->curTile->xMax - 1;+  }+  if (y1 >= core->curTile->yMax) {+    y1 = core->curTile->yMax - 1;+  }++  core->clippedRedrawRect(core->curTile, x0, y0,+			  core->curTile->xDest + x0, core->curTile->yDest + y0,+			  x1 - x0 + 1, y1 - y0 + 1,+			  0, 0, core->drawAreaWidth, core->drawAreaHeight,+			  gTrue, composited);+}++void PDFCore::redrawWindow(int x, int y, int width, int height,+			   GBool needUpdate) {+  PDFCorePage *page;+  PDFCoreTile *tile;+  int xDest, yDest, w, i, j;++  if (pages->getLength() == 0) {+    redrawRect(NULL, 0, 0, x, y, width, height, gTrue);+    return;+  }++  for (i = 0; i < pages->getLength(); ++i) {+    page = (PDFCorePage *)pages->get(i);+    for (j = 0; j < page->tiles->getLength(); ++j) {+      tile = (PDFCoreTile *)page->tiles->get(j);+      if (tile->edges & pdfCoreTileTopEdge) {+	if (tile->edges & pdfCoreTileLeftEdge) {+	  xDest = 0;+	} else {+	  xDest = tile->xDest;+	}+	if (tile->edges & pdfCoreTileRightEdge) {+	  w = drawAreaWidth - xDest;+	} else {+	  w = tile->xDest + (tile->xMax - tile->xMin) - xDest;+	}+	clippedRedrawRect(NULL, 0, 0,+			  xDest, 0, w, tile->yDest,+			  x, y, width, height, gFalse);+      }+      if (tile->edges & pdfCoreTileBottomEdge) {+	if (tile->edges & pdfCoreTileLeftEdge) {+	  xDest = 0;+	} else {+	  xDest = tile->xDest;+	}+	if (tile->edges & pdfCoreTileRightEdge) {+	  w = drawAreaWidth - xDest;+	} else {+	  w = tile->xDest + (tile->xMax - tile->xMin) - xDest;+	}+	yDest = tile->yDest + (tile->yMax - tile->yMin);+	clippedRedrawRect(NULL, 0, 0,+			  xDest, yDest, w, drawAreaHeight - yDest,+			  x, y, width, height, gFalse);+      } else if ((tile->edges & pdfCoreTileBottomSpace) &&+		 i+1 < pages->getLength()) {+	if (tile->edges & pdfCoreTileLeftEdge) {+	  xDest = 0;+	} else {+	  xDest = tile->xDest;+	}+	if (tile->edges & pdfCoreTileRightEdge) {+	  w = drawAreaWidth - xDest;+	} else {+	  w = tile->xDest + (tile->xMax - tile->xMin) - xDest;+	}+	yDest = tile->yDest + (tile->yMax - tile->yMin);+	clippedRedrawRect(NULL, 0, 0,+			  xDest, yDest,+			  w, ((PDFCorePage *)pages->get(i+1))->yDest - yDest,+			  x, y, width, height, gFalse);+      }+      if (tile->edges & pdfCoreTileLeftEdge) {+	clippedRedrawRect(NULL, 0, 0,+			  0, tile->yDest,+			  tile->xDest, tile->yMax - tile->yMin,+			  x, y, width, height, gFalse);+      }+      if (tile->edges & pdfCoreTileRightEdge) {+	xDest = tile->xDest + (tile->xMax - tile->xMin);+	clippedRedrawRect(NULL, 0, 0,+			  xDest, tile->yDest,+			  drawAreaWidth - xDest, tile->yMax - tile->yMin,+			  x, y, width, height, gFalse);+      }+      clippedRedrawRect(tile, 0, 0, tile->xDest, tile->yDest,+			tile->bitmap->getWidth(), tile->bitmap->getHeight(),+			x, y, width, height, needUpdate);+    }+  }+}++PDFCoreTile *PDFCore::newTile(int xDestA, int yDestA) {+  return new PDFCoreTile(xDestA, yDestA);+}++void PDFCore::updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,+			     int width, int height, GBool composited) {+}++void PDFCore::clippedRedrawRect(PDFCoreTile *tile, int xSrc, int ySrc,+				int xDest, int yDest, int width, int height,+				int xClip, int yClip, int wClip, int hClip,+				GBool needUpdate, GBool composited) {+  if (tile && needUpdate) {+    updateTileData(tile, xSrc, ySrc, width, height, composited);+  }+  if (xDest < xClip) {+    xSrc += xClip - xDest;+    width -= xClip - xDest;+    xDest = xClip;+  }+  if (xDest + width > xClip + wClip) {+    width = xClip + wClip - xDest;+  }+  if (yDest < yClip) {+    ySrc += yClip - yDest;+    height -= yClip - yDest;+    yDest = yClip;+  }+  if (yDest + height > yClip + hClip) {+    height = yClip + hClip - yDest;+  }+  if (width > 0 && height > 0) {+    redrawRect(tile, xSrc, ySrc, xDest, yDest, width, height, composited);+  }+}
+ xpdf/PDFCore.h view
@@ -0,0 +1,321 @@+//========================================================================+//+// PDFCore.h+//+// Copyright 2004 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"++class GString;+class GList;+class SplashBitmap;+class SplashPattern;+class BaseStream;+class PDFDoc;+class Links;+class LinkDest;+class LinkAction;+class TextPage;+class HighlightFile;+class CoreOutputDev;+class PDFCore;++//------------------------------------------------------------------------+// zoom factor+//------------------------------------------------------------------------++#define zoomPage  -1+#define zoomWidth -2+#define defZoom   125++//------------------------------------------------------------------------+++//------------------------------------------------------------------------++// Number of pixels of matte color between pages in continuous mode.+#define continuousModePageSpacing 3++//------------------------------------------------------------------------+// PDFCorePage+//------------------------------------------------------------------------++class PDFCorePage {+public:++  PDFCorePage(int pageA, int wA, int hA, int tileWA, int tileHA);+  ~PDFCorePage();++  int page;+  GList *tiles;			// cached tiles [PDFCoreTile]+  int xDest, yDest;		// position of upper-left corner+				//   in the drawing area+  int w, h;			// size of whole page bitmap+  int tileW, tileH;		// size of tiles+  Links *links;			// hyperlinks for this page+  TextPage *text;		// extracted text+};++//------------------------------------------------------------------------+// PDFCoreTile+//------------------------------------------------------------------------++class PDFCoreTile {+public:++  PDFCoreTile(int xDestA, int yDestA);+  virtual ~PDFCoreTile();++  int xMin, yMin, xMax, yMax;+  int xDest, yDest;+  Guint edges;+  SplashBitmap *bitmap;+  double ctm[6];		// coordinate transform matrix:+				//   default user space -> device space+  double ictm[6];		// inverse CTM+};++#define pdfCoreTileTopEdge      0x01+#define pdfCoreTileBottomEdge   0x02+#define pdfCoreTileLeftEdge     0x04+#define pdfCoreTileRightEdge    0x08+#define pdfCoreTileTopSpace     0x10+#define pdfCoreTileBottomSpace  0x20++//------------------------------------------------------------------------+// PDFHistory+//------------------------------------------------------------------------++struct PDFHistory {+  GString *fileName;+  int page;+};++#define pdfHistorySize 50+++//------------------------------------------------------------------------+// PDFCore+//------------------------------------------------------------------------++class PDFCore {+public:++  PDFCore(SplashColorMode colorModeA, int bitmapRowPadA,+	  GBool reverseVideoA, SplashColorPtr paperColorA,+	  GBool incrementalUpdate);+  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);++  // 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; otherwise, no+  // scrolling is done.  If <addToHist> is set, this page change is+  // added to the history list.+  virtual void displayPage(int topPageA, double zoomA, int rotateA,+			   GBool scrollToTop, GBool addToHist);++  // Display a link destination.+  virtual void displayDest(LinkDest *dest, double zoomA, int rotateA,+			   GBool addToHist);++  // Update the display, given the specified parameters.+  virtual void update(int topPageA, int scrollXA, int scrollYA,+		      double zoomA, int rotateA, GBool force, GBool addToHist);++  //----- 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);+  virtual void scrollUpPrevPage(int nLines = 16);+  virtual void scrollDown(int nLines = 16);+  virtual void scrollDownNextPage(int nLines = 16);+  virtual void scrollPageUp();+  virtual void scrollPageDown();+  virtual void scrollTo(int x, int y);+  virtual void scrollToLeftEdge();+  virtual void scrollToRightEdge();+  virtual void scrollToTopEdge();+  virtual void scrollToBottomEdge();+  virtual void scrollToTopLeft();+  virtual void scrollToBottomRight();+  virtual void zoomToRect(int pg, double ulx, double uly,+			  double lrx, double lry);+  virtual void zoomCentered(double zoomA);+  virtual void zoomToCurrentWidth();+  virtual void setContinuousMode(GBool cm);++  //----- selection++  // Current selected region.+  void setSelection(int newSelectPage,+		    int newSelectULX, int newSelectULY,+		    int newSelectLRX, int newSelectLRY);+  void moveSelection(int pg, int x, int y);+  GBool getSelection(int *pg, double *ulx, double *uly,+		     double *lrx, double *lry);++  // Text extraction.+  GString *extractText(int pg, double xMin, double yMin,+		       double xMax, double yMax);++  //----- find++  virtual GBool find(char *s, GBool caseSensitive, GBool next, GBool backward,+		     GBool onePageOnly);+  virtual GBool findU(Unicode *u, int len, GBool caseSensitive,+		      GBool next, GBool backward, GBool onePageOnly);+++  //----- coordinate conversion++  // user space: per-pace, 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);+  void cvtUserToWindow(int pg, double xy, double yu, int *xw, int *yw);+  void cvtUserToDev(int pg, double xu, double yu, int *xd, int *yd);+  void cvtDevToWindow(int pg, int xd, int yd, int *xw, int *yw);+  void cvtDevToUser(int pg, int xd, int yd, double *xu, double *yu);++  //----- misc access++  PDFDoc *getDoc() { return doc; }+  int getPageNum() { return topPage; }+  double getZoom() { return zoom; }+  double getZoomDPI() { return dpi; }+  int getRotate() { return rotate; }+  GBool getContinuousMode() { return continuousMode; }+  virtual void setReverseVideo(GBool reverseVideoA);+  GBool canGoBack() { return historyBLen > 1; }+  GBool canGoForward() { return historyFLen > 0; }+  int getScrollX() { return scrollX; }+  int getScrollY() { return scrollY; }+  int getDrawAreaWidth() { return drawAreaWidth; }+  int getDrawAreaHeight() { return drawAreaHeight; }+  virtual void setBusyCursor(GBool busy) = 0;+  LinkAction *findLink(int pg, double x, double y);++protected:++  int loadFile2(PDFDoc *newDoc);+  void addPage(int pg, int rot);+  void needTile(PDFCorePage *page, int x, int y);+  void xorRectangle(int pg, int x0, int y0, int x1, int y1,+		    SplashPattern *pattern, PDFCoreTile *oneTile = NULL);+  int loadHighlightFile(HighlightFile *hf, SplashColorPtr color,+			SplashColorPtr selectColor, GBool selectable);+  PDFCorePage *findPage(int pg);+  static void redrawCbk(void *data, int x0, int y0, int x1, int y1,+			GBool composited);+  void redrawWindow(int x, int y, int width, int height,+		    GBool needUpdate);+  virtual PDFCoreTile *newTile(int xDestA, int yDestA);+  virtual void updateTileData(PDFCoreTile *tileA, int xSrc, int ySrc,+			      int width, int height, GBool composited);+  virtual void redrawRect(PDFCoreTile *tileA, int xSrc, int ySrc,+			  int xDest, int yDest, int width, int height,+			  GBool composited) = 0;+  void clippedRedrawRect(PDFCoreTile *tile, int xSrc, int ySrc,+			 int xDest, int yDest, int width, int height,+			 int xClip, int yClip, int wClip, int hClip,+			 GBool needUpdate, GBool composited = gTrue);+  virtual void updateScrollbars() = 0;+  virtual GBool checkForNewFile() { return gFalse; }++  PDFDoc *doc;			// current PDF file+  GBool continuousMode;		// false for single-page mode, true for+				//   continuous mode+  int drawAreaWidth,		// size of the PDF display area+      drawAreaHeight;+  double maxUnscaledPageW,	// maximum unscaled page size+         maxUnscaledPageH;+  int maxPageW;			// maximum page width (only used in+				//   continuous mode)+  int totalDocH;		// total document height (only used in+				//   continuous mode)+  int *pageY;			// top coordinates for each page (only used+				//   in continuous mode)+  int topPage;			// page at top of window+  int scrollX, scrollY;		// offset from top left corner of topPage+				//   to top left corner of window+  double zoom;			// current zoom level, in percent of 72 dpi+  double dpi;			// current zoom level, in DPI+  int rotate;			// current page rotation++  int selectPage;		// page number of current selection+  int selectULX,		// coordinates of current selection,+      selectULY,		//   in device space -- (ULX==LRX || ULY==LRY)+      selectLRX,		//   means there is no selection+      selectLRY;+  GBool dragging;		// set while selection is being dragged+  GBool lastDragLeft;		// last dragged selection edge was left/right+  GBool lastDragTop;		// last dragged selection edge was top/bottom++  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+++  GList *pages;			// cached pages [PDFCorePage]+  PDFCoreTile *curTile;		// tile currently being rasterized+  PDFCorePage *curPage;		// page to which curTile belongs++  SplashColor paperColor;+  CoreOutputDev *out;++  friend class PDFCoreTile;+};++#endif
+ xpdf/PDFDoc.cc view
@@ -0,0 +1,402 @@+//========================================================================+//+// 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 "GString.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"+#ifndef DISABLE_OUTLINE+#include "Outline.h"+#endif+#include "PDFDoc.h"++//------------------------------------------------------------------------++#define headerSearchSize 1024	// read this many bytes at beginning of+				//   file to look for '%PDF'++//------------------------------------------------------------------------+// PDFDoc+//------------------------------------------------------------------------++PDFDoc::PDFDoc(GString *fileNameA, GString *ownerPassword,+	       GString *userPassword, void *guiDataA) {+  Object obj;+  GString *fileName1, *fileName2;++  ok = gFalse;+  errCode = errNone;++  guiData = guiDataA;++  file = NULL;+  str = NULL;+  xref = NULL;+  catalog = NULL;+#ifndef DISABLE_OUTLINE+  outline = NULL;+#endif++  fileName = fileNameA;+  fileName1 = fileName;+++  // try to open file+  fileName2 = NULL;+#ifdef VMS+  if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) {+    error(-1, "Couldn't open file '%s'", fileName1->getCString());+    errCode = errOpenFile;+    return;+  }+#else+  if (!(file = fopen(fileName1->getCString(), "rb"))) {+    fileName2 = fileName->copy();+    fileName2->lowerCase();+    if (!(file = fopen(fileName2->getCString(), "rb"))) {+      fileName2->upperCase();+      if (!(file = fopen(fileName2->getCString(), "rb"))) {+	error(-1, "Couldn't open file '%s'", fileName->getCString());+	delete fileName2;+	errCode = errOpenFile;+	return;+      }+    }+    delete fileName2;+  }+#endif++  // create stream+  obj.initNull();+  str = new FileStream(file, 0, gFalse, 0, &obj);++  ok = setup(ownerPassword, userPassword);+}++#ifdef WIN32+PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword,+	       GString *userPassword, void *guiDataA) {+  OSVERSIONINFO version;+  wchar_t fileName2[_MAX_PATH + 1];+  Object obj;+  int i;++  ok = gFalse;+  errCode = errNone;++  guiData = guiDataA;++  file = NULL;+  str = NULL;+  xref = NULL;+  catalog = NULL;+#ifndef DISABLE_OUTLINE+  outline = NULL;+#endif++  //~ file name should be stored in Unicode (?)+  fileName = new GString();+  for (i = 0; i < fileNameLen; ++i) {+    fileName->append((char)fileNameA[i]);+  }++  // zero-terminate the file name string+  for (i = 0; i < fileNameLen && i < _MAX_PATH; ++i) {+    fileName2[i] = fileNameA[i];+  }+  fileName2[i] = 0;++  // 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(fileName2, L"rb");+  } else {+    file = fopen(fileName->getCString(), "rb");+  }+  if (!file) {+    error(-1, "Couldn't open file '%s'", fileName->getCString());+    errCode = errOpenFile;+    return;+  }++  // create stream+  obj.initNull();+  str = new FileStream(file, 0, gFalse, 0, &obj);++  ok = setup(ownerPassword, userPassword);+}+#endif++PDFDoc::PDFDoc(BaseStream *strA, GString *ownerPassword,+	       GString *userPassword, void *guiDataA) {+  ok = gFalse;+  errCode = errNone;+  guiData = guiDataA;+  if (strA->getFileName()) {+    fileName = strA->getFileName()->copy();+  } else {+    fileName = NULL;+  }+  file = NULL;+  str = strA;+  xref = NULL;+  catalog = NULL;+#ifndef DISABLE_OUTLINE+  outline = NULL;+#endif+  ok = setup(ownerPassword, userPassword);+}++GBool PDFDoc::setup(GString *ownerPassword, GString *userPassword) {+  str->reset();++  // check header+  checkHeader();++  // read xref table+  xref = new XRef(str);+  if (!xref->isOk()) {+    error(-1, "Couldn't read xref table");+    errCode = xref->getErrorCode();+    return gFalse;+  }++  // check for encryption+  if (!checkEncryption(ownerPassword, userPassword)) {+    errCode = errEncrypted;+    return gFalse;+  }++  // read catalog+  catalog = new Catalog(xref);+  if (!catalog->isOk()) {+    error(-1, "Couldn't read page catalog");+    errCode = errBadCatalog;+    return gFalse;+  }++#ifndef DISABLE_OUTLINE+  // read outline+  outline = new Outline(catalog->getOutline(), xref);+#endif++  // done+  return gTrue;+}++PDFDoc::~PDFDoc() {+#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;+  }+}++// 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;+  for (i = 0; i < headerSearchSize; ++i) {+    hdrBuf[i] = str->getChar();+  }+  hdrBuf[headerSearchSize] = '\0';+  for (i = 0; i < headerSearchSize - 5; ++i) {+    if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {+      break;+    }+  }+  if (i >= headerSearchSize - 5) {+    error(-1, "May not be a PDF file (continuing anyway)");+    return;+  }+  str->moveStart(i);+  if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {+    error(-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(-1, "PDF version %s -- xpdf supports version %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->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, catalog,+				  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);+  }+}++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, catalog,+				       abortCheckCbk, abortCheckCbkData);+}++Links *PDFDoc::getLinks(int page) {+  return catalog->getPage(page)->getLinks(catalog);+}++void PDFDoc::processLinks(OutputDev *out, int page) {+  catalog->getPage(page)->processLinks(out, catalog);+}++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;+  int c;++  if (!(f = fopen(name->getCString(), "wb"))) {+    error(-1, "Couldn't open file '%s'", name->getCString());+    return gFalse;+  }+  str->reset();+  while ((c = str->getChar()) != EOF) {+    fputc(c, f);+  }+  str->close();+  fclose(f);+  return gTrue;+}
+ xpdf/PDFDoc.h view
@@ -0,0 +1,183 @@+//========================================================================+//+// 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;++//------------------------------------------------------------------------+// PDFDoc+//------------------------------------------------------------------------++class PDFDoc {+public:++  PDFDoc(GString *fileNameA, GString *ownerPassword = NULL,+	 GString *userPassword = NULL, void *guiDataA = NULL);+#ifdef WIN32+  PDFDoc(wchar_t *fileNameA, int fileNameLen, GString *ownerPassword = NULL,+	 GString *userPassword = NULL, void *guiDataA = NULL);+#endif+  PDFDoc(BaseStream *strA, GString *ownerPassword = NULL,+	 GString *userPassword = NULL, void *guiDataA = 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; }++  // 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; }+#endif++  // 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 GUI (XPDFCore or WinPDFCore object).+  void *getGUIData() { return guiData; }+++private:++  GBool setup(GString *ownerPassword, GString *userPassword);+  void checkHeader();+  GBool checkEncryption(GString *ownerPassword, GString *userPassword);++  GString *fileName;+  FILE *file;+  BaseStream *str;+  void *guiData;+  double pdfVersion;+  XRef *xref;+  Catalog *catalog;+#ifndef DISABLE_OUTLINE+  Outline *outline;+#endif+++  GBool ok;+  int errCode;+};++#endif
+ xpdf/PDFDocEncoding.cc view
@@ -0,0 +1,44 @@+//========================================================================+//+// PDFDocEncoding.h+//+// Copyright 2002-2003 Glyph & Cog, LLC+//+//========================================================================++#include "PDFDocEncoding.h"++Unicode pdfDocEncoding[256] = {+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 00+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,+  0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // 10+  0x02d8, 0x02c7, 0x02c6, 0x02d9, 0x02dd, 0x02db, 0x02da, 0x02dc,+  0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, // 20+  0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,+  0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, // 30+  0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f,+  0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, // 40+  0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,+  0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, // 50+  0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f,+  0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, // 60+  0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,+  0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, // 70+  0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x0000,+  0x2022, 0x2020, 0x2021, 0x2026, 0x2014, 0x2013, 0x0192, 0x2044, // 80+  0x2039, 0x203a, 0x2212, 0x2030, 0x201e, 0x201c, 0x201d, 0x2018,+  0x2019, 0x201a, 0x2122, 0xfb01, 0xfb02, 0x0141, 0x0152, 0x0160, // 90+  0x0178, 0x017d, 0x0131, 0x0142, 0x0153, 0x0161, 0x017e, 0x0000,+  0x20ac, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, // a0+  0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x0000, 0x00ae, 0x00af,+  0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, // b0+  0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf,+  0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, // c0+  0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf,+  0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, // d0+  0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df,+  0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, // e0+  0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef,+  0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, // f0+  0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff+};
+ xpdf/PDFDocEncoding.h view
@@ -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
+ xpdf/PSOutputDev.cc view
@@ -0,0 +1,6222 @@+//========================================================================+//+// PSOutputDev.cc+//+// Copyright 1996-2003 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 "GString.h"+#include "GList.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 "XRef.h"+#include "PreScanOutputDev.h"+#if HAVE_SPLASH+#  include "Splash.h"+#  include "SplashBitmap.h"+#  include "SplashOutputDev.h"+#endif+#include "PSOutputDev.h"++#ifdef MACOS+// needed for setting type/creator of MacOS files+#include "ICSupport.h"+#endif++// the MSVC math.h doesn't define this+#ifndef M_PI+#define M_PI 3.14159265358979323846+#endif++//------------------------------------------------------------------------++// Resolution at which pages with transparency will be rasterized.+#define splashDPI 300++//------------------------------------------------------------------------+// PostScript prolog and setup+//------------------------------------------------------------------------++// The '~' escapes mark prolog code that is emitted only in certain+// levels:+//+//   ~[123][sn]+//      ^   ^----- s=psLevel*Sep, n=psLevel*+//      +----- 1=psLevel1*, 2=psLevel2*, 3=psLevel3*++static char *prolog[] = {+  "/xpdf 75 dict def xpdf begin",+  "% PDF special state",+  "/pdfDictSize 15 def",+  "~1sn",+  "/pdfStates 64 array def",+  "  0 1 63 {",+  "    pdfStates exch pdfDictSize dict",+  "    dup /pdfStateIdx 3 index put",+  "    put",+  "  } for",+  "~123sn",+  "/pdfSetup {",+  "  3 1 roll 2 array astore",+  "  /setpagedevice where {",+  "    pop 3 dict begin",+  "      /PageSize exch def",+  "      /ImagingBBox null def",+  "      /Policies 1 dict dup begin /PageSize 3 def end def",+  "      { /Duplex true def } if",+  "    currentdict end setpagedevice",+  "  } {",+  "    pop pop",+  "  } ifelse",+  "} def",+  "~1sn",+  "/pdfOpNames [",+  "  /pdfFill /pdfStroke /pdfLastFill /pdfLastStroke",+  "  /pdfTextMat /pdfFontSize /pdfCharSpacing /pdfTextRender",+  "  /pdfTextRise /pdfWordSpacing /pdfHorizScaling /pdfTextClipPath",+  "] def",+  "~123sn",+  "/pdfStartPage {",+  "~1sn",+  "  pdfStates 0 get begin",+  "~23sn",+  "  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",+  "~23sn",+  "  /pdfFill [0] def",+  "  /pdfStroke [0] def",+  "  /pdfFillOP false def",+  "  /pdfStrokeOP false def",+  "~123sn",+  "  /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",+  "~123sn",+  "% 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 } if",+  "      dup /pdfFill exch def aload pop pdfFillXform setcolor",+  "     /pdfLastFill true def /pdfLastStroke false def } def",+  "/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } 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",+  "~23s",+  "/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",+  "/ck { 6 copy 6 array astore /pdfFill exch def",+  "      findcmykcustomcolor exch setcustomcolor",+  "      /pdfLastFill true def /pdfLastStroke false def } def",+  "/CK { 6 copy 6 array astore /pdfStroke exch def",+  "      findcmykcustomcolor exch setcustomcolor",+  "      /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",+  "~123sn",+  "% 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",+  "~3sn",+  "/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",+  "~123sn",+  "% graphics state operators",+  "~1sn",+  "/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",+  "~23sn",+  "/q { gsave pdfDictSize dict begin } def",+  "/Q {",+  "  end grestore",+  "  /pdfLastFill where {",+  "    pop",+  "    pdfLastFill {",+  "      pdfFillOP setoverprint",+  "    } {",+  "      pdfStrokeOP setoverprint",+  "    } ifelse",+  "  } if",+  "} def",+  "~123sn",+  "/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 } def",+  "/W* { eoclip newpath } def",+  "/Ws { strokepath clip newpath } def",+  "% 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",+  "/cshow where {",+  "  pop",+  "  /cshow2 {",+  "    dup {",+  "      pop pop",+  "      1 string dup 0 3 index put 3 index exec",+  "    } exch cshow",+  "    pop pop",+  "  } def",+  "}{",+  "  /cshow2 {",+  "    currentfont /FontType get 0 eq {",+  "      0 2 2 index length 1 sub {",+  "        2 copy get exch 1 add 2 index exch get",+  "        2 copy exch 256 mul add",+  "        2 string dup 0 6 5 roll put dup 1 5 4 roll put",+  "        3 index exec",+  "      } for",+  "    } {",+  "      dup {",+  "        1 string dup 0 3 index put 3 index exec",+  "      } forall",+  "    } ifelse",+  "    pop pop",+  "  } def",+  "} ifelse",+  "/awcp {", // awidthcharpath+  "  exch {",+  "    false charpath",+  "    5 index 5 index rmoveto",+  "    6 index eq { 7 index 7 index rmoveto } if",+  "  } exch cshow2",+  "  6 {pop} repeat",+  "} def",+  "/Tj {",+  "  fCol",  // because stringwidth has to draw Type 3 chars+  "  1 index stringwidth pdfTextMat idtransform pop",+  "  sub 1 index length dup 0 ne { div } { pop pop 0 } ifelse",+  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",+  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",+  "  pdfTextMat dtransform",+  "  6 5 roll Tj1",+  "} def",+  "/Tj16 {",+  "  fCol",  // because stringwidth has to draw Type 3 chars+  "  2 index stringwidth pdfTextMat idtransform pop",+  "  sub exch div",+  "  pdfWordSpacing pdfHorizScaling mul 0 pdfTextMat dtransform 32",+  "  4 3 roll pdfCharSpacing pdfHorizScaling mul add 0",+  "  pdfTextMat dtransform",+  "  6 5 roll Tj1",+  "} def",+  "/Tj16V {",+  "  fCol",  // because stringwidth has to draw Type 3 chars+  "  2 index stringwidth pdfTextMat idtransform exch pop",+  "  sub exch div",+  "  0 pdfWordSpacing pdfTextMat dtransform 32",+  "  4 3 roll pdfCharSpacing add 0 exch",+  "  pdfTextMat dtransform",+  "  6 5 roll Tj1",+  "} def",+  "/Tj1 {",+  "  0 pdfTextRise pdfTextMat dtransform rmoveto",+  "  currentpoint 8 2 roll",+  "  pdfTextRender 1 and 0 eq {",+  "    6 copy awidthshow",+  "  } if",+  "  pdfTextRender 3 and dup 1 eq exch 2 eq or {",+  "    7 index 7 index moveto",+  "    6 copy",+  "    currentfont /FontType get 3 eq { fCol } { sCol } ifelse",+  "    false awcp currentpoint stroke moveto",+  "  } if",+  "  pdfTextRender 4 and 0 ne {",+  "    8 6 roll moveto",+  "    false awcp",+  "    /pdfTextClipPath [ pdfTextClipPath aload pop",+  "      {/moveto cvx}",+  "      {/lineto cvx}",+  "      {/curveto cvx}",+  "      {/closepath cvx}",+  "    pathforall ] def",+  "    currentpoint newpath moveto",+  "  } {",+  "    8 {pop} repeat",+  "  } ifelse",+  "  0 pdfTextRise neg pdfTextMat dtransform rmoveto",+  "} def",+  "/TJm { pdfFontSize 0.001 mul mul neg 0",+  "       pdfTextMat dtransform rmoveto } def",+  "/TJmV { pdfFontSize 0.001 mul 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",+  "/pdfImM1a {",+  "  { 2 copy get exch 1 add exch } imagemask",+  "  pop pop",+  "} def",+  "~23sn",+  "% Level 2 image operators",+  "/pdfImBuf 100 string def",+  "/pdfIm {",+  "  image",+  "  { currentfile pdfImBuf readline",+  "    not { pop exit } if",+  "    (%-EOD-) eq { exit } if } loop",+  "} 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",+  "  { currentfile pdfImBuf readline",+  "    not { pop exit } if",+  "    (%-EOD-) eq { exit } if } loop",+  "} def",+  "~23sn",+  "/pdfImM {",+  "  fCol imagemask",+  "  { currentfile pdfImBuf readline",+  "    not { pop exit } if",+  "    (%-EOD-) eq { exit } if } loop",+  "} def",+  "/pr { 2 index 2 index 3 2 roll putinterval 4 add } def",+  "/pdfImClip {",+  "  gsave",+  "  0 2 4 index length 1 sub {",+  "    dup 4 index exch 2 copy",+  "    get 5 index div put",+  "    1 add 3 index exch 2 copy",+  "    get 3 index div put",+  "  } for",+  "  pop pop rectclip",+  "} def",+  "/pdfImClipEnd { grestore } def",+  "~23sn",+  "% 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",+  "~23sn",+  "    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 0 eq {",+  "    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",+  "~23sn",+  "    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",+  "~23sn",+  "    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",+  "~123sn",+  "end",+  NULL+};++static 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 {+  char *psName;			// PostScript name+  double mWidth;		// width of 'm' character+};++static char *psFonts[] = {+  "Courier",+  "Courier-Bold",+  "Courier-Oblique",+  "Courier-BoldOblique",+  "Helvetica",+  "Helvetica-Bold",+  "Helvetica-Oblique",+  "Helvetica-BoldOblique",+  "Symbol",+  "Times-Roman",+  "Times-Bold",+  "Times-Italic",+  "Times-BoldItalic",+  "ZapfDingbats",+  NULL+};++static PSSubstFont psSubstFonts[] = {+  {"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},+  {"Courier",               0.600},+  {"Courier-Oblique",       0.600},+  {"Courier-Bold",          0.600},+  {"Courier-BoldOblique",   0.600}+};++// Info for 8-bit fonts+struct PSFont8Info {+  Ref fontID;+  Gushort *codeToGID;		// code-to-GID mapping for TrueType fonts+};++// Encoding info for substitute 16-bit font+struct PSFont16Enc {+  Ref fontID;+  GString *enc;+};++//------------------------------------------------------------------------+// 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;+};++//------------------------------------------------------------------------+// DeviceNRecoder+//------------------------------------------------------------------------++class DeviceNRecoder: public FilterStream {+public:++  DeviceNRecoder(Stream *strA, int widthA, int heightA,+		 GfxImageColorMap *colorMapA);+  virtual ~DeviceNRecoder();+  virtual StreamKind getKind() { return strWeird; }+  virtual void reset();+  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, char *indent) { 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 (imgStr) {+    delete imgStr;+  }+}++void DeviceNRecoder::reset() {+  imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(),+			   colorMap->getBits());+  imgStr->reset();+}++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;+}++//------------------------------------------------------------------------+// PSOutputDev+//------------------------------------------------------------------------++extern "C" {+typedef void (*SignalFunc)(int);+}++static void outputToFile(void *stream, char *data, int len) {+  fwrite(data, 1, len, (FILE *)stream);+}++PSOutputDev::PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,+			 int firstPage, int lastPage, PSOutMode modeA,+			 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,+			 GBool manualCtrlA) {+  FILE *f;+  PSFileType fileTypeA;++  underlayCbk = NULL;+  underlayCbkData = NULL;+  overlayCbk = NULL;+  overlayCbkData = NULL;++  fontIDs = NULL;+  fontFileIDs = NULL;+  fontFileNames = NULL;+  font8Info = NULL;+  font16Enc = NULL;+  imgIDs = NULL;+  formIDs = NULL;+  xobjStack = 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(-1, "Couldn't run print command '%s'", fileName);+      ok = gFalse;+      return;+    }+#else+    error(-1, "Print commands are not supported ('%s')", fileName);+    ok = gFalse;+    return;+#endif+  } else {+    fileTypeA = psFile;+    if (!(f = fopen(fileName, "w"))) {+      error(-1, "Couldn't open PostScript file '%s'", fileName);+      ok = gFalse;+      return;+    }+  }++  init(outputToFile, f, fileTypeA,+       xrefA, catalog, firstPage, lastPage, modeA,+       imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA);+}++PSOutputDev::PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,+			 XRef *xrefA, Catalog *catalog,+			 int firstPage, int lastPage, PSOutMode modeA,+			 int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,+			 GBool manualCtrlA) {+  underlayCbk = NULL;+  underlayCbkData = NULL;+  overlayCbk = NULL;+  overlayCbkData = NULL;++  fontIDs = NULL;+  fontFileIDs = NULL;+  fontFileNames = NULL;+  font8Info = NULL;+  font16Enc = NULL;+  imgIDs = NULL;+  formIDs = NULL;+  xobjStack = NULL;+  embFontList = NULL;+  customColors = NULL;+  haveTextClip = gFalse;+  t3String = NULL;++  init(outputFuncA, outputStreamA, psGeneric,+       xrefA, catalog, firstPage, lastPage, modeA,+       imgLLXA, imgLLYA, imgURXA, imgURYA, manualCtrlA);+}++void PSOutputDev::init(PSOutputFunc outputFuncA, void *outputStreamA,+		       PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,+		       int firstPage, int lastPage, PSOutMode modeA,+		       int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,+		       GBool manualCtrlA) {+  Page *page;+  PDFRectangle *box;++  // initialize+  ok = gTrue;+  outputFunc = outputFuncA;+  outputStream = outputStreamA;+  fileType = fileTypeA;+  xref = xrefA;+  level = globalParams->getPSLevel();+  mode = modeA;+  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) {+    // this check is needed in case the document has zero pages+    if (firstPage > 0 && firstPage <= catalog->getNumPages()) {+      page = catalog->getPage(firstPage);+      paperWidth = (int)ceil(page->getMediaWidth());+      paperHeight = (int)ceil(page->getMediaHeight());+    } else {+      paperWidth = 1;+      paperHeight = 1;+    }+    imgLLX = imgLLY = 0;+    imgURX = paperWidth;+    imgURY = paperHeight;+  }+  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 fontIDs, fontFileIDs, and fontFileNames lists+  fontIDSize = 64;+  fontIDLen = 0;+  fontIDs = (Ref *)gmallocn(fontIDSize, sizeof(Ref));+  fontFileIDSize = 64;+  fontFileIDLen = 0;+  fontFileIDs = (Ref *)gmallocn(fontFileIDSize, sizeof(Ref));+  fontFileNameSize = 64;+  fontFileNameLen = 0;+  fontFileNames = (GString **)gmallocn(fontFileNameSize, sizeof(GString *));+  nextTrueTypeNum = 0;+  font8InfoLen = 0;+  font8InfoSize = 0;+  font16EncLen = 0;+  font16EncSize = 0;+  imgIDLen = 0;+  imgIDSize = 0;+  formIDLen = 0;+  formIDSize = 0;++  xobjStack = new GList();+  numSaves = 0;+  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 > 0 && firstPage <= catalog->getNumPages()) {+      writeHeader(firstPage, lastPage,+		  catalog->getPage(firstPage)->getMediaBox(),+		  catalog->getPage(firstPage)->getCropBox(),+		  catalog->getPage(firstPage)->getRotate());+    } else {+      box = new PDFRectangle(0, 0, 1, 1);+      writeHeader(firstPage, lastPage, box, box, 0);+      delete box;+    }+    if (mode != psModeForm) {+      writePS("%%BeginProlog\n");+    }+    writeXpdfProcset();+    if (mode != psModeForm) {+      writePS("%%EndProlog\n");+      writePS("%%BeginSetup\n");+    }+    writeDocSetup(catalog, firstPage, lastPage);+    if (mode != psModeForm) {+      writePS("%%EndSetup\n");+    }+  }++  // initialize sequential page number+  seqPage = 1;+}++PSOutputDev::~PSOutputDev() {+  PSOutCustomColor *cc;+  int i;++  if (ok) {+    if (!manualCtrl) {+      writePS("%%Trailer\n");+      writeTrailer();+      if (mode != psModeForm) {+	writePS("%%EOF\n");+      }+    }+    if (fileType == psFile) {+#ifdef MACOS+      ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);+#endif+      fclose((FILE *)outputStream);+    }+#ifdef HAVE_POPEN+    else if (fileType == psPipe) {+      pclose((FILE *)outputStream);+#ifndef WIN32+      signal(SIGPIPE, (SignalFunc)SIG_DFL);+#endif+    }+#endif+  }+  if (embFontList) {+    delete embFontList;+  }+  if (fontIDs) {+    gfree(fontIDs);+  }+  if (fontFileIDs) {+    gfree(fontFileIDs);+  }+  if (fontFileNames) {+    for (i = 0; i < fontFileNameLen; ++i) {+      delete fontFileNames[i];+    }+    gfree(fontFileNames);+  }+  if (font8Info) {+    for (i = 0; i < font8InfoLen; ++i) {+      gfree(font8Info[i].codeToGID);+    }+    gfree(font8Info);+  }+  if (font16Enc) {+    for (i = 0; i < font16EncLen; ++i) {+      delete font16Enc[i].enc;+    }+    gfree(font16Enc);+  }+  gfree(imgIDs);+  gfree(formIDs);+  if (xobjStack) {+    delete xobjStack;+  }+  while (customColors) {+    cc = customColors;+    customColors = cc->next;+    delete cc;+  }+}++void PSOutputDev::writeHeader(int firstPage, int lastPage,+			      PDFRectangle *mediaBox, PDFRectangle *cropBox,+			      int pageRotate) {+  Object info, obj1;+  double x1, y1, x2, y2;++  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("% Produced by xpdf/pdftops {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 == psLevel1 || level == psLevel1Sep) ? 1 :+	     (level == psLevel2 || level == psLevel2Sep) ? 2 : 3);+  if (level == psLevel1Sep || level == psLevel2Sep || level == psLevel3Sep) {+    writePS("%%DocumentProcessColors: (atend)\n");+    writePS("%%DocumentCustomColors: (atend)\n");+  }+  writePS("%%DocumentSuppliedResources: (atend)\n");++  switch (mode) {+  case psModePS:+    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");+    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:.4g} {1:.4g} {2:.4g} {3:.4g}\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, sep, nonSep;+  char **p;+  char *q;++  writePSFmt("%%BeginResource: procset xpdf {0:s} 0\n", xpdfVersion);+  writePSFmt("%%Copyright: {0:s}\n", xpdfCopyright);+  lev1 = lev2 = lev3 = sep = nonSep = gTrue;+  for (p = prolog; *p; ++p) {+    if ((*p)[0] == '~') {+      lev1 = lev2 = lev3 = sep = nonSep = 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 's': sep = gTrue; break;+	case 'n': nonSep = gTrue; break;+	}+      }+    } else if ((level == psLevel1 && lev1 && nonSep) ||+	       (level == psLevel1Sep && lev1 && sep) ||+	       (level == psLevel2 && lev2 && nonSep) ||+	       (level == psLevel2Sep && lev2 && sep) ||+	       (level == psLevel3 && lev3 && nonSep) ||+	       (level == psLevel3Sep && lev3 && sep)) {+      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,+				int firstPage, int lastPage) {+  Page *page;+  Dict *resDict;+  Annots *annots;+  Object obj1, obj2;+  int pg, i;++  if (mode == psModeForm) {+    // swap the form and xpdf dicts+    writePS("xpdf end begin dup begin\n");+  } else {+    writePS("xpdf begin\n");+  }+  for (pg = firstPage; pg <= lastPage; ++pg) {+    page = catalog->getPage(pg);+    if ((resDict = page->getResourceDict())) {+      setupResources(resDict);+    }+    annots = new Annots(xref, catalog, page->getAnnots(&obj1));+    obj1.free();+    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 (mode != psModeForm) {+    if (mode != psModeEPS && !manualCtrl) {+      writePSFmt("{0:d} {1:d} {2:s} pdfSetup\n",+		 paperWidth, paperHeight,+		 globalParams->getPSDuplex() ? "true" : "false");+    }+#if OPI_SUPPORT+    if (globalParams->getPSOPI()) {+      writePS("/opiMatrix matrix currentmatrix def\n");+    }+#endif+  }+}++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("end\n");+    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) {+	writePSFmt(" ({0:s})", cc->name->getCString());+      }+      writePS("\n");+      writePS("%%CMYKCustomColor:\n");+      for (cc = customColors; cc; cc = cc->next) {+	writePSFmt("%%+ {0:.4g} {1:.4g} {2:.4g} {3:.4g} ({4:t})\n",+		   cc->c, cc->m, cc->y, cc->k, cc->name);+      }+    }+  }+}++void PSOutputDev::setupResources(Dict *resDict) {+  Object xObjDict, xObjRef, xObj, patDict, patRef, pat, resObj;+  Ref ref0, ref1;+  GBool skip;+  int i, j;++  setupFonts(resDict);+  setupImages(resDict);+  setupForms(resDict);++  //----- recursively scan XObjects+  resDict->lookup("XObject", &xObjDict);+  if (xObjDict.isDict()) {+    for (i = 0; i < xObjDict.dictGetLength(); ++i) {++      // avoid infinite recursion on XObjects+      skip = gFalse;+      if ((xObjDict.dictGetValNF(i, &xObjRef)->isRef())) {+	ref0 = xObjRef.getRef();+	for (j = 0; j < xobjStack->getLength(); ++j) {+	  ref1 = *(Ref *)xobjStack->get(j);+	  if (ref1.num == ref0.num && ref1.gen == ref0.gen) {+	    skip = gTrue;+	    break;+	  }+	}+	if (!skip) {+	  xobjStack->append(&ref0);+	}+      }+      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();+      }++      if (xObjRef.isRef() && !skip) {+	xobjStack->del(xobjStack->getLength() - 1);+      }+      xObjRef.free();+    }+  }+  xObjDict.free();++  //----- recursively scan Patterns+  resDict->lookup("Pattern", &patDict);+  if (patDict.isDict()) {+    inType3Char = gTrue;+    for (i = 0; i < patDict.dictGetLength(); ++i) {++      // avoid infinite recursion on Patterns+      skip = gFalse;+      if ((patDict.dictGetValNF(i, &patRef)->isRef())) {+	ref0 = patRef.getRef();+	for (j = 0; j < xobjStack->getLength(); ++j) {+	  ref1 = *(Ref *)xobjStack->get(j);+	  if (ref1.num == ref0.num && ref1.gen == ref0.gen) {+	    skip = gTrue;+	    break;+	  }+	}+	if (!skip) {+	  xobjStack->append(&ref0);+	}+      }+      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();+      }++      if (patRef.isRef() && !skip) {+	xobjStack->del(xobjStack->getLength() - 1);+      }+      patRef.free();+    }+    inType3Char = gFalse;+  }+  patDict.free();+}++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) {+  Ref fontFileID;+  GString *name;+  PSFontParam *fontParam;+  GString *psName;+  char buf[16];+  GBool subst;+  UnicodeMap *uMap;+  char *charName;+  double xs, ys;+  int code;+  double w1, w2;+  double *fm;+  int i, j;++  // check if font is already set up+  for (i = 0; i < fontIDLen; ++i) {+    if (fontIDs[i].num == font->getID()->num &&+	fontIDs[i].gen == font->getID()->gen) {+      return;+    }+  }++  // add entry to fontIDs list+  if (fontIDLen >= fontIDSize) {+    fontIDSize += 64;+    fontIDs = (Ref *)greallocn(fontIDs, fontIDSize, sizeof(Ref));+  }+  fontIDs[fontIDLen++] = *font->getID();++  xs = ys = 1;+  subst = gFalse;++  // check for resident 8-bit font+  if (font->getName() &&+      (fontParam = globalParams->getPSFont(font->getName()))) {+    psName = new GString(fontParam->psFontName->getCString());++  // check for embedded Type 1 font+  } else if (globalParams->getPSEmbedType1() &&+	     font->getType() == fontType1 &&+	     font->getEmbeddedFontID(&fontFileID)) {+    psName = filterPSName(font->getEmbeddedFontName());+    setupEmbeddedType1Font(&fontFileID, psName);++  // check for embedded Type 1C font+  } else if (globalParams->getPSEmbedType1() &&+	     font->getType() == fontType1C &&+	     font->getEmbeddedFontID(&fontFileID)) {+    // use the PDF font name because the embedded font name might+    // not include the subset prefix+    psName = filterPSName(font->getOrigName());+    setupEmbeddedType1CFont(font, &fontFileID, psName);++  // check for embedded OpenType - Type 1C font+  } else if (globalParams->getPSEmbedType1() &&+	     font->getType() == fontType1COT &&+	     font->getEmbeddedFontID(&fontFileID)) {+    // use the PDF font name because the embedded font name might+    // not include the subset prefix+    psName = filterPSName(font->getOrigName());+    setupEmbeddedOpenTypeT1CFont(font, &fontFileID, psName);++  // check for external Type 1 font file+  } else if (globalParams->getPSEmbedType1() &&+	     font->getType() == fontType1 &&+	     font->getExtFontFile()) {+    // this assumes that the PS font name matches the PDF font name+    psName = font->getName()->copy();+    setupExternalType1Font(font->getExtFontFile(), psName);++  // check for embedded TrueType font+  } else if (globalParams->getPSEmbedTrueType() &&+	     (font->getType() == fontTrueType ||+	      font->getType() == fontTrueTypeOT) &&+	     font->getEmbeddedFontID(&fontFileID)) {+    psName = filterPSName(font->getEmbeddedFontName());+    setupEmbeddedTrueTypeFont(font, &fontFileID, psName);++  // check for external TrueType font file+  } else if (globalParams->getPSEmbedTrueType() &&+	     font->getType() == fontTrueType &&+	     font->getExtFontFile()) {+    psName = filterPSName(font->getName());+    setupExternalTrueTypeFont(font, psName);++  // check for embedded CID PostScript font+  } else if (globalParams->getPSEmbedCIDPostScript() &&+	     font->getType() == fontCIDType0C &&+	     font->getEmbeddedFontID(&fontFileID)) {+    psName = filterPSName(font->getEmbeddedFontName());+    setupEmbeddedCIDType0Font(font, &fontFileID, psName);++  // check for embedded CID TrueType font+  } else if (globalParams->getPSEmbedCIDTrueType() &&+	     (font->getType() == fontCIDType2 ||+	      font->getType() == fontCIDType2OT) &&+	     font->getEmbeddedFontID(&fontFileID)) {+    psName = filterPSName(font->getEmbeddedFontName());+    //~ should check to see if font actually uses vertical mode+    setupEmbeddedCIDTrueTypeFont(font, &fontFileID, psName, gTrue);++  // check for embedded OpenType - CID CFF font+  } else if (globalParams->getPSEmbedCIDPostScript() &&+	     font->getType() == fontCIDType0COT &&+	     font->getEmbeddedFontID(&fontFileID)) {+    psName = filterPSName(font->getEmbeddedFontName());+    setupEmbeddedOpenTypeCFFFont(font, &fontFileID, psName);++  // check for Type 3 font+  } else if (font->getType() == fontType3) {+    psName = GString::format("T3_{0:d}_{1:d}",+			     font->getID()->num, font->getID()->gen);+    setupType3Font(font, psName, parentResDict);++  // do 8-bit font substitution+  } else if (!font->isCIDFont()) {+    subst = gTrue;+    name = font->getName();+    psName = NULL;+    if (name) {+      for (i = 0; psFonts[i]; ++i) {+	if (name->cmp(psFonts[i]) == 0) {+	  psName = new GString(psFonts[i]);+	  break;+	}+      }+    }+    if (!psName) {+      if (font->isFixedWidth()) {+	i = 8;+      } else if (font->isSerif()) {+	i = 4;+      } else {+	i = 0;+      }+      if (font->isBold()) {+	i += 2;+      }+      if (font->isItalic()) {+	i += 1;+      }+      psName = new GString(psSubstFonts[i].psName);+      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(code);+      } else {+	w1 = 0;+      }+      w2 = psSubstFonts[i].mWidth;+      xs = w1 / w2;+      if (xs < 0.1) {+	xs = 1;+      }+      if (font->getType() == fontType3) {+	// This is a hack which makes it possible to substitute for 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.+	ys = xs;+	fm = font->getFontMatrix();+	if (fm[0] != 0) {+	  ys *= fm[3] / fm[0];+	}+      } else {+	ys = 1;+      }+    }++  // do 16-bit font substitution+  } else if ((fontParam = globalParams->+	        getPSFont16(font->getName(),+			    ((GfxCIDFont *)font)->getCollection(),+			    font->getWMode()))) {+    subst = gTrue;+    psName = fontParam->psFontName->copy();+    if (font16EncLen >= font16EncSize) {+      font16EncSize += 16;+      font16Enc = (PSFont16Enc *)greallocn(font16Enc,+					   font16EncSize, sizeof(PSFont16Enc));+    }+    font16Enc[font16EncLen].fontID = *font->getID();+    font16Enc[font16EncLen].enc = fontParam->encoding->copy();+    if ((uMap = globalParams->getUnicodeMap(font16Enc[font16EncLen].enc))) {+      uMap->decRefCnt();+      ++font16EncLen;+    } else {+      error(-1, "Couldn't find Unicode map for 16-bit font encoding '%s'",+	    font16Enc[font16EncLen].enc->getCString());+    }++  // give up - can't do anything with this font+  } else {+    error(-1, "Couldn't find a font to substitute for '%s' ('%s' character collection)",+	  font->getName() ? font->getName()->getCString() : "(unnamed)",+	  ((GfxCIDFont *)font)->getCollection()+	    ? ((GfxCIDFont *)font)->getCollection()->getCString()+	    : "(unknown)");+    return;+  }++  // generate PostScript code to set up the font+  if (font->isCIDFont()) {+    if (level == psLevel3 || level == psLevel3Sep) {+      writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16L3\n",+		 font->getID()->num, font->getID()->gen, psName,+		 font->getWMode());+    } else {+      writePSFmt("/F{0:d}_{1:d} /{2:t} {3:d} pdfMakeFont16\n",+		 font->getID()->num, font->getID()->gen, psName,+		 font->getWMode());+    }+  } else {+    writePSFmt("/F{0:d}_{1:d} /{2:t} {3:.4g} {4:.4g}\n",+	       font->getID()->num, font->getID()->gen, 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()) {+	  sprintf(buf, "c%02x", i+j);+	  charName = buf;+	} else {+	  charName = ((Gfx8BitFont *)font)->getCharName(i+j);+	  // this is a kludge for broken PDF files that encode char 32+	  // as .notdef+	  if (i+j == 32 && charName && !strcmp(charName, ".notdef")) {+	    charName = "space";+	  }+	}+	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");+  }++  delete psName;+}++void PSOutputDev::setupEmbeddedType1Font(Ref *id, GString *psName) {+  static char hexChar[17] = "0123456789abcdef";+  Object refObj, strObj, obj1, obj2, obj3;+  Dict *dict;+  int length1, length2, length3;+  int c;+  int start[4];+  GBool binMode;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileIDLen; ++i) {+    if (fontFileIDs[i].num == id->num &&+	fontFileIDs[i].gen == id->gen)+      return;+  }++  // add entry to fontFileIDs list+  if (fontFileIDLen >= fontFileIDSize) {+    fontFileIDSize += 64;+    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));+  }+  fontFileIDs[fontFileIDLen++] = *id;++  // get the font stream and info+  refObj.initRef(id->num, id->gen);+  refObj.fetch(xref, &strObj);+  refObj.free();+  if (!strObj.isStream()) {+    error(-1, "Embedded font file object is not a stream");+    goto err1;+  }+  if (!(dict = strObj.streamGetDict())) {+    error(-1, "Embedded font stream is missing its dictionary");+    goto err1;+  }+  dict->lookup("Length1", &obj1);+  dict->lookup("Length2", &obj2);+  dict->lookup("Length3", &obj3);+  if (!obj1.isInt() || !obj2.isInt() || !obj3.isInt()) {+    error(-1, "Missing length fields in embedded font stream dictionary");+    obj1.free();+    obj2.free();+    obj3.free();+    goto err1;+  }+  length1 = obj1.getInt();+  length2 = obj2.getInt();+  length3 = obj3.getInt();+  obj1.free();+  obj2.free();+  obj3.free();++  // beginning comment+  writePSFmt("%%BeginResource: font {0:t}\n", psName);+  embFontList->append("%%+ font ");+  embFontList->append(psName->getCString());+  embFontList->append("\n");++  // copy ASCII portion of font+  strObj.streamReset();+  for (i = 0; i < length1 && (c = strObj.streamGetChar()) != EOF; ++i) {+    writePSChar(c);+  }++  // figure out if encrypted portion is binary or ASCII+  binMode = gFalse;+  for (i = 0; i < 4; ++i) {+    start[i] = strObj.streamGetChar();+    if (start[i] == EOF) {+      error(-1, "Unexpected end of file in embedded font stream");+      goto err1;+    }+    if (!((start[i] >= '0' && start[i] <= '9') ||+	  (start[i] >= 'A' && start[i] <= 'F') ||+	  (start[i] >= 'a' && start[i] <= 'f')))+      binMode = gTrue;+  }++  // convert binary data to ASCII+  if (binMode) {+    for (i = 0; i < 4; ++i) {+      writePSChar(hexChar[(start[i] >> 4) & 0x0f]);+      writePSChar(hexChar[start[i] & 0x0f]);+    }+#if 0 // this causes trouble for various PostScript printers+    // if Length2 is incorrect (too small), font data gets chopped, so+    // we take a few extra characters from the trailer just in case+    length2 += length3 >= 8 ? 8 : length3;+#endif+    while (i < length2) {+      if ((c = strObj.streamGetChar()) == EOF) {+	break;+      }+      writePSChar(hexChar[(c >> 4) & 0x0f]);+      writePSChar(hexChar[c & 0x0f]);+      if (++i % 32 == 0) {+	writePSChar('\n');+      }+    }+    if (i % 32 > 0) {+      writePSChar('\n');+    }++  // already in ASCII format -- just copy it+  } else {+    for (i = 0; i < 4; ++i) {+      writePSChar(start[i]);+    }+    for (i = 4; i < length2; ++i) {+      if ((c = strObj.streamGetChar()) == EOF) {+	break;+      }+      writePSChar(c);+    }+  }++  // write padding and "cleartomark"+  for (i = 0; i < 8; ++i) {+    writePS("00000000000000000000000000000000"+	    "00000000000000000000000000000000\n");+  }+  writePS("cleartomark\n");++  // ending comment+  writePS("%%EndResource\n");++ err1:+  strObj.streamClose();+  strObj.free();+}++//~ This doesn't handle .pfb files or binary eexec data (which only+//~ happens in pfb files?).+void PSOutputDev::setupExternalType1Font(GString *fileName, GString *psName) {+  FILE *fontFile;+  int c;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileNameLen; ++i) {+    if (!fontFileNames[i]->cmp(fileName)) {+      return;+    }+  }++  // add entry to fontFileNames list+  if (fontFileNameLen >= fontFileNameSize) {+    fontFileNameSize += 64;+    fontFileNames = (GString **)greallocn(fontFileNames,+					  fontFileNameSize, sizeof(GString *));+  }+  fontFileNames[fontFileNameLen++] = fileName->copy();++  // beginning comment+  writePSFmt("%%BeginResource: font {0:t}\n", psName);+  embFontList->append("%%+ font ");+  embFontList->append(psName->getCString());+  embFontList->append("\n");++  // copy the font file+  if (!(fontFile = fopen(fileName->getCString(), "rb"))) {+    error(-1, "Couldn't open external font file");+    return;+  }+  while ((c = fgetc(fontFile)) != EOF) {+    writePSChar(c);+  }+  fclose(fontFile);++  // ending comment+  writePS("%%EndResource\n");+}++void PSOutputDev::setupEmbeddedType1CFont(GfxFont *font, Ref *id,+					  GString *psName) {+  char *fontBuf;+  int fontLen;+  FoFiType1C *ffT1C;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileIDLen; ++i) {+    if (fontFileIDs[i].num == id->num &&+	fontFileIDs[i].gen == id->gen)+      return;+  }++  // add entry to fontFileIDs list+  if (fontFileIDLen >= fontFileIDSize) {+    fontFileIDSize += 64;+    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));+  }+  fontFileIDs[fontFileIDLen++] = *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+  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");+}++void PSOutputDev::setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id,+					       GString *psName) {+  char *fontBuf;+  int fontLen;+  FoFiTrueType *ffTT;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileIDLen; ++i) {+    if (fontFileIDs[i].num == id->num &&+	fontFileIDs[i].gen == id->gen)+      return;+  }++  // add entry to fontFileIDs list+  if (fontFileIDLen >= fontFileIDSize) {+    fontFileIDSize += 64;+    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));+  }+  fontFileIDs[fontFileIDLen++] = *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+  fontBuf = font->readEmbFontFile(xref, &fontLen);+  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {+    if (ffTT->isOpenTypeCFF()) {+      ffTT->convertToType1(psName->getCString(), NULL, gTrue,+			   outputFunc, outputStream);+    }+    delete ffTT;+  }+  gfree(fontBuf);++  // ending comment+  writePS("%%EndResource\n");+}++void PSOutputDev::setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id,+					    GString *psName) {+  char *fontBuf;+  int fontLen;+  FoFiTrueType *ffTT;+  Gushort *codeToGID;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileIDLen; ++i) {+    if (fontFileIDs[i].num == id->num &&+	fontFileIDs[i].gen == id->gen) {+      psName->appendf("_{0:d}", nextTrueTypeNum++);+      break;+    }+  }++  // add entry to fontFileIDs list+  if (i == fontFileIDLen) {+    if (fontFileIDLen >= fontFileIDSize) {+      fontFileIDSize += 64;+      fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));+    }+    fontFileIDs[fontFileIDLen++] = *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+  fontBuf = font->readEmbFontFile(xref, &fontLen);+  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {+    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);+    ffTT->convertToType42(psName->getCString(),+			  ((Gfx8BitFont *)font)->getHasEncoding()+			    ? ((Gfx8BitFont *)font)->getEncoding()+			    : (char **)NULL,+			  codeToGID, outputFunc, outputStream);+    if (codeToGID) {+      if (font8InfoLen >= font8InfoSize) {+	font8InfoSize += 16;+	font8Info = (PSFont8Info *)greallocn(font8Info,+					     font8InfoSize,+					     sizeof(PSFont8Info));+      }+      font8Info[font8InfoLen].fontID = *font->getID();+      font8Info[font8InfoLen].codeToGID = codeToGID;+      ++font8InfoLen;+    }+    delete ffTT;+  }+  gfree(fontBuf);++  // ending comment+  writePS("%%EndResource\n");+}++void PSOutputDev::setupExternalTrueTypeFont(GfxFont *font, GString *psName) {+  GString *fileName;+  char *fontBuf;+  int fontLen;+  FoFiTrueType *ffTT;+  Gushort *codeToGID;+  int i;++  // check if font is already embedded+  fileName = font->getExtFontFile();+  for (i = 0; i < fontFileNameLen; ++i) {+    if (!fontFileNames[i]->cmp(fileName)) {+      psName->appendf("_{0:d}", nextTrueTypeNum++);+      break;+    }+  }++  // add entry to fontFileNames list+  if (i == fontFileNameLen) {+    if (fontFileNameLen >= fontFileNameSize) {+      fontFileNameSize += 64;+      fontFileNames =+	(GString **)greallocn(fontFileNames,+			      fontFileNameSize, sizeof(GString *));+    }+    fontFileNames[fontFileNameLen++] = fileName->copy();+  }++  // 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+  fontBuf = font->readExtFontFile(&fontLen);+  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {+    codeToGID = ((Gfx8BitFont *)font)->getCodeToGIDMap(ffTT);+    ffTT->convertToType42(psName->getCString(),+			  ((Gfx8BitFont *)font)->getHasEncoding()+			    ? ((Gfx8BitFont *)font)->getEncoding()+			    : (char **)NULL,+			  codeToGID, outputFunc, outputStream);+    if (codeToGID) {+      if (font8InfoLen >= font8InfoSize) {+	font8InfoSize += 16;+	font8Info = (PSFont8Info *)greallocn(font8Info,+					     font8InfoSize,+					     sizeof(PSFont8Info));+      }+      font8Info[font8InfoLen].fontID = *font->getID();+      font8Info[font8InfoLen].codeToGID = codeToGID;+      ++font8InfoLen;+    }+    delete ffTT;+  }+  gfree(fontBuf);++  // ending comment+  writePS("%%EndResource\n");+}++void PSOutputDev::setupEmbeddedCIDType0Font(GfxFont *font, Ref *id,+					    GString *psName) {+  char *fontBuf;+  int fontLen;+  FoFiType1C *ffT1C;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileIDLen; ++i) {+    if (fontFileIDs[i].num == id->num &&+	fontFileIDs[i].gen == id->gen)+      return;+  }++  // add entry to fontFileIDs list+  if (fontFileIDLen >= fontFileIDSize) {+    fontFileIDSize += 64;+    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));+  }+  fontFileIDs[fontFileIDLen++] = *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+  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(), outputFunc, outputStream);+    } else {+      // otherwise: use a non-CID composite font+      ffT1C->convertToType0(psName->getCString(), outputFunc, outputStream);+    }+    delete ffT1C;+  }+  gfree(fontBuf);++  // ending comment+  writePS("%%EndResource\n");+}++void PSOutputDev::setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id,+					       GString *psName,+					       GBool needVerticalMetrics) {+  char *fontBuf;+  int fontLen;+  FoFiTrueType *ffTT;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileIDLen; ++i) {+    if (fontFileIDs[i].num == id->num &&+	fontFileIDs[i].gen == id->gen) {+      psName->appendf("_{0:d}", nextTrueTypeNum++);+      break;+    }+  }++  // add entry to fontFileIDs list+  if (fontFileIDLen >= fontFileIDSize) {+    fontFileIDSize += 64;+    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));+  }+  fontFileIDs[fontFileIDLen++] = *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+  fontBuf = font->readEmbFontFile(xref, &fontLen);+  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {+    if (globalParams->getPSLevel() >= psLevel3) {+      // Level 3: use a CID font+      ffTT->convertToCIDType2(psName->getCString(),+			      ((GfxCIDFont *)font)->getCIDToGID(),+			      ((GfxCIDFont *)font)->getCIDToGIDLen(),+			      needVerticalMetrics,+			      outputFunc, outputStream);+    } else {+      // otherwise: use a non-CID composite font+      ffTT->convertToType0(psName->getCString(),+			   ((GfxCIDFont *)font)->getCIDToGID(),+			   ((GfxCIDFont *)font)->getCIDToGIDLen(),+			   needVerticalMetrics,+			   outputFunc, outputStream);+    }+    delete ffTT;+  }+  gfree(fontBuf);++  // ending comment+  writePS("%%EndResource\n");+}++void PSOutputDev::setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id,+					       GString *psName) {+  char *fontBuf;+  int fontLen;+  FoFiTrueType *ffTT;+  int i;++  // check if font is already embedded+  for (i = 0; i < fontFileIDLen; ++i) {+    if (fontFileIDs[i].num == id->num &&+	fontFileIDs[i].gen == id->gen)+      return;+  }++  // add entry to fontFileIDs list+  if (fontFileIDLen >= fontFileIDSize) {+    fontFileIDSize += 64;+    fontFileIDs = (Ref *)greallocn(fontFileIDs, fontFileIDSize, sizeof(Ref));+  }+  fontFileIDs[fontFileIDLen++] = *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+  fontBuf = font->readEmbFontFile(xref, &fontLen);+  if ((ffTT = FoFiTrueType::make(fontBuf, fontLen))) {+    if (ffTT->isOpenTypeCFF()) {+      if (globalParams->getPSLevel() >= psLevel3) {+	// Level 3: use a CID font+	ffTT->convertToCIDType0(psName->getCString(),+				outputFunc, outputStream);+      } else {+	// otherwise: use a non-CID composite font+	ffTT->convertToType0(psName->getCString(), outputFunc, outputStream);+      }+    }+    delete ffTT;+  }+  gfree(fontBuf);++  // ending comment+  writePS("%%EndResource\n");+}++void PSOutputDev::setupType3Font(GfxFont *font, GString *psName,+				 Dict *parentResDict) {+  Dict *resDict;+  Dict *charProcs;+  Object charProc;+  Gfx *gfx;+  PDFRectangle box;+  double *m;+  GString *buf;+  int i;++  // 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:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] def\n",+	     m[0], m[1], m[2], m[3], m[4], m[5]);+  m = font->getFontBBox();+  writePSFmt("/FontBBox [{0:.4g} {1:.4g} {2:.4g} {3:.4g}] 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(xref, this, resDict, &box, NULL);+    inType3Char = gTrue;+    for (i = 0; i < charProcs->getLength(); ++i) {+      t3Cacheable = gFalse;+      t3NeedsRestore = gFalse;+      writePS("/");+      writePSName(charProcs->getKey(i));+      writePS(" {\n");+      gfx->display(charProcs->getVal(i, &charProc));+      charProc.free();+      if (t3String) {+	if (t3Cacheable) {+	  buf = GString::format("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} setcachedevice\n",+				t3WX, t3WY, t3LLX, t3LLY, t3URX, t3URY);+	} else {+	  buf = GString::format("{0:.4g} {1:.4g} 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");+}++void PSOutputDev::setupImages(Dict *resDict) {+  Object xObjDict, xObj, xObjRef, subtypeObj;+  int i;++  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()) {+	    setupImage(xObjRef.getRef(), xObj.getStream());+	  } else {+	    error(-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 useRLE, useCompressed, useASCIIHex;+  GString *s;+  int c;+  int size, line, col, i;++  // check if image is already setup+  for (i = 0; i < imgIDLen; ++i) {+    if (imgIDs[i].num == id.num && imgIDs[i].gen == id.gen) {+      return;+    }+  }++  // add entry to imgIDs list+  if (imgIDLen >= imgIDSize) {+    if (imgIDSize == 0) {+      imgIDSize = 64;+    } else {+      imgIDSize *= 2;+    }+    imgIDs = (Ref *)greallocn(imgIDs, imgIDSize, sizeof(Ref));+  }+  imgIDs[imgIDLen++] = id;++  // filters+  //~ this does not correctly handle the DeviceN color space+  //~   -- need to use DeviceNRecoder+  if (level < psLevel2) {+    useRLE = gFalse;+    useCompressed = gFalse;+    useASCIIHex = gTrue;+  } else {+    s = str->getPSFilter(level < psLevel3 ? 2 : 3, "");+    if (s) {+      useRLE = gFalse;+      useCompressed = gTrue;+      delete s;+    } else {+      useRLE = gTrue;+      useCompressed = gFalse;+    }+    useASCIIHex = level == psLevel1 || level == psLevel1Sep ||+                  globalParams->getPSASCIIHex();+  }+  if (useCompressed) {+    str = str->getUndecodedStream();+  }+  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 RunLengthDecode filter may read past the end+  ++size;+  if (useRLE) {+    ++size;+  }+  writePSFmt("{0:d} array dup /ImData_{1:d}_{2:d} exch def\n",+	     size, 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(c);+      ++col;+    } else {+      writePSChar(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(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 (useRLE) {+    ++line;+    writePSFmt("{0:d} <> put\n", line);+  } else {+    writePS("pop\n");+  }+  str->close();++  delete str;+}++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.getRef(), &xObj);+	  } else {+	    error(-1, "Form in resource dict is not an indirect reference");+	  }+	}+	subtypeObj.free();+      }+      xObj.free();+      xObjRef.free();+    }+  }+  xObjDict.free();+}++void PSOutputDev::setupForm(Ref id, 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 == id.num && formIDs[i].gen == id.gen) {+      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++] = id;++  dict = strObj->streamGetDict();++  // get bounding box+  dict->lookup("BBox", &bboxObj);+  if (!bboxObj.isArray()) {+    bboxObj.free();+    error(-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", id.num, id.gen);+  writePS("q\n");+  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] 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(xref, this, resDict, &box, &box);+  gfx->display(strObj);+  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, Catalog *catalog,+				  GBool (*abortCheckCbk)(void *data),+				  void *abortCheckCbkData) {+#if HAVE_SPLASH+  PreScanOutputDev *scan;+  GBool rasterize;+  SplashOutputDev *splashOut;+  SplashColor paperColor;+  PDFRectangle box;+  GfxState *state;+  SplashBitmap *bitmap;+  Stream *str0, *str;+  Object obj;+  Guchar *p;+  Guchar col[4];+  double m0, m1, m2, m3, m4, m5;+  int c, w, h, x, y, comp, i;++  scan = new PreScanOutputDev();+  page->displaySlice(scan, 72, 72, rotateA, useMediaBox, crop,+		     sliceX, sliceY, sliceW, sliceH,+		     printing, catalog, abortCheckCbk, abortCheckCbkData);+  rasterize = scan->usesTransparency();+  delete scan;+  if (!rasterize) {+    return gTrue;+  }++  // rasterize the page+  if (level == psLevel1) {+    paperColor[0] = 0xff;+    splashOut = new SplashOutputDev(splashModeMono8, 1, gFalse,+				    paperColor, gTrue, gFalse);+#if SPLASH_CMYK+  } else if (level == psLevel1Sep) {+    paperColor[0] = paperColor[1] = paperColor[2] = paperColor[3] = 0;+    splashOut = new SplashOutputDev(splashModeCMYK8, 1, gFalse,+				    paperColor, gTrue, gFalse);+#endif+  } else {+    paperColor[0] = paperColor[1] = paperColor[2] = 0xff;+    splashOut = new SplashOutputDev(splashModeRGB8, 1, gFalse,+				    paperColor, gTrue, gFalse);+  }+  splashOut->startDoc(xref);+  page->displaySlice(splashOut, splashDPI, splashDPI, rotateA,+		     useMediaBox, crop,+		     sliceX, sliceY, sliceW, sliceH,+		     printing, catalog, abortCheckCbk, abortCheckCbkData);++  // start the PS page+  page->makeBox(splashDPI, splashDPI, 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(splashDPI, splashDPI, &box, rotateA, gFalse);+  startPage(page->getNum(), state);+  delete state;+  switch (rotateA) {+  case 0:+  default:  // this should never happen+    m0 = box.x2 - box.x1;+    m1 = 0;+    m2 = 0;+    m3 = box.y2 - box.y1;+    m4 = box.x1;+    m5 = box.y1;+    break;+  case 90:+    m0 = 0;+    m1 = box.y2 - box.y1;+    m2 = -(box.x2 - box.x1);+    m3 = 0;+    m4 = box.x2;+    m5 = box.y1;+    break;+  case 180:+    m0 = -(box.x2 - box.x1);+    m1 = 0;+    m2 = 0;+    m3 = -(box.y2 - box.y1);+    m4 = box.x2;+    m5 = box.y2;+    break;+  case 270:+    m0 = 0;+    m1 = -(box.y2 - box.y1);+    m2 = box.x2 - box.x1;+    m3 = 0;+    m4 = box.x1;+    m5 = box.y2;+    break;+  }++  //~ need to add the process colors++  // draw the rasterized image+  bitmap = splashOut->getBitmap();+  w = bitmap->getWidth();+  h = bitmap->getHeight();+  writePS("gsave\n");+  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] 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();+    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();+    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 psLevel2Sep:+  case psLevel3:+  case psLevel3Sep:+    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");+    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");+    }+    writePS("    /RunLengthDecode filter\n");+    writePS(">>\n");+    writePS("image\n");+    obj.initNull();+    str0 = new MemStream((char *)bitmap->getDataPtr(), 0, w * h * 3, &obj);+    str = new RunLengthEncoder(str0);+    if (globalParams->getPSASCIIHex()) {+      str = new ASCIIHexEncoder(str);+    } else {+      str = new ASCII85Encoder(str);+    }+    str->reset();+    while ((c = str->getChar()) != EOF) {+      writePSChar(c);+    }+    str->close();+    delete str;+    delete str0;+    processColors |= psProcessCMYK;+    break;+  }+  delete splashOut;+  writePS("grestore\n");++  // finish the PS page+  endPage();++  return gFalse;+#else+  return gTrue;+#endif+}++void PSOutputDev::startPage(int pageNum, GfxState *state) {+  int x1, y1, x2, y2, width, height;+  int imgWidth, imgHeight, imgWidth2, imgHeight2;+  GBool landscape;+++  if (mode == psModePS) {+    writePSFmt("%%Page: {0:d} {1:d}\n", pageNum, seqPage);+    writePS("%%BeginPageSetup\n");+  }++  // 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 (rotate0 >= 0) {+      rotate = (360 - rotate0) % 360;+      landscape = gFalse;+    } else {+      rotate = (360 - state->getRotate()) % 360;+      if (rotate == 0 || rotate == 180) {+	if (width > height && width > imgWidth) {+	  rotate += 90;+	  landscape = gTrue;+	} else {+	  landscape = gFalse;+	}+      } else { // rotate == 90 || rotate == 270+	if (height > width && height > 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;+      yScale = yScale0;+    } else if ((globalParams->getPSShrinkLarger() &&+	 (width > imgWidth2 || height > imgHeight2)) ||+	(globalParams->getPSExpandSmaller() &&+	 (width < imgWidth2 && height < imgHeight2))) {+      xScale = (double)imgWidth2 / (double)width;+      yScale = (double)imgHeight2 / (double)height;+      if (yScale < xScale) {+	xScale = yScale;+      } else {+	yScale = xScale;+      }+    } else {+      xScale = yScale = 1;+    }+    // 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 ? tx0 : ty0;+      ty += rotate == 0 ? 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 ? imgLLX : imgLLY;+    ty += rotate == 0 ? imgLLY : -imgLLX;+    if (tx != 0 || ty != 0) {+      writePSFmt("{0:.4g} {1:.4g} translate\n", tx, ty);+    }+    if (xScale != 1 || yScale != 1) {+      writePSFmt("{0:.4f} {0:.4f} scale\n", xScale);+    }+    if (clipLLX0 < clipURX0 && clipLLY0 < clipURY0) {+      writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} 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);+    }++    writePS("%%EndPageSetup\n");+    ++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:.4g} {1:.4g} 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;+  }+}++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();+  }+}++void PSOutputDev::saveState(GfxState *state) {+  writePS("q\n");+  ++numSaves;+}++void PSOutputDev::restoreState(GfxState *state) {+  writePS("Q\n");+  --numSaves;+}++void PSOutputDev::updateCTM(GfxState *state, double m11, double m12,+			    double m21, double m22, double m31, double m32) {+  writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] cm\n",+	     m11, m12, m21, m22, m31, m32);+}++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:.4g}{1:w}",+	       dash[i] < 0 ? 0 : dash[i],+	       (i == length-1) ? 0 : 1);+  }+  writePSFmt("] {0:.4g} d\n", start);+}++void PSOutputDev::updateFlatness(GfxState *state) {+  writePSFmt("{0:d} i\n", state->getFlatness());+}++void PSOutputDev::updateLineJoin(GfxState *state) {+  writePSFmt("{0:d} j\n", state->getLineJoin());+}++void PSOutputDev::updateLineCap(GfxState *state) {+  writePSFmt("{0:d} J\n", state->getLineCap());+}++void PSOutputDev::updateMiterLimit(GfxState *state) {+  writePSFmt("{0:.4g} M\n", state->getMiterLimit());+}++void PSOutputDev::updateLineWidth(GfxState *state) {+  writePSFmt("{0:.4g} w\n", state->getLineWidth());+}++void PSOutputDev::updateFillColorSpace(GfxState *state) {+  switch (level) {+  case psLevel1:+  case psLevel1Sep:+    break;+  case psLevel2:+  case psLevel3:+    if (state->getFillColorSpace()->getMode() != csPattern) {+      dumpColorSpaceL2(state->getFillColorSpace(), gTrue, gFalse, gFalse);+      writePS(" cs\n");+    }+    break;+  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->getStrokeColorSpace(), gTrue, gFalse, gFalse);+      writePS(" CS\n");+    }+    break;+  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:+    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);+      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(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;+}++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:+    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);+      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(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;+}++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(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);+  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::updateFillOverprint(GfxState *state) {+  if (level >= psLevel2) {+    writePSFmt("{0:s} op\n", state->getFillOverprint() ? "true" : "false");+  }+}++void PSOutputDev::updateStrokeOverprint(GfxState *state) {+  if (level >= psLevel2) {+    writePSFmt("{0:s} OP\n", state->getStrokeOverprint() ? "true" : "false");+  }+}++void PSOutputDev::updateTransfer(GfxState *state) {+  Function **funcs;+  int i;++  funcs = state->getTransfer();+  if (funcs[0] && funcs[1] && funcs[2] && funcs[3]) {+    if (level >= psLevel2) {+      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");+  }+}++void PSOutputDev::updateFont(GfxState *state) {+  if (state->getFont()) {+    writePSFmt("/F{0:d}_{1:d} {2:.4g} Tf\n",+	       state->getFont()->getID()->num, state->getFont()->getID()->gen,+	       fabs(state->getFontSize()) < 0.00001 ? 0.00001+	                                            : state->getFontSize());+  }+}++void PSOutputDev::updateTextMat(GfxState *state) {+  double *mat;++  mat = state->getTextMat();+  if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.00001) {+    // avoid a singular (or close-to-singular) matrix+    writePSFmt("[0.00001 0 0 0.00001 {0:.4g} {1:.4g}] Tm\n", mat[4], mat[5]);+  } else {+    writePSFmt("[{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] Tm\n",+	       mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);+  }+}++void PSOutputDev::updateCharSpace(GfxState *state) {+  writePSFmt("{0:.4g} Tc\n", state->getCharSpace());+}++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;+  }+}++void PSOutputDev::updateRise(GfxState *state) {+  writePSFmt("{0:.4g} Ts\n", state->getRise());+}++void PSOutputDev::updateWordSpace(GfxState *state) {+  writePSFmt("{0:.4g} Tw\n", state->getWordSpace());+}++void PSOutputDev::updateHorizScaling(GfxState *state) {+  double h;++  h = state->getHorizScaling();+  if (fabs(h) < 0.01) {+    h = 0.01;+  }+  writePSFmt("{0:.4g} Tz\n", h);+}++void PSOutputDev::updateTextPos(GfxState *state) {+  writePSFmt("{0:.4g} {1:.4g} Td\n", state->getLineX(), state->getLineY());+}++void PSOutputDev::updateTextShift(GfxState *state, double shift) {+  if (state->getFont()->getWMode()) {+    writePSFmt("{0:.4g} TJmV\n", shift);+  } else {+    writePSFmt("{0:.4g} TJm\n", shift);+  }+}++void PSOutputDev::stroke(GfxState *state) {+  doPath(state->getPath());+  if (t3String) {+    // if we're construct a cacheable Type 3 glyph, we need to do+    // everything in the fill color+    writePS("Sf\n");+  } else {+    writePS("S\n");+  }+}++void PSOutputDev::fill(GfxState *state) {+  doPath(state->getPath());+  writePS("f\n");+}++void PSOutputDev::eoFill(GfxState *state) {+  doPath(state->getPath());+  writePS("f*\n");+}++void PSOutputDev::tilingPatternFill(GfxState *state, Object *str,+				    int paintType, Dict *resDict,+				    double *mat, double *bbox,+				    int x0, int y0, int x1, int y1,+				    double xStep, double yStep) {+  PDFRectangle box;+  Gfx *gfx;++  // 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:.4g} {1:.4g} {2:.4g} {3:.4g}] 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];+  gfx = new Gfx(xref, this, resDict, &box, NULL);+  writePS("/x {\n");+  if (paintType == 2) {+    writePSFmt("{0:.4g} 0 {1:.4g} {2:.4g} {3:.4g} {4:.4g} setcachedevice\n",+	       xStep, bbox[0], bbox[1], bbox[2], bbox[3]);+  } else {+    if (x1 - 1 <= x0) {+      writePS("1 0 setcharwidth\n");+    } else {+      writePSFmt("{0:.4g} 0 setcharwidth\n", xStep);+    }+  }+  inType3Char = gTrue;+  ++numTilingPatterns;+  gfx->display(str);+  --numTilingPatterns;+  inType3Char = gFalse;+  writePS("} def\n");+  delete gfx;+  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);+  writePSFmt("gsave [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] concat\n",+	     mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);+  writePSFmt("{0:d} 1 {1:d} {{ {2:.4g} exch {3:.4g} 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");+}++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:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g}] 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:.4g} {1:.4g} {2:.4g} {3:.4g} 0 funcSH\n", x0, y0, x1, y1);++  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:.4g} def\n", t0);+  writePSFmt("/t1 {0:.4g} def\n", t1);+  writePSFmt("/dt {0:.4g} def\n", t1 - t0);+  writePSFmt("/x0 {0:.4g} def\n", x0);+  writePSFmt("/y0 {0:.4g} def\n", y0);+  writePSFmt("/dx {0:.4g} def\n", x1 - x0);+  writePSFmt("/x1 {0:.4g} def\n", x1);+  writePSFmt("/y1 {0:.4g} def\n", y1);+  writePSFmt("/dy {0:.4g} def\n", y1 - y0);+  writePSFmt("/xMin {0:.4g} def\n", xMin);+  writePSFmt("/yMin {0:.4g} def\n", yMin);+  writePSFmt("/xMax {0:.4g} def\n", xMax);+  writePSFmt("/yMax {0:.4g} 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:.4g} {1:.4g} 0 axialSH\n", tMin, tMax);++  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 sz, xz, yz, sMin, sMax, sa, ta;+  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.+  if (r0 == r1) {+    enclosed = x0 == x1 && y0 == y1;+    theta = 0;+    sz = 0; // make gcc happy+  } else {+    sz = -r0 / (r1 - r0);+    xz = x0 + sz * (x1 - x0);+    yz = y0 + sz * (y1 - y0);+    enclosed = (xz - x0) * (xz - x0) + (yz - y0) * (yz - y0) <= r0 * r0;+    theta = asin(r0 / sqrt((x0 - xz) * (x0 - xz) + (y0 - yz) * (y0 - yz)));+    if (r0 > r1) {+      theta = -theta;+    }+  }+  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 {+    sMin = 1;+    sMax = 0;+    // solve for x(s) + r(s) = xMin+    if ((x1 + r1) - (x0 + r0) != 0) {+      sa = (xMin - (x0 + r0)) / ((x1 + r1) - (x0 + r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // solve for x(s) - r(s) = xMax+    if ((x1 - r1) - (x0 - r0) != 0) {+      sa = (xMax - (x0 - r0)) / ((x1 - r1) - (x0 - r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // solve for y(s) + r(s) = yMin+    if ((y1 + r1) - (y0 + r0) != 0) {+      sa = (yMin - (y0 + r0)) / ((y1 + r1) - (y0 + r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // solve for y(s) - r(s) = yMax+    if ((y1 - r1) - (y0 - r0) != 0) {+      sa = (yMax - (y0 - r0)) / ((y1 - r1) - (y0 - r0));+      if (sa < sMin) {+	sMin = sa;+      } else if (sa > sMax) {+	sMax = sa;+      }+    }+    // check against sz+    if (r0 < r1) {+      if (sMin < sz) {+	sMin = sz;+      }+    } else if (r0 > r1) {+      if (sMax > sz) {+	sMax = sz;+      }+    }+    // check the 'extend' flags+    if (!shading->getExtend0() && sMin < 0) {+      sMin = 0;+    }+    if (!shading->getExtend1() && sMax > 1) {+      sMax = 1;+    }+  }++  // generate the PS code+  writePSFmt("/x0 {0:.4g} def\n", x0);+  writePSFmt("/x1 {0:.4g} def\n", x1);+  writePSFmt("/dx {0:.4g} def\n", x1 - x0);+  writePSFmt("/y0 {0:.4g} def\n", y0);+  writePSFmt("/y1 {0:.4g} def\n", y1);+  writePSFmt("/dy {0:.4g} def\n", y1 - y0);+  writePSFmt("/r0 {0:.4g} def\n", r0);+  writePSFmt("/r1 {0:.4g} def\n", r1);+  writePSFmt("/dr {0:.4g} def\n", r1 - r0);+  writePSFmt("/t0 {0:.4g} def\n", t0);+  writePSFmt("/t1 {0:.4g} def\n", t1);+  writePSFmt("/dt {0:.4g} 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:.4g} def\n", a1);+  writePSFmt("/a2 {0:.4g} 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:.4g} {1:.4g} 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:.4g} radialCol aload pop k\n", ta);+      } else {+	writePSFmt("{0:.4g} radialCol sc\n", ta);+      }+      writePSFmt("{0:.4g} {1:.4g} {2:.4g} 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:.4g} radialCol aload pop k\n", ta);+      } else {+	writePSFmt("{0:.4g} radialCol sc\n", ta);+      }+      writePSFmt("{0:.4g} {1:.4g} {2:.4g} 0 360 arc h\n", xa, ya, ra);+      writePSFmt("{0:.4g} {1:.4g} m {2:.4g} {3:.4g} l {4:.4g} {5:.4g} l {6:.4g} {7:.4g} l h f*\n",+		 xMin, yMin, xMin, yMax, xMax, yMax, xMax, yMin);+    }+  }++  return gTrue;+}++void PSOutputDev::clip(GfxState *state) {+  doPath(state->getPath());+  writePS("W\n");+}++void PSOutputDev::eoClip(GfxState *state) {+  doPath(state->getPath());+  writePS("W*\n");+}++void PSOutputDev::clipToStrokePath(GfxState *state) {+  doPath(state->getPath());+  writePS("Ws\n");+}++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:.4g} {1:.4g} {2:.4g} {3:.4g} 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:.4g} {1:.4g} {2:.4g} {3:.4g} 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:.4g} {1:.4g} m\n", subpath->getX(0), subpath->getY(0));+    j = 1;+    while (j < m) {+      if (subpath->getCurve(j)) {+	writePSFmt("{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} 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:.4g} {1:.4g} 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;+  Gushort *codeToGID;+  GString *s2;+  double dx, dy, dx2, dy2, originX, originY;+  char *p;+  UnicodeMap *uMap;+  CharCode code;+  Unicode u[8];+  char buf[8];+  int 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();++  // check for a subtitute 16-bit font+  uMap = NULL;+  codeToGID = NULL;+  if (font->isCIDFont()) {+    for (i = 0; i < font16EncLen; ++i) {+      if (font->getID()->num == font16Enc[i].fontID.num &&+	  font->getID()->gen == font16Enc[i].fontID.gen) {+	uMap = globalParams->getUnicodeMap(font16Enc[i].enc);+	break;+      }+    }++  // check for a code-to-GID map+  } else {+    for (i = 0; i < font8InfoLen; ++i) {+      if (font->getID()->num == font8Info[i].fontID.num &&+	  font->getID()->gen == font8Info[i].fontID.gen) {+	codeToGID = font8Info[i].codeToGID;+	break;+      }+    }+  }++  // compute width of chars in string, ignoring char spacing and word+  // spacing -- the Tj operator will adjust for the metrics of the+  // font that's actually used+  dx = dy = 0;+  nChars = 0;+  p = s->getCString();+  len = s->getLength();+  s2 = new GString();+  while (len > 0) {+    n = font->getNextChar(p, len, &code,+			  u, (int)(sizeof(u) / sizeof(Unicode)), &uLen,+			  &dx2, &dy2, &originX, &originY);+    if (font->isCIDFont()) {+      if (uMap) {+	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+	nChars += uLen;+      } else {+	s2->append((char)((code >> 8) & 0xff));+	s2->append((char)(code & 0xff));+	++nChars;+      }+    } else {+      if (!codeToGID || codeToGID[code]) {+	s2->append((char)code);+      }+    }+    dx += dx2;+    dy += dy2;+    p += n;+    len -= n;+  }+  dx *= state->getFontSize() * state->getHorizScaling();+  dy *= state->getFontSize();+  if (uMap) {+    uMap->decRefCnt();+  }++  if (s2->getLength() > 0) {+    writePSString(s2);+    if (font->isCIDFont()) {+      if (wMode) {+	writePSFmt(" {0:d} {1:.4g} Tj16V\n", nChars, dy);+      } else {+	writePSFmt(" {0:d} {1:.4g} Tj16\n", nChars, dx);+      }+    } else {+      writePSFmt(" {0:.4g} Tj\n", dx);+    }+  }+  delete s2;++  if (state->getRender() & 4) {+    haveTextClip = gTrue;+  }+}++void PSOutputDev::endTextObject(GfxState *state) {+  if (haveTextClip) {+    writePS("Tclip\n");+    haveTextClip = gFalse;+  }+}++void PSOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,+				int width, int height, GBool invert,+				GBool inlineImg) {+  int len;++  len = height * ((width + 7) / 8);+  switch (level) {+  case psLevel1:+  case psLevel1Sep:+    doImageL1(ref, NULL, invert, inlineImg, str, width, height, len);+    break;+  case psLevel2:+  case psLevel2Sep:+    doImageL2(ref, NULL, invert, inlineImg, str, width, height, len,+	      NULL, NULL, 0, 0, gFalse);+    break;+  case psLevel3:+  case psLevel3Sep:+    doImageL3(ref, NULL, invert, inlineImg, str, width, height, len,+	      NULL, NULL, 0, 0, gFalse);+    break;+  }+}++void PSOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,+			    int width, int height, GfxImageColorMap *colorMap,+			    int *maskColors, GBool inlineImg) {+  int len;++  len = height * ((width * colorMap->getNumPixelComps() *+		   colorMap->getBits() + 7) / 8);+  switch (level) {+  case psLevel1:+    doImageL1(ref, colorMap, gFalse, inlineImg, str, width, height, len);+    break;+  case psLevel1Sep:+    //~ handle indexed, separation, ... color spaces+    doImageL1Sep(colorMap, gFalse, inlineImg, str, width, height, len);+    break;+  case psLevel2:+  case psLevel2Sep:+    doImageL2(ref, colorMap, gFalse, inlineImg, str,+	      width, height, len, maskColors, NULL, 0, 0, gFalse);+    break;+  case psLevel3:+  case psLevel3Sep:+    doImageL3(ref, colorMap, gFalse, inlineImg, str,+	      width, height, len, maskColors, NULL, 0, 0, gFalse);+    break;+  }+  t3Cacheable = gFalse;+}++void PSOutputDev::drawMaskedImage(GfxState *state, Object *ref, Stream *str,+				  int width, int height,+				  GfxImageColorMap *colorMap,+				  Stream *maskStr,+				  int maskWidth, int maskHeight,+				  GBool maskInvert) {+  int len;++  len = height * ((width * colorMap->getNumPixelComps() *+		   colorMap->getBits() + 7) / 8);+  switch (level) {+  case psLevel1:+    doImageL1(ref, colorMap, gFalse, gFalse, str, width, height, len);+    break;+  case psLevel1Sep:+    //~ handle indexed, separation, ... color spaces+    doImageL1Sep(colorMap, gFalse, gFalse, str, width, height, len);+    break;+  case psLevel2:+  case psLevel2Sep:+    doImageL2(ref, colorMap, gFalse, gFalse, str, width, height, len,+	      NULL, maskStr, maskWidth, maskHeight, maskInvert);+    break;+  case psLevel3:+  case psLevel3Sep:+    doImageL3(ref, colorMap, gFalse, gFalse, str, width, height, len,+	      NULL, maskStr, maskWidth, maskHeight, maskInvert);+    break;+  }+  t3Cacheable = gFalse;+}++void PSOutputDev::doImageL1(Object *ref, 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(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);+	  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(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 *)gmalloc(4 * width);++  // 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);+      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, 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;+  ImageStream *imgStr;+  Guchar *line;+  PSOutImgClipRect *rects0, *rects1, *rectsTmp, *rectsOut;+  int rects0Len, rects1Len, rectsSize, rectsOutLen, rectsOutSize;+  GBool emitRect, addRect, extendRect;+  GString *s;+  int n, numComps;+  GBool useRLE, useASCII, useASCIIHex, useCompressed;+  GfxSeparationColorSpace *sepCS;+  GfxColor color;+  GfxCMYK cmyk;+  int c;+  int col, i, j, x0, x1, y, maskXor;++  // 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+    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} array 0\n", rectsOutLen * 4);+    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);+    }+    writePSFmt("pop {0:d} {1:d} pdfImClip\n", width, height);+    gfree(rectsOut);+    gfree(rects0);+    gfree(rects1);+    delete imgStr;+    str->close();++  // explicit masking+  } else if (maskStr) {+    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} array 0\n", rectsOutLen * 4);+    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);+    }+    writePSFmt("pop {0:d} {1:d} pdfImClip\n", maskWidth, maskHeight);+    gfree(rectsOut);+    gfree(rects0);+    gfree(rects1);+    delete imgStr;+    maskStr->close();+  }++  // color space+  if (colorMap) {+    dumpColorSpaceL2(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);+      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(c);+	  ++col;+	} else {+	  writePSChar(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(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 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) {+    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 (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 { 2 copy get exch 1 add exch }\n");+  } else {+    writePS("  /DataSource currentfile\n");+  }++  // filters+  s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,+		       "    ");+  if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||+      inlineImg || !s) {+    useRLE = gTrue;+    useASCII = !(mode == psModeForm || inType3Char || preload);+    useCompressed = gFalse;+  } else {+    useRLE = gFalse;+    useASCII = str->isBinary() &&+               !(mode == psModeForm || inType3Char || preload);+    useCompressed = gTrue;+  }+  if (useASCII) {+    writePSFmt("    /ASCII{0:s}Decode filter\n",+	       useASCIIHex ? "Hex" : "85");+  }+  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 DeviceN data+    if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {+      str = new DeviceNRecoder(str, width, height, colorMap);+    }++    // add RunLengthEncode and ASCIIHex/85 encode filters+    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(-1, "Internal: OPI in inline image");+	n = 0;+      } else {+	// need to read the stream to count characters -- the length+	// is data-dependent (because of ASCII and RLE filters)+	str->reset();+	n = 0;+	while ((c = str->getChar()) != EOF) {+	  ++n;+	}+	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);+      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 ((c = str->getChar()) != EOF) {+      writePSChar(c);+    }+    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 (useRLE || useASCII || inlineImg) {+      delete str;+    }+  }++  if ((maskColors && colorMap && !inlineImg) || maskStr) {+    writePS("pdfImClipEnd\n");+  }+}++//~ this doesn't currently support OPI+void PSOutputDev::doImageL3(Object *ref, 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 useRLE, useASCII, useASCIIHex, useCompressed;+  GBool maskUseRLE, maskUseASCII, maskUseCompressed;+  GfxSeparationColorSpace *sepCS;+  GfxColor color;+  GfxCMYK cmyk;+  int c;+  int col, i;++  useASCIIHex = globalParams->getPSASCIIHex();+  useRLE = useASCII = useCompressed = gFalse; // make gcc happy+  maskUseRLE = maskUseASCII = maskUseCompressed = gFalse; // make gcc happy++  // color space+  if (colorMap) {+    dumpColorSpaceL2(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);+      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(c);+	  ++col;+	} else {+	  writePSChar(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(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 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) {+    writePS("<<\n  /ImageType 3\n");+    writePS("  /InterleaveType 3\n");+    writePS("  /DataDict\n");+  }++  // image (data) dictionary+  writePSFmt("<<\n  /ImageType {0:d}\n", (maskColors && colorMap) ? 4 : 1);++  // color key masking+  if (maskColors && colorMap) {+    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 && colorMap->getColorSpace()->getMode() == csDeviceN) {+    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 (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 { 2 copy get exch 1 add exch }\n");+  } else {+    writePS("  /DataSource currentfile\n");+  }++  // filters+  s = str->getPSFilter(level < psLevel2 ? 1 : level < psLevel3 ? 2 : 3,+		       "    ");+  if ((colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) ||+      inlineImg || !s) {+    useRLE = gTrue;+    useASCII = !(mode == psModeForm || inType3Char || preload);+    useCompressed = gFalse;+  } else {+    useRLE = gFalse;+    useASCII = str->isBinary() &&+               !(mode == psModeForm || inType3Char || preload);+    useCompressed = gTrue;+  }+  if (useASCII) {+    writePSFmt("    /ASCII{0:s}Decode filter\n",+	       useASCIIHex ? "Hex" : "85");+  }+  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) {+    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+    writePS("  /DataSource currentfile\n");+    s = maskStr->getPSFilter(3, "    ");+    if (!s) {+      maskUseRLE = gTrue;+      maskUseASCII = gTrue;+      maskUseCompressed = gFalse;+    } else {+      maskUseRLE = gFalse;+      maskUseASCII = maskStr->isBinary();+      maskUseCompressed = gTrue;+    }+    if (maskUseASCII) {+      writePSFmt("    /ASCII{0:s}Decode filter\n",+		 useASCIIHex ? "Hex" : "85");+    }+    if (maskUseRLE) {+      writePS("    /RunLengthDecode filter\n");+    }+    if (maskUseCompressed) {+      writePS(s->getCString());+    }+    if (s) {+      delete s;+    }++    writePS(">>\n");+    writePS(">>\n");+  }++  if (mode == psModeForm || inType3Char || preload) {++    // image command+    writePSFmt("{0:s}\n", colorMap ? "image" : "imagemask");++  } else {++    if ((level == psLevel2Sep || level == psLevel3Sep) && colorMap &&+	colorMap->getColorSpace()->getMode() == csSeparation) {+      color.c[0] = gfxColorComp1;+      sepCS = (GfxSeparationColorSpace *)colorMap->getColorSpace();+      sepCS->getCMYK(&color, &cmyk);+      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");+    }++  }++  // explicit masking+  if (maskStr) {++    if (maskUseCompressed) {+      maskStr = maskStr->getUndecodedStream();+    }++    // add RunLengthEncode and ASCIIHex/85 encode filters+    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 ((c = maskStr->getChar()) != EOF) {+      writePSChar(c);+    }+    maskStr->close();+    writePSChar('\n');++    // delete encoders+    if (maskUseRLE || maskUseASCII) {+      delete maskStr;+    }+  }++  // 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 DeviceN data+    if (colorMap && colorMap->getColorSpace()->getMode() == csDeviceN) {+      str = new DeviceNRecoder(str, width, height, colorMap);+    }++    // add RunLengthEncode and ASCIIHex/85 encode filters+    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 ((c = str->getChar()) != EOF) {+      writePSChar(c);+    }+    str->close();++    // add newline and trailer to the end+    writePSChar('\n');+    writePS("%-EOD-\n");++    // delete encoders+    if (useRLE || useASCII || inlineImg) {+      delete str;+    }+  }+}++void PSOutputDev::dumpColorSpaceL2(GfxColorSpace *colorSpace,+				   GBool genXform, GBool updateColors,+				   GBool map01) {+  GfxCalGrayColorSpace *calGrayCS;+  GfxCalRGBColorSpace *calRGBCS;+  GfxLabColorSpace *labCS;+  GfxIndexedColorSpace *indexedCS;+  GfxSeparationColorSpace *separationCS;+  GfxDeviceNColorSpace *deviceNCS;+  GfxColorSpace *baseCS;+  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;++  switch (colorSpace->getMode()) {++  case csDeviceGray:+    writePS("/DeviceGray");+    if (genXform) {+      writePS(" {}");+    }+    if (updateColors) {+      processColors |= psProcessBlack;+    }+    break;++  case csCalGray:+    calGrayCS = (GfxCalGrayColorSpace *)colorSpace;+    writePS("[/CIEBasedA <<\n");+    writePSFmt(" /DecodeA {{{0:.4g} exp}} bind\n", calGrayCS->getGamma());+    writePSFmt(" /MatrixA [{0:.4g} {1:.4g} {2:.4g}]\n",+	       calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),+	       calGrayCS->getWhiteZ());+    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",+	       calGrayCS->getWhiteX(), calGrayCS->getWhiteY(),+	       calGrayCS->getWhiteZ());+    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",+	       calGrayCS->getBlackX(), calGrayCS->getBlackY(),+	       calGrayCS->getBlackZ());+    writePS(">>]");+    if (genXform) {+      writePS(" {}");+    }+    if (updateColors) {+      processColors |= psProcessBlack;+    }+    break;++  case csDeviceRGB:+    writePS("/DeviceRGB");+    if (genXform) {+      writePS(" {}");+    }+    if (updateColors) {+      processColors |= psProcessCMYK;+    }+    break;++  case csCalRGB:+    calRGBCS = (GfxCalRGBColorSpace *)colorSpace;+    writePS("[/CIEBasedABC <<\n");+    writePSFmt(" /DecodeABC [{{{0:.4g} exp}} bind {{{1:.4g} exp}} bind {{{2:.4g} exp}} bind]\n",+	       calRGBCS->getGammaR(), calRGBCS->getGammaG(),+	       calRGBCS->getGammaB());+    writePSFmt(" /MatrixABC [{0:.4g} {1:.4g} {2:.4g} {3:.4g} {4:.4g} {5:.4g} {6:.4g} {7:.4g} {8:.4g}]\n",+	       calRGBCS->getMatrix()[0], calRGBCS->getMatrix()[1],+	       calRGBCS->getMatrix()[2], calRGBCS->getMatrix()[3],+	       calRGBCS->getMatrix()[4], calRGBCS->getMatrix()[5],+	       calRGBCS->getMatrix()[6], calRGBCS->getMatrix()[7],+	       calRGBCS->getMatrix()[8]);+    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",+	       calRGBCS->getWhiteX(), calRGBCS->getWhiteY(),+	       calRGBCS->getWhiteZ());+    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",+	       calRGBCS->getBlackX(), calRGBCS->getBlackY(),+	       calRGBCS->getBlackZ());+    writePS(">>]");+    if (genXform) {+      writePS(" {}");+    }+    if (updateColors) {+      processColors |= psProcessCMYK;+    }+    break;++  case csDeviceCMYK:+    writePS("/DeviceCMYK");+    if (genXform) {+      writePS(" {}");+    }+    if (updateColors) {+      processColors |= psProcessCMYK;+    }+    break;++  case csLab:+    labCS = (GfxLabColorSpace *)colorSpace;+    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",+		 (labCS->getAMax() - labCS->getAMin()) / 500.0,+		 labCS->getAMin() / 500.0,+		 (labCS->getBMax() - labCS->getBMin()) / 200.0,+		 labCS->getBMin() / 200.0);+    } else {+      writePSFmt(" /RangeABC [0 100 {0:.4g} {1:.4g} {2:.4g} {3:.4g}]\n",+		 labCS->getAMin(), labCS->getAMax(),+		 labCS->getBMin(), labCS->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",+	       labCS->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",+	       labCS->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",+	       labCS->getWhiteZ());+    writePSFmt(" /WhitePoint [{0:.4g} {1:.4g} {2:.4g}]\n",+	       labCS->getWhiteX(), labCS->getWhiteY(), labCS->getWhiteZ());+    writePSFmt(" /BlackPoint [{0:.4g} {1:.4g} {2:.4g}]\n",+	       labCS->getBlackX(), labCS->getBlackY(), labCS->getBlackZ());+    writePS(">>]");+    if (genXform) {+      writePS(" {}");+    }+    if (updateColors) {+      processColors |= psProcessCMYK;+    }+    break;++  case csICCBased:+    // there is no transform function to the alternate color space, so+    // we can use it directly+    dumpColorSpaceL2(((GfxICCBasedColorSpace *)colorSpace)->getAlt(),+		     genXform, updateColors, gFalse);+    break;++  case csIndexed:+    indexedCS = (GfxIndexedColorSpace *)colorSpace;+    baseCS = indexedCS->getBase();+    writePS("[/Indexed ");+    dumpColorSpaceL2(baseCS, gFalse, gFalse, gTrue);+    n = indexedCS->getIndexHigh();+    numComps = baseCS->getNComps();+    lookup = indexedCS->getLookup();+    writePSFmt(" {0:d} <\n", n);+    if (baseCS->getMode() == csDeviceN) {+      func = ((GfxDeviceNColorSpace *)baseCS)->getTintTransformFunc();+      baseCS->getDefaultRanges(low, range, indexedCS->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);+	    indexedCS->getCMYK(&color, &cmyk);+	    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);+	    indexedCS->getCMYK(&color, &cmyk);+	    addProcessColor(colToDbl(cmyk.c), colToDbl(cmyk.m),+			    colToDbl(cmyk.y), colToDbl(cmyk.k));+	  }+	}+	writePS("\n");+      }+    }+    writePS(">]");+    if (genXform) {+      writePS(" {}");+    }+    break;++  case csSeparation:+    separationCS = (GfxSeparationColorSpace *)colorSpace;+    writePS("[/Separation ");+    writePSString(separationCS->getName());+    writePS(" ");+    dumpColorSpaceL2(separationCS->getAlt(), gFalse, gFalse, gFalse);+    writePS("\n");+    cvtFunction(separationCS->getFunc());+    writePS("]");+    if (genXform) {+      writePS(" {}");+    }+    if (updateColors) {+      addCustomColor(separationCS);+    }+    break;++  case csDeviceN:+    // DeviceN color spaces are a Level 3 PostScript feature.+    deviceNCS = (GfxDeviceNColorSpace *)colorSpace;+    dumpColorSpaceL2(deviceNCS->getAlt(), gFalse, updateColors, map01);+    if (genXform) {+      writePS(" ");+      cvtFunction(deviceNCS->getTintTransformFunc());+    }+    break;++  case csPattern:+    //~ unimplemented+    break;+  }+}++#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:.4g} {1:.4g}\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:.4g} {1:.4g} {2:.4g} {3:.4g}\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:.4g} {1:.4g} setcharwidth\n", wx, wy);+  writePS("q\n");+  t3NeedsRestore = gTrue;+}++void PSOutputDev::type3D1(GfxState *state, double wx, double wy,+			  double llx, double lly, double urx, double ury) {+  t3WX = wx;+  t3WY = wy;+  t3LLX = llx;+  t3LLY = lly;+  t3URX = urx;+  t3URY = ury;+  t3String = new GString();+  writePS("q\n");+  t3Cacheable = gTrue;+  t3NeedsRestore = gTrue;+}++void PSOutputDev::drawForm(Ref id) {+  writePSFmt("f_{0:d}_{1:d}\n", id.num, id.gen);+}++void PSOutputDev::psXObject(Stream *psStream, Stream *level1Stream) {+  Stream *str;+  int c;++  if ((level == psLevel1 || level == psLevel1Sep) && level1Stream) {+    str = level1Stream;+  } else {+    str = psStream;+  }+  str->reset();+  while ((c = str->getChar()) != EOF) {+    writePSChar(c);+  }+  str->close();+}++//~ 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:.4g}\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:.4g} sub {1:.4g} mul {2:.4g} 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:.4g} mul {1:.4g} add\n",+		 func0->getDecodeMax(i) - func0->getDecodeMin(i),+		 func0->getDecodeMin(i));+      writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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:.4g} exp {2:.4g} mul {3:.4g} add\n",+		 i, func2->getE(), func2->getC1()[i] - func2->getC0()[i],+		 func2->getC0()[i]);+      if (func2->getHasRange()) {+	writePSFmt("dup {0:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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:.4g} lt {{ pop {1:.4g} }} {{ dup {2:.4g} gt {{ pop {3:.4g} }} 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:.4g} lt {{ {1:.4g} sub {2:.4g} mul {3:.4g} 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:.4g} sub {1:.4g} mul {2:.4g} 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::writePS(char *s) {+  if (t3String) {+    t3String->append(s);+  } else {+    (*outputFunc)(outputStream, s, 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) {+      sprintf(buf, "\\%03o", *p);+      writePS(buf);+      line += 4;+    } else {+      writePSChar((char)*p);+      ++line;+    }+  }+  writePSChar(')');+}++void PSOutputDev::writePSName(char *s) {+  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 == '%') {+      sprintf(buf, "#%02x", c & 0xff);+      name2->append(buf);+    } else {+      name2->append(c);+    }+  }+  return name2;+}++// Write a DSC-compliant <textline>.+void PSOutputDev::writePSTextLine(GString *s) {+  int i, j, step;+  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+  if (s->getLength() >= 2 &&+      (s->getChar(0) & 0xff) == 0xfe &&+      (s->getChar(1) & 0xff) == 0xff) {+    i = 3;+    step = 2;+  } else {+    i = 0;+    step = 1;+  }+  for (j = 0; i < s->getLength() && j < 200; i += step) {+    c = s->getChar(i) & 0xff;+    if (c == '\\') {+      writePS("\\\\");+      j += 2;+    } else if (c < 0x20 || c > 0x7e || (j == 0 && c == '(')) {+      writePSFmt("\\{0:03o}", c);+      j += 4;+    } else {+      writePSChar(c);+      ++j;+    }+  }+  writePS("\n");+}
+ xpdf/PSOutputDev.h view
@@ -0,0 +1,395 @@+//========================================================================+//+// 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 Function;+class GfxPath;+class GfxFont;+class GfxColorSpace;+class GfxSeparationColorSpace;+class PDFRectangle;+struct PSFont8Info;+struct PSFont16Enc;+class PSOutCustomColor;++//------------------------------------------------------------------------+// 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+};++typedef void (*PSOutputFunc)(void *stream, char *data, int len);++class PSOutputDev: public OutputDev {+public:++  // Open a PostScript output file, and write the prolog.+  PSOutputDev(char *fileName, XRef *xrefA, Catalog *catalog,+	      int firstPage, int lastPage, PSOutMode modeA,+	      int imgLLXA = 0, int imgLLYA = 0,+	      int imgURXA = 0, int imgURYA = 0,+	      GBool manualCtrlA = gFalse);++  // Open a PSOutputDev that will write to a generic stream.+  PSOutputDev(PSOutputFunc outputFuncA, void *outputStreamA,+	      XRef *xrefA, Catalog *catalog,+	      int firstPage, int lastPage, PSOutMode modeA,+	      int imgLLXA = 0, int imgLLYA = 0,+	      int imgURXA = 0, int imgURYA = 0,+	      GBool manualCtrlA = gFalse);++  // Destructor -- writes the trailer and closes the file.+  virtual ~PSOutputDev();++  // 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 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 functionShadedFill(), axialShadedFill(), and+  // radialShadedFill()?  If this returns false, these shaded fills+  // will be reduced to a series of other drawing operations.+  virtual GBool useShadedFills()+    { return level >= psLevel2; }++  // 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(int firstPage, int lastPage,+		   PDFRectangle *mediaBox, PDFRectangle *cropBox,+		   int pageRotate);++  // Write the Xpdf procset.+  void writeXpdfProcset();++  // Write the document-level setup.+  void writeDocSetup(Catalog *catalog, int firstPage, int lastPage);++  // 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, Catalog *catalog,+			       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 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);++  //----- path painting+  virtual void stroke(GfxState *state);+  virtual void fill(GfxState *state);+  virtual void eoFill(GfxState *state);+  virtual void tilingPatternFill(GfxState *state, Object *str,+				 int paintType, Dict *resDict,+				 double *mat, double *bbox,+				 int x0, int y0, int x1, int y1,+				 double xStep, double yStep);+  virtual GBool functionShadedFill(GfxState *state,+				   GfxFunctionShading *shading);+  virtual GBool axialShadedFill(GfxState *state, GfxAxialShading *shading);+  virtual GBool radialShadedFill(GfxState *state, GfxRadialShading *shading);++  //----- 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);+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,+			 int width, int height, GfxImageColorMap *colorMap,+			 int *maskColors, GBool inlineImg);+  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,+			       int width, int height,+			       GfxImageColorMap *colorMap,+			       Stream *maskStr, int maskWidth, int maskHeight,+			       GBool maskInvert);++#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 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; }++private:++  void init(PSOutputFunc outputFuncA, void *outputStreamA,+	    PSFileType fileTypeA, XRef *xrefA, Catalog *catalog,+	    int firstPage, int lastPage, PSOutMode modeA,+	    int imgLLXA, int imgLLYA, int imgURXA, int imgURYA,+	    GBool manualCtrlA);+  void setupResources(Dict *resDict);+  void setupFonts(Dict *resDict);+  void setupFont(GfxFont *font, Dict *parentResDict);+  void setupEmbeddedType1Font(Ref *id, GString *psName);+  void setupExternalType1Font(GString *fileName, GString *psName);+  void setupEmbeddedType1CFont(GfxFont *font, Ref *id, GString *psName);+  void setupEmbeddedOpenTypeT1CFont(GfxFont *font, Ref *id, GString *psName);+  void setupEmbeddedTrueTypeFont(GfxFont *font, Ref *id, GString *psName);+  void setupExternalTrueTypeFont(GfxFont *font, GString *psName);+  void setupEmbeddedCIDType0Font(GfxFont *font, Ref *id, GString *psName);+  void setupEmbeddedCIDTrueTypeFont(GfxFont *font, Ref *id, GString *psName,+				    GBool needVerticalMetrics);+  void setupEmbeddedOpenTypeCFFFont(GfxFont *font, Ref *id, GString *psName);+  void setupType3Font(GfxFont *font, GString *psName, Dict *parentResDict);+  void setupImages(Dict *resDict);+  void setupImage(Ref id, Stream *str);+  void setupForms(Dict *resDict);+  void setupForm(Ref id, Object *strObj);+  void addProcessColor(double c, double m, double y, double k);+  void addCustomColor(GfxSeparationColorSpace *sepCS);+  void doPath(GfxPath *path);+  void doImageL1(Object *ref, GfxImageColorMap *colorMap,+		 GBool invert, GBool inlineImg,+		 Stream *str, int width, int height, int len);+  void doImageL1Sep(GfxImageColorMap *colorMap,+		    GBool invert, GBool inlineImg,+		    Stream *str, int width, int height, int len);+  void doImageL2(Object *ref, 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 doImageL3(Object *ref, 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(GfxColorSpace *colorSpace,+			GBool genXform, GBool updateColors,+			GBool map01);+#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);+  void writePSChar(char c);+  void writePS(char *s);+  void writePSFmt(const char *fmt, ...);+  void writePSString(GString *s);+  void writePSName(char *s);+  GString *filterPSName(GString *name);+  void writePSTextLine(GString *s);++  PSLevel level;		// PostScript level (1, 2, separation)+  PSOutMode mode;		// PostScript mode (PS, EPS, form)+  int paperWidth;		// width of paper, in pts+  int paperHeight;		// height of paper, in pts+  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;++  XRef *xref;			// the xref table for this PDF file++  Ref *fontIDs;			// list of object IDs of all used fonts+  int fontIDLen;		// number of entries in fontIDs array+  int fontIDSize;		// size of fontIDs array+  Ref *fontFileIDs;		// list of object IDs of all embedded fonts+  int fontFileIDLen;		// number of entries in fontFileIDs array+  int fontFileIDSize;		// size of fontFileIDs array+  GString **fontFileNames;	// list of names of all embedded external fonts+  int fontFileNameLen;		// number of entries in fontFileNames array+  int fontFileNameSize;		// size of fontFileNames array+  int nextTrueTypeNum;		// next unique number to append to a TrueType+				//   font name+  PSFont8Info *font8Info;	// info for 8-bit fonts+  int font8InfoLen;		// number of entries in font8Info array+  int font8InfoSize;		// size of font8Info array+  PSFont16Enc *font16Enc;	// encodings for substitute 16-bit fonts+  int font16EncLen;		// number of entries in font16Enc array+  int font16EncSize;		// size of font16Enc array+  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+  GList *xobjStack;		// stack of XObject dicts currently being+				//   processed+  int numSaves;			// current number of gsaves+  int numTilingPatterns;	// current number of nested tiling patterns+  int nextFunc;			// next unique number to use for a function++  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 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
+ xpdf/PSTokenizer.cc view
@@ -0,0 +1,135 @@+//========================================================================+//+// 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 "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++] = c;+  if (c == '(') {+    backslash = gFalse;+    while ((c = lookChar()) != EOF) {+      if (i < size - 1) {+	buf[i++] = 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++] = c;+      }+      if (c == '>') {+	break;+      }+    }+  } else if (c != '[' && c != ']') {+    while ((c = lookChar()) != EOF && !specialChars[c]) {+      getChar();+      if (i < size - 1) {+	buf[i++] = 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;+}
+ xpdf/PSTokenizer.h view
@@ -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
+ xpdf/Page.cc view
@@ -0,0 +1,441 @@+//========================================================================+//+// Page.cc+//+// Copyright 1996-2007 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stddef.h>+#include "GlobalParams.h"+#include "Object.h"+#include "Array.h"+#include "Dict.h"+#include "XRef.h"+#include "Link.h"+#include "OutputDev.h"+#ifndef PDF_PARSER_ONLY+#include "Gfx.h"+#include "GfxState.h"+#include "Annot.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);++  // clip all other boxes to the media box+  cropBox.clipTo(&mediaBox);+  bleedBox.clipTo(&mediaBox);+  trimBox.clipTo(&mediaBox);+  artBox.clipTo(&mediaBox);++  // 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);++  // resource dictionary+  dict->lookup("Resources", &obj1);+  if (obj1.isDict()) {+    resources.free();+    obj1.copy(&resources);+  }+  obj1.free();+}++PageAttrs::~PageAttrs() {+  lastModified.free();+  boxColorInfo.free();+  group.free();+  metadata.free();+  pieceInfo.free();+  separationInfo.free();+  resources.free();+}++GBool PageAttrs::readBox(Dict *dict, 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(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA) {+  ok = gTrue;+  xref = xrefA;+  num = numA;++  // get attributes+  attrs = attrsA;++  // annotations+  pageDict->lookupNF("Annots", &annots);+  if (!(annots.isRef() || annots.isArray() || annots.isNull())) {+    error(-1, "Page annotations object (page %d) is wrong type (%s)",+	  num, annots.getTypeName());+    annots.free();+    goto err2;+  }++  // contents+  pageDict->lookupNF("Contents", &contents);+  if (!(contents.isRef() || contents.isArray() ||+	contents.isNull())) {+    error(-1, "Page contents object (page %d) is wrong type (%s)",+	  num, contents.getTypeName());+    contents.free();+    goto err1;+  }++  return;++ err2:+  annots.initNull();+ err1:+  contents.initNull();+  ok = gFalse;+}++Page::~Page() {+  delete attrs;+  annots.free();+  contents.free();+}++Links *Page::getLinks(Catalog *catalog) {+  Links *links;+  Object obj;++  links = new Links(getAnnots(&obj), catalog->getBaseURI());+  obj.free();+  return links;+}++void Page::display(OutputDev *out, double hDPI, double vDPI,+		   int rotate, GBool useMediaBox, GBool crop,+		   GBool printing, Catalog *catalog,+		   GBool (*abortCheckCbk)(void *data),+		   void *abortCheckCbkData) {+  displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,+	       -1, -1, -1, -1, printing, catalog,+	       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, Catalog *catalog,+			GBool (*abortCheckCbk)(void *data),+			void *abortCheckCbkData) {+#ifndef PDF_PARSER_ONLY+  PDFRectangle *mediaBox, *cropBox;+  PDFRectangle box;+  Gfx *gfx;+  Object obj;+  Annots *annotList;+  Dict *acroForm;+  int i;++  if (!out->checkPageSlice(this, hDPI, vDPI, rotate, useMediaBox, crop,+			   sliceX, sliceY, sliceW, sliceH,+			   printing, catalog,+			   abortCheckCbk, abortCheckCbkData)) {+    return;+  }++  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(xref, 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(&obj);+    gfx->restoreState();+  }+  obj.free();++  // draw annotations+  annotList = new Annots(xref, catalog, getAnnots(&obj));+  obj.free();+  acroForm = catalog->getAcroForm()->isDict() ?+               catalog->getAcroForm()->getDict() : NULL;+  if (acroForm) {+    if (acroForm->lookup("NeedAppearances", &obj)) {+      if (obj.isBool() && obj.getBool()) {+	annotList->generateAppearances(acroForm);+      }+    }+    obj.free();+  }+  if (annotList->getNumAnnots() > 0) {+    if (globalParams->getPrintCommands()) {+      printf("***** Annotations\n");+    }+    for (i = 0; i < annotList->getNumAnnots(); ++i) {+      annotList->getAnnot(i)->draw(gfx, printing);+    }+    out->dump();+  }+  delete annotList;++  delete gfx;+#endif+}++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, Catalog *catalog) {+  Links *links;+  int i;++  links = getLinks(catalog);+  for (i = 0; i < links->getNumLinks(); ++i) {+    out->processLink(links->getLink(i), catalog);+  }+  delete links;+}++void Page::getDefaultCTM(double *ctm, double hDPI, double vDPI,+			 int rotate, GBool useMediaBox, GBool upsideDown) {+  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;+}
+ xpdf/Page.h view
@@ -0,0 +1,187 @@+//========================================================================+//+// 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 XRef;+class OutputDev;+class Links;+class Catalog;++//------------------------------------------------------------------------++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);++  // 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; }+  Dict *getResourceDict()+    { return resources.isDict() ? resources.getDict() : (Dict *)NULL; }++private:++  GBool readBox(Dict *dict, 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;+  Object resources;+};++//------------------------------------------------------------------------+// Page+//------------------------------------------------------------------------++class Page {+public:++  // Constructor.+  Page(XRef *xrefA, int numA, Dict *pageDict, PageAttrs *attrsA);++  // Destructor.+  ~Page();++  // Is page valid?+  GBool isOk() { return ok; }++  // Get page parameters.+  int getNum() { return num; }+  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(); }++  // 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(Catalog *catalog);++  // Get contents.+  Object *getContents(Object *obj) { return contents.fetch(xref, obj); }++  // Display a page.+  void display(OutputDev *out, double hDPI, double vDPI,+	       int rotate, GBool useMediaBox, GBool crop,+	       GBool printing, Catalog *catalog,+	       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, Catalog *catalog,+		    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, Catalog *catalog);++  // Get the page's default CTM.+  void getDefaultCTM(double *ctm, double hDPI, double vDPI,+		     int rotate, GBool useMediaBox, GBool upsideDown);++private:++  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+  GBool ok;			// true if page is valid+};++#endif
+ xpdf/Parser.cc view
@@ -0,0 +1,227 @@+//========================================================================+//+// Parser.cc+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stddef.h>+#include "Object.h"+#include "Array.h"+#include "Dict.h"+#include "Decrypt.h"+#include "Parser.h"+#include "XRef.h"+#include "Error.h"++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, Guchar *fileKey,+		       CryptAlgorithm encAlgorithm, int keyLength,+		       int objNum, int objGen) {+  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 (buf1.isCmd("[")) {+    shift();+    obj->initArray(xref);+    while (!buf1.isCmd("]") && !buf1.isEOF())+      obj->arrayAdd(getObj(&obj2, fileKey, encAlgorithm, keyLength,+			   objNum, objGen));+    if (buf1.isEOF())+      error(getPos(), "End of file inside array");+    shift();++  // dictionary or stream+  } else if (buf1.isCmd("<<")) {+    shift();+    obj->initDict(xref);+    while (!buf1.isCmd(">>") && !buf1.isEOF()) {+      if (!buf1.isName()) {+	error(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, fileKey, encAlgorithm, keyLength,+				 objNum, objGen));+      }+    }+    if (buf1.isEOF())+      error(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))) {+	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) {+  Object obj;+  BaseStream *baseStr;+  Stream *str;+  Guint pos, endPos, length;++  // get stream start position+  lexer->skipToNextLine();+  pos = lexer->getPos();++  // get length+  dict->dictLookup("Length", &obj);+  if (obj.isInt()) {+    length = (Guint)obj.getInt();+    obj.free();+  } else {+    error(getPos(), "Bad 'Length' attribute in stream");+    obj.free();+    return NULL;+  }++  // check for length in damaged file+  if (xref && xref->getStreamEnd(pos, &endPos)) {+    length = endPos - pos;+  }++  // 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;+  }+  baseStr = lexer->getStream()->getBaseStream();++  // skip over stream data+  lexer->setPos(pos + length);++  // refill token buffers and check for 'endstream'+  shift();  // kill '>>'+  shift();  // kill 'stream'+  if (buf1.isCmd("endstream")) {+    shift();+  } else {+    error(getPos(), "Missing 'endstream'");+    // kludge for broken PDF files: just add 5k to the length, and+    // hope its enough+    length += 5000;+  }++  // make base stream+  str = baseStr->makeSubStream(pos, gTrue, length, dict);++  // handle decryption+  if (fileKey) {+    str = new DecryptStream(str, fileKey, encAlgorithm, keyLength,+			    objNum, objGen);+  }++  // get filters+  str = str->addFilters(dict);++  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);+}
+ xpdf/Parser.h view
@@ -0,0 +1,59 @@+//========================================================================+//+// 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.+  Object *getObj(Object *obj, Guchar *fileKey = NULL,+		 CryptAlgorithm encAlgorithm = cryptRC4, int keyLength = 0,+		 int objNum = 0, int objGen = 0);++  // Get stream.+  Stream *getStream() { return lexer->getStream(); }++  // Get current position in file.+  int 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);+  void shift();+};++#endif+
+ xpdf/PreScanOutputDev.cc view
@@ -0,0 +1,257 @@+//========================================================================+//+// PreScanOutputDev.cc+//+// Copyright 2005 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <math.h>+#include "GlobalParams.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->getStrokeColorSpace(), state->getStrokeColor(),+	state->getStrokeOpacity(), state->getBlendMode());+  state->getLineDash(&dash, &dashLen, &dashStart);+  if (dashLen != 0) {+    gdi = gFalse;+  }+}++void PreScanOutputDev::fill(GfxState *state) {+  check(state->getFillColorSpace(), state->getFillColor(),+	state->getFillOpacity(), state->getBlendMode());+}++void PreScanOutputDev::eoFill(GfxState *state) {+  check(state->getFillColorSpace(), state->getFillColor(),+	state->getFillOpacity(), state->getBlendMode());+}++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;+  Ref embRef;+  DisplayFontParam *dfp;+  GBool simpleTTF;++  render = state->getRender();+  if (!(render & 1)) {+    check(state->getFillColorSpace(), state->getFillColor(),+	  state->getFillOpacity(), state->getBlendMode());+  }+  if ((render & 3) == 1 || (render & 3) == 2) {+    check(state->getStrokeColorSpace(), state->getStrokeColor(),+	  state->getStrokeOpacity(), state->getBlendMode());+  }++  font = state->getFont();+  state->getFontTransMat(&m11, &m12, &m21, &m22);+  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) &&+              (font->getEmbeddedFontID(&embRef) ||+	       font->getExtFontFile() ||+	       (font->getName() &&+		(dfp = globalParams->getDisplayFont(font->getName())) &&+		dfp->kind == displayFontTT));+  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) {+  int i, j;++  check(state->getFillColorSpace(), state->getFillColor(),+	state->getFillOpacity(), state->getBlendMode());+  gdi = gFalse;++  if (inlineImg) {+    str->reset();+    j = height * ((width + 7) / 8);+    for (i = 0; i < j; ++i)+      str->getChar();+    str->close();+  }+}++void PreScanOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,+				 int width, int height,+				 GfxImageColorMap *colorMap,+				 int *maskColors, GBool inlineImg) {+  GfxColorSpace *colorSpace;+  int i, j;++  colorSpace = colorMap->getColorSpace();+  if (colorSpace->getMode() == csIndexed) {+    colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();+  }+  if (colorSpace->getMode() != csDeviceGray &&+      colorSpace->getMode() != csCalGray) {+    gray = gFalse;+  }+  mono = gFalse;+  if (state->getBlendMode() != gfxBlendNormal) {+    transparency = gTrue;+  }+  gdi = gFalse;++  if (inlineImg) {+    str->reset();+    j = height * ((width * colorMap->getNumPixelComps() *+		   colorMap->getBits() + 7) / 8);+    for (i = 0; i < j; ++i)+      str->getChar();+    str->close();+  }+}++void PreScanOutputDev::drawMaskedImage(GfxState *state, Object *ref,+				       Stream *str,+				       int width, int height,+				       GfxImageColorMap *colorMap,+				       Stream *maskStr,+				       int maskWidth, int maskHeight,+				       GBool maskInvert) {+  GfxColorSpace *colorSpace;++  colorSpace = colorMap->getColorSpace();+  if (colorSpace->getMode() == csIndexed) {+    colorSpace = ((GfxIndexedColorSpace *)colorSpace)->getBase();+  }+  if (colorSpace->getMode() != csDeviceGray &&+      colorSpace->getMode() != csCalGray) {+    gray = gFalse;+  }+  mono = gFalse;+  if (state->getBlendMode() != gfxBlendNormal) {+    transparency = gTrue;+  }+  gdi = gFalse;+}++void PreScanOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,+					   Stream *str,+					   int width, int height,+					   GfxImageColorMap *colorMap,+					   Stream *maskStr,+					   int maskWidth, int maskHeight,+					   GfxImageColorMap *maskColorMap) {+  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(GfxColorSpace *colorSpace, GfxColor *color,+			     double opacity, GfxBlendMode blendMode) {+  GfxRGB rgb;++  if (colorSpace->getMode() == csPattern) {+    mono = gFalse;+    gray = gFalse;+    gdi = gFalse;+  } else {+    colorSpace->getRGB(color, &rgb);+    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;+  gdi = gTrue;+}
+ xpdf/PreScanOutputDev.h view
@@ -0,0 +1,130 @@+//========================================================================+//+// 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 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);++  //----- 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);+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,+			 int width, int height, GfxImageColorMap *colorMap,+			 int *maskColors, GBool inlineImg);+  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,+			       int width, int height,+			       GfxImageColorMap *colorMap,+			       Stream *maskStr, int maskWidth, int maskHeight,+			       GBool maskInvert);+  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,+				   int width, int height,+				   GfxImageColorMap *colorMap,+				   Stream *maskStr,+				   int maskWidth, int maskHeight,+				   GfxImageColorMap *maskColorMap);++  //----- 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() 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(GfxColorSpace *colorSpace, GfxColor *color,+	     double opacity, GfxBlendMode blendMode);++  GBool mono;+  GBool gray;+  GBool transparency;+  GBool gdi;+};++#endif
+ xpdf/SecurityHandler.cc view
@@ -0,0 +1,390 @@+//========================================================================+//+// SecurityHandler.cc+//+// Copyright 2004 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include "GString.h"+#include "PDFDoc.h"+#include "Decrypt.h"+#include "Error.h"+#include "GlobalParams.h"+#if HAVE_XPDFCORE+#  include "XPDFCore.h"+#elif HAVE_WINPDFCORE+#  include "WinPDFCore.h"+#endif+#ifdef ENABLE_PLUGINS+#  include "XpdfPluginAPI.h"+#endif+#include "SecurityHandler.h"++//------------------------------------------------------------------------+// SecurityHandler+//------------------------------------------------------------------------++SecurityHandler *SecurityHandler::make(PDFDoc *docA, Object *encryptDictA) {+  Object filterObj;+  SecurityHandler *secHdlr;+#ifdef ENABLE_PLUGINS+  XpdfSecurityHandler *xsh;+#endif++  encryptDictA->dictLookup("Filter", &filterObj);+  if (filterObj.isName("Standard")) {+    secHdlr = new StandardSecurityHandler(docA, encryptDictA);+  } else if (filterObj.isName()) {+#ifdef ENABLE_PLUGINS+    if ((xsh = globalParams->getSecurityHandler(filterObj.getName()))) {+      secHdlr = new ExternalSecurityHandler(docA, encryptDictA, xsh);+    } else {+#endif+      error(-1, "Couldn't find the '%s' security handler",+	    filterObj.getName());+      secHdlr = NULL;+#ifdef ENABLE_PLUGINS+    }+#endif+  } else {+    error(-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(-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, permObj, fileIDObj;+  Object fileIDObj1;+  Object cryptFiltersObj, streamFilterObj, stringFilterObj;+  Object cryptFilterObj, cfmObj, cfLengthObj;+  Object encryptMetadataObj;++  ok = gFalse;+  fileID = NULL;+  ownerKey = NULL;+  userKey = NULL;++  encryptDictA->dictLookup("V", &versionObj);+  encryptDictA->dictLookup("R", &revisionObj);+  encryptDictA->dictLookup("Length", &lengthObj);+  encryptDictA->dictLookup("O", &ownerKeyObj);+  encryptDictA->dictLookup("U", &userKeyObj);+  encryptDictA->dictLookup("P", &permObj);+  doc->getXRef()->getTrailerDict()->dictLookup("ID", &fileIDObj);+  if (versionObj.isInt() &&+      revisionObj.isInt() &&+      ownerKeyObj.isString() && ownerKeyObj.getString()->getLength() == 32 &&+      userKeyObj.isString() && userKeyObj.getString()->getLength() == 32 &&+      permObj.isInt()) {+    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;+    //~ this currently only handles a subset of crypt filter functionality+    if (encVersion == 4 && encRevision == 4) {+      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 (cryptFiltersObj.dictLookup(streamFilterObj.getName(),+				       &cryptFilterObj)->isDict()) {+	  cryptFilterObj.dictLookup("CFM", &cfmObj);+	  if (cfmObj.isName("V2")) {+	    encVersion = 2;+	    encRevision = 3;+	    if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {+	      //~ according to the spec, this should be cfLengthObj / 8+	      fileKeyLength = cfLengthObj.getInt();+	    }+	    cfLengthObj.free();+	  } else if (cfmObj.isName("AESV2")) {+	    encVersion = 2;+	    encRevision = 3;+	    encAlgorithm = cryptAES;+	    if (cryptFilterObj.dictLookup("Length", &cfLengthObj)->isInt()) {+	      //~ according to the spec, this should be cfLengthObj / 8+	      fileKeyLength = cfLengthObj.getInt();+	    }+	    cfLengthObj.free();+	  }+	  cfmObj.free();+	}+	cryptFilterObj.free();+      }+      stringFilterObj.free();+      streamFilterObj.free();+      cryptFiltersObj.free();+      if (encryptDictA->dictLookup("EncryptMetadata",+				   &encryptMetadataObj)->isBool()) {+	encryptMetadata = encryptMetadataObj.getBool();+      }+      encryptMetadataObj.free();+    }+    permFlags = permObj.getInt();+    ownerKey = ownerKeyObj.getString()->copy();+    userKey = userKeyObj.getString()->copy();+    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();+      }+      ok = gTrue;+    } else {+      error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",+	    encVersion, encRevision);+    }+  } else {+    error(-1, "Weird encryption info");+  }+  if (fileKeyLength > 16) {+    fileKeyLength = 16;+  }+  fileIDObj.free();+  permObj.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;+  }+}++void *StandardSecurityHandler::makeAuthData(GString *ownerPassword,+					    GString *userPassword) {+  return new StandardAuthData(ownerPassword ? ownerPassword->copy()+			                    : (GString *)NULL,+			      userPassword ? userPassword->copy()+			                   : (GString *)NULL);+}++void *StandardSecurityHandler::getAuthData() {+#if HAVE_XPDFCORE+  XPDFCore *core;+  GString *password;++  if (!(core = (XPDFCore *)doc->getGUIData()) ||+      !(password = core->getPassword())) {+    return NULL;+  }+  return new StandardAuthData(password, password->copy());+#elif HAVE_WINPDFCORE+  WinPDFCore *core;+  GString *password;++  if (!(core = (WinPDFCore *)doc->getGUIData()) ||+      !(password = core->getPassword())) {+    return NULL;+  }+  return new StandardAuthData(password, password->copy());+#else+  return NULL;+#endif+}++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, permFlags, fileID,+			    ownerPassword, userPassword, fileKey,+			    encryptMetadata, &ownerPasswordOk)) {+    return gFalse;+  }+  return gTrue;+}++#ifdef ENABLE_PLUGINS++//------------------------------------------------------------------------+// ExternalSecurityHandler+//------------------------------------------------------------------------++ExternalSecurityHandler::ExternalSecurityHandler(PDFDoc *docA,+						 Object *encryptDictA,+						 XpdfSecurityHandler *xshA):+  SecurityHandler(docA)+{+  encryptDictA->copy(&encryptDict);+  xsh = xshA;+  encAlgorithm = cryptRC4; //~ this should be obtained via getKey+  ok = gFalse;++  if (!(*xsh->newDoc)(xsh->handlerData, (XpdfDoc)docA,+		      (XpdfObject)encryptDictA, &docData)) {+    return;+  }++  ok = gTrue;+}++ExternalSecurityHandler::~ExternalSecurityHandler() {+  (*xsh->freeDoc)(xsh->handlerData, docData);+  encryptDict.free();+}++void *ExternalSecurityHandler::makeAuthData(GString *ownerPassword,+					    GString *userPassword) {+  char *opw, *upw;+  void *authData;++  opw = ownerPassword ? ownerPassword->getCString() : (char *)NULL;+  upw = userPassword ? userPassword->getCString() : (char *)NULL;+  if (!(*xsh->makeAuthData)(xsh->handlerData, docData, opw, upw, &authData)) {+    return NULL;+  }+  return authData;+}++void *ExternalSecurityHandler::getAuthData() {+  void *authData;++  if (!(*xsh->getAuthData)(xsh->handlerData, docData, &authData)) {+    return NULL;+  }+  return authData;+}++void ExternalSecurityHandler::freeAuthData(void *authData) {+  (*xsh->freeAuthData)(xsh->handlerData, docData, authData);+}++GBool ExternalSecurityHandler::authorize(void *authData) {+  char *key;+  int length;++  if (!ok) {+    return gFalse;+  }+  permFlags = (*xsh->authorize)(xsh->handlerData, docData, authData);+  if (!(permFlags & xpdfPermissionOpen)) {+    return gFalse;+  }+  if (!(*xsh->getKey)(xsh->handlerData, docData, &key, &length, &encVersion)) {+    return gFalse;+  }+  if ((fileKeyLength = length) > 16) {+    fileKeyLength = 16;+  }+  memcpy(fileKey, key, fileKeyLength);+  (*xsh->freeKey)(xsh->handlerData, docData, key, length);+  return gTrue;+}++#endif // ENABLE_PLUGINS
+ xpdf/SecurityHandler.h view
@@ -0,0 +1,160 @@+//========================================================================+//+// 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();++  // 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 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[16];+  int fileKeyLength;+  int encVersion;+  int encRevision;+  CryptAlgorithm encAlgorithm;+  GBool encryptMetadata;++  GString *ownerKey, *userKey;+  GString *fileID;+  GBool ok;+};++#ifdef ENABLE_PLUGINS+//------------------------------------------------------------------------+// ExternalSecurityHandler+//------------------------------------------------------------------------++class ExternalSecurityHandler: public SecurityHandler {+public:++  ExternalSecurityHandler(PDFDoc *docA, Object *encryptDictA,+			  XpdfSecurityHandler *xshA);+  virtual ~ExternalSecurityHandler();++  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 gFalse; }+  virtual Guchar *getFileKey() { return fileKey; }+  virtual int getFileKeyLength() { return fileKeyLength; }+  virtual int getEncVersion() { return encVersion; }+  virtual CryptAlgorithm getEncAlgorithm() { return encAlgorithm; }++private:++  Object encryptDict;+  XpdfSecurityHandler *xsh;+  void *docData;+  int permFlags;+  Guchar fileKey[16];+  int fileKeyLength;+  int encVersion;+  CryptAlgorithm encAlgorithm;+  GBool ok;+};+#endif // ENABLE_PLUGINS++#endif
+ xpdf/Splash.cc view
@@ -0,0 +1,3310 @@+//========================================================================+//+// Splash.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <string.h>+#include "gmem.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"++//------------------------------------------------------------------------++// 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);+}++//------------------------------------------------------------------------+// SplashPipe+//------------------------------------------------------------------------++#define splashPipeMaxStages 9++struct SplashPipe {+  // pixel coordinates+  int x, y;++  // source pattern+  SplashPattern *pattern;++  // source alpha and color+  SplashCoord aInput;+  GBool usesShape;+  Guchar aSrc;+  SplashColorPtr cSrc;+  SplashColor cSrcVal;++  // non-isolated group alpha0+  Guchar *alpha0Ptr;++  // soft mask+  SplashColorPtr softMaskPtr;++  // destination alpha and color+  SplashColorPtr destColorPtr;+  int destColorMask;+  Guchar *destAlphaPtr;++  // shape+  SplashCoord shape;++  // result alpha and color+  GBool noTransparency;+  SplashPipeResultColorCtrl resultColorCtrl;++  // non-isolated group correction+  int nonIsolatedGroup;+};++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+};++//------------------------------------------------------------------------++static void blendXor(SplashColorPtr src, SplashColorPtr dest,+		     SplashColorPtr blend, SplashColorMode cm) {+  int i;++  for (i = 0; i < splashColorModeNComps[cm]; ++i) {+    blend[i] = src[i] ^ dest[i];+  }+}++//------------------------------------------------------------------------+// modified region+//------------------------------------------------------------------------++void Splash::clearModRegion() {+  modXMin = bitmap->getWidth();+  modYMin = bitmap->getHeight();+  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, int x, int y,+			     SplashPattern *pattern, SplashColorPtr cSrc,+			     SplashCoord aInput, GBool usesShape,+			     GBool nonIsolatedGroup) {+  pipeSetXY(pipe, x, y);+  pipe->pattern = NULL;++  // source color+  if (pattern) {+    if (pattern->isStatic()) {+      pattern->getColor(x, y, pipe->cSrcVal);+    } else {+      pipe->pattern = pattern;+    }+    pipe->cSrc = pipe->cSrcVal;+  } else {+    pipe->cSrc = cSrc;+  }++  // source alpha+  pipe->aInput = aInput;+  if (!state->softMask) {+    if (usesShape) {+      pipe->aInput *= 255;+    } else {+      pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255);+    }+  }+  pipe->usesShape = usesShape;++  // result alpha+  if (aInput == 1 && !state->softMask && !usesShape &&+      !state->inNonIsolatedGroup) {+    pipe->noTransparency = gTrue;+  } else {+    pipe->noTransparency = gFalse;+  }++  // result color+  if (pipe->noTransparency) {+    // the !state->blendFunc case is handled separately in pipeRun+    pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];+  } else if (!state->blendFunc) {+    pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];+  } else {+    pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];+  }++  // non-isolated group correction+  if (nonIsolatedGroup) {+    pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode];+  } else {+    pipe->nonIsolatedGroup = 0;+  }+}++inline void Splash::pipeRun(SplashPipe *pipe) {+  Guchar aSrc, aDest, alpha2, alpha0, aResult;+  SplashColor cDest, cBlend;+  Guchar cResult0, cResult1, cResult2, cResult3;++  //----- source color++  // static pattern: handled in pipeInit+  // fixed color: handled in pipeInit++  // dynamic pattern+  if (pipe->pattern) {+    pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);+  }++  if (pipe->noTransparency && !state->blendFunc) {++    //----- write destination pixel++    switch (bitmap->mode) {+    case splashModeMono1:+      cResult0 = pipe->cSrc[0];+      if (state->screen->test(pipe->x, pipe->y, cResult0)) {+	*pipe->destColorPtr |= pipe->destColorMask;+      } else {+	*pipe->destColorPtr &= ~pipe->destColorMask;+      }+      if (!(pipe->destColorMask >>= 1)) {+	pipe->destColorMask = 0x80;+	++pipe->destColorPtr;+      }+      break;+    case splashModeMono8:+      *pipe->destColorPtr++ = pipe->cSrc[0];+      break;+    case splashModeRGB8:+      *pipe->destColorPtr++ = pipe->cSrc[0];+      *pipe->destColorPtr++ = pipe->cSrc[1];+      *pipe->destColorPtr++ = pipe->cSrc[2];+      break;+    case splashModeBGR8:+      *pipe->destColorPtr++ = pipe->cSrc[2];+      *pipe->destColorPtr++ = pipe->cSrc[1];+      *pipe->destColorPtr++ = pipe->cSrc[0];+      break;+#if SPLASH_CMYK+    case splashModeCMYK8:+      *pipe->destColorPtr++ = pipe->cSrc[0];+      *pipe->destColorPtr++ = pipe->cSrc[1];+      *pipe->destColorPtr++ = pipe->cSrc[2];+      *pipe->destColorPtr++ = pipe->cSrc[3];+      break;+#endif+    }+    if (pipe->destAlphaPtr) {+      *pipe->destAlphaPtr++ = 255;+    }++  } else {++    //----- read destination pixel++    switch (bitmap->mode) {+    case splashModeMono1:+      cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;+      break;+    case splashModeMono8:+      cDest[0] = *pipe->destColorPtr;+      break;+    case splashModeRGB8:+      cDest[0] = pipe->destColorPtr[0];+      cDest[1] = pipe->destColorPtr[1];+      cDest[2] = pipe->destColorPtr[2];+      break;+    case splashModeBGR8:+      cDest[0] = pipe->destColorPtr[2];+      cDest[1] = pipe->destColorPtr[1];+      cDest[2] = pipe->destColorPtr[0];+      break;+#if SPLASH_CMYK+    case splashModeCMYK8:+      cDest[0] = pipe->destColorPtr[0];+      cDest[1] = pipe->destColorPtr[1];+      cDest[2] = pipe->destColorPtr[2];+      cDest[3] = pipe->destColorPtr[3];+      break;+#endif+    }+    if (pipe->destAlphaPtr) {+      aDest = *pipe->destAlphaPtr;+    } else {+      aDest = 0xff;+    }++    //----- blend function++    if (state->blendFunc) {+      (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode);+    }++    //----- source alpha++    if (state->softMask) {+      if (pipe->usesShape) {+	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr+++				   * pipe->shape);+      } else {+	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++);+      }+    } else if (pipe->usesShape) {+      // pipe->aInput is premultiplied by 255 in pipeInit+      aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape);+    } else {+      // precomputed in pipeInit+      aSrc = pipe->aSrc;+    }++    //----- result alpha and non-isolated group element correction++    if (pipe->noTransparency) {+      alpha2 = aResult = 255;+    } else {+      aResult = aSrc + aDest - div255(aSrc * aDest);++      if (pipe->alpha0Ptr) {+	alpha0 = *pipe->alpha0Ptr++;+	alpha2 = aResult + alpha0 - div255(aResult * alpha0);+      } else {+	alpha2 = aResult;+      }+    }++    //----- result color++    cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy++    switch (pipe->resultColorCtrl) {++#if SPLASH_CMYK+    case splashPipeResultColorNoAlphaBlendCMYK:+      cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]);+#endif+    case splashPipeResultColorNoAlphaBlendRGB:+      cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]);+      cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]);+    case splashPipeResultColorNoAlphaBlendMono:+      cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]);+      break;++    case splashPipeResultColorAlphaNoBlendMono:+      if (alpha2 == 0) {+	cResult0 = 0;+      } else {+	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] ++			     aSrc * pipe->cSrc[0]) / alpha2);+      }+      break;+    case splashPipeResultColorAlphaNoBlendRGB:+      if (alpha2 == 0) {+	cResult0 = 0;+	cResult1 = 0;+	cResult2 = 0;+      } else {+	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] ++			     aSrc * pipe->cSrc[0]) / alpha2);+	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] ++			     aSrc * pipe->cSrc[1]) / alpha2);+	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] ++			     aSrc * pipe->cSrc[2]) / alpha2);+      }+      break;+#if SPLASH_CMYK+    case splashPipeResultColorAlphaNoBlendCMYK:+      if (alpha2 == 0) {+	cResult0 = 0;+	cResult1 = 0;+	cResult2 = 0;+	cResult3 = 0;+      } else {+	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] ++			     aSrc * pipe->cSrc[0]) / alpha2);+	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] ++			     aSrc * pipe->cSrc[1]) / alpha2);+	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] ++			     aSrc * pipe->cSrc[2]) / alpha2);+	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] ++			     aSrc * pipe->cSrc[3]) / alpha2);+      }+      break;+#endif++    case splashPipeResultColorAlphaBlendMono:+      if (alpha2 == 0) {+	cResult0 = 0;+      } else {+	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] ++			     aSrc * ((255 - aDest) * pipe->cSrc[0] ++				     aDest * cBlend[0]) / 255) /+			    alpha2);+      }+      break;+    case splashPipeResultColorAlphaBlendRGB:+      if (alpha2 == 0) {+	cResult0 = 0;+	cResult1 = 0;+	cResult2 = 0;+      } else {+	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] ++			     aSrc * ((255 - aDest) * pipe->cSrc[0] ++				     aDest * cBlend[0]) / 255) /+			    alpha2);+	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] ++			     aSrc * ((255 - aDest) * pipe->cSrc[1] ++				     aDest * cBlend[1]) / 255) /+			    alpha2);+	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] ++			     aSrc * ((255 - aDest) * pipe->cSrc[2] ++				     aDest * cBlend[2]) / 255) /+			    alpha2);+      }+      break;+#if SPLASH_CMYK+    case splashPipeResultColorAlphaBlendCMYK:+      if (alpha2 == 0) {+	cResult0 = 0;+	cResult1 = 0;+	cResult2 = 0;+	cResult3 = 0;+      } else {+	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] ++			     aSrc * ((255 - aDest) * pipe->cSrc[0] ++				     aDest * cBlend[0]) / 255) /+			    alpha2);+	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] ++			     aSrc * ((255 - aDest) * pipe->cSrc[1] ++				     aDest * cBlend[1]) / 255) /+			    alpha2);+	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] ++			     aSrc * ((255 - aDest) * pipe->cSrc[2] ++				     aDest * cBlend[2]) / 255) /+			    alpha2);+	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] ++			     aSrc * ((255 - aDest) * pipe->cSrc[3] ++				     aDest * cBlend[3]) / 255) /+			    alpha2);+      }+      break;+#endif+    }++    //----- non-isolated group correction++    if (aResult != 0) {+      switch (pipe->nonIsolatedGroup) {+#if SPLASH_CMYK+      case 4:+	cResult3 += (cResult3 - cDest[3]) * aDest *+	            (255 - aResult) / (255 * aResult);+#endif+      case 3:+	cResult2 += (cResult2 - cDest[2]) * aDest *+	            (255 - aResult) / (255 * aResult);+	cResult1 += (cResult1 - cDest[1]) * aDest *+	            (255 - aResult) / (255 * aResult);+      case 1:+	cResult0 += (cResult0 - cDest[0]) * aDest *+	            (255 - aResult) / (255 * aResult);+      case 0:+	break;+      }+    }++    //----- write destination pixel++    switch (bitmap->mode) {+    case splashModeMono1:+      if (state->screen->test(pipe->x, pipe->y, cResult0)) {+	*pipe->destColorPtr |= pipe->destColorMask;+      } else {+	*pipe->destColorPtr &= ~pipe->destColorMask;+      }+      if (!(pipe->destColorMask >>= 1)) {+	pipe->destColorMask = 0x80;+	++pipe->destColorPtr;+      }+      break;+    case splashModeMono8:+      *pipe->destColorPtr++ = cResult0;+      break;+    case splashModeRGB8:+      *pipe->destColorPtr++ = cResult0;+      *pipe->destColorPtr++ = cResult1;+      *pipe->destColorPtr++ = cResult2;+      break;+    case splashModeBGR8:+      *pipe->destColorPtr++ = cResult2;+      *pipe->destColorPtr++ = cResult1;+      *pipe->destColorPtr++ = cResult0;+      break;+#if SPLASH_CMYK+    case splashModeCMYK8:+      *pipe->destColorPtr++ = cResult0;+      *pipe->destColorPtr++ = cResult1;+      *pipe->destColorPtr++ = cResult2;+      *pipe->destColorPtr++ = cResult3;+      break;+#endif+    }+    if (pipe->destAlphaPtr) {+      *pipe->destAlphaPtr++ = aResult;+    }++  }++  ++pipe->x;+}++inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {+  pipe->x = x;+  pipe->y = y;+  if (state->softMask) {+    pipe->softMaskPtr =+        &state->softMask->data[y * state->softMask->rowSize + x];+  }+  switch (bitmap->mode) {+  case splashModeMono1:+    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];+    pipe->destColorMask = 0x80 >> (x & 7);+    break;+  case splashModeMono8:+    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];+    break;+#endif+  }+  if (bitmap->alpha) {+    pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];+  } else {+    pipe->destAlphaPtr = NULL;+  }+  if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {+    pipe->alpha0Ptr =+        &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width ++			     (alpha0X + x)];+  } else {+    pipe->alpha0Ptr = NULL;+  }+}++inline void Splash::pipeIncX(SplashPipe *pipe) {+  ++pipe->x;+  if (state->softMask) {+    ++pipe->softMaskPtr;+  }+  switch (bitmap->mode) {+  case splashModeMono1:+    if (!(pipe->destColorMask >>= 1)) {+      pipe->destColorMask = 0x80;+      ++pipe->destColorPtr;+    }+    break;+  case splashModeMono8:+    ++pipe->destColorPtr;+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    pipe->destColorPtr += 3;+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    pipe->destColorPtr += 4;+    break;+#endif+  }+  if (pipe->destAlphaPtr) {+    ++pipe->destAlphaPtr;+  }+  if (pipe->alpha0Ptr) {+    ++pipe->alpha0Ptr;+  }+}++inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {+  if (noClip || state->clip->test(x, y)) {+    pipeSetXY(pipe, x, y);+    pipeRun(pipe);+    updateModX(x);+    updateModY(y);+  }+}++inline void Splash::drawAAPixelInit() {+  aaBufY = -1;+}++inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {+#if splashAASize == 4+  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,+			       1, 2, 2, 3, 2, 3, 3, 4 };+  int w;+#else+  int xx, yy;+#endif+  SplashColorPtr p;+  int x0, x1, t;++  if (x < 0 || x >= bitmap->width ||+      y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {+    return;+  }++  // update aaBuf+  if (y != aaBufY) {+    memset(aaBuf->getDataPtr(), 0xff,+	   aaBuf->getRowSize() * aaBuf->getHeight());+    x0 = 0;+    x1 = bitmap->width - 1;+    state->clip->clipAALine(aaBuf, &x0, &x1, y);+    aaBufY = y;+  }++  // compute the shape value+#if splashAASize == 4+  p = aaBuf->getDataPtr() + (x >> 1);+  w = aaBuf->getRowSize();+  if (x & 1) {+    t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] ++        bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];+  } else {+    t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] ++        bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];+  }+#else+  t = 0;+  for (yy = 0; yy < splashAASize; ++yy) {+    for (xx = 0; xx < splashAASize; ++xx) {+      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() ++	  ((x * splashAASize + xx) >> 3);+      t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;+    }+  }+#endif++  // draw the pixel+  if (t != 0) {+    pipeSetXY(pipe, x, y);+    pipe->shape *= aaGamma[t];+    pipeRun(pipe);+    updateModX(x);+    updateModY(y);+  }+}++inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,+			     GBool noClip) {+  int x;++  pipeSetXY(pipe, x0, y);+  if (noClip) {+    for (x = x0; x <= x1; ++x) {+      pipeRun(pipe);+    }+    updateModX(x0);+    updateModX(x1);+    updateModY(y);+  } else {+    for (x = x0; x <= x1; ++x) {+      if (state->clip->test(x, y)) {+	pipeRun(pipe);+	updateModX(x);+	updateModY(y);+      } else {+	pipeIncX(pipe);+      }+    }+  }+}++inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) {+#if splashAASize == 4+  static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,+			       1, 2, 2, 3, 2, 3, 3, 4 };+  SplashColorPtr p0, p1, p2, p3;+  int t;+#else+  SplashColorPtr p;+  int xx, yy, t;+#endif+  int x;++#if splashAASize == 4+  p0 = aaBuf->getDataPtr() + (x0 >> 1);+  p1 = p0 + aaBuf->getRowSize();+  p2 = p1 + aaBuf->getRowSize();+  p3 = p2 + aaBuf->getRowSize();+#endif+  pipeSetXY(pipe, x0, y);+  for (x = x0; x <= x1; ++x) {++    // compute the shape value+#if splashAASize == 4+    if (x & 1) {+      t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] ++	  bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];+      ++p0; ++p1; ++p2; ++p3;+    } else {+      t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] ++	  bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];+    }+#else+    t = 0;+    for (yy = 0; yy < splashAASize; ++yy) {+      for (xx = 0; xx < splashAASize; ++xx) {+	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() ++	    ((x * splashAASize + xx) >> 3);+	t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;+      }+    }+#endif++    if (t != 0) {+      pipe->shape = aaGamma[t];+      pipeRun(pipe);+      updateModX(x);+      updateModY(y);+    } else {+      pipeIncX(pipe);+    }+  }+}++//------------------------------------------------------------------------++// 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];+}++//------------------------------------------------------------------------+// Splash+//------------------------------------------------------------------------++Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,+	       SplashScreenParams *screenParams) {+  int i;++  bitmap = bitmapA;+  vectorAntialias = vectorAntialiasA;+  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,+			  screenParams);+  if (vectorAntialias) {+    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,+			     1, splashModeMono1, gFalse);+    for (i = 0; i <= splashAASize * splashAASize; ++i) {+      aaGamma[i] = splashPow((SplashCoord)i /+			       (SplashCoord)(splashAASize * splashAASize),+			     1.5);+    }+  } else {+    aaBuf = NULL;+  }+  clearModRegion();+  debugMode = gFalse;+}++Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,+	       SplashScreen *screenA) {+  int i;++  bitmap = bitmapA;+  vectorAntialias = vectorAntialiasA;+  state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,+			  screenA);+  if (vectorAntialias) {+    aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,+			     1, splashModeMono1, gFalse);+    for (i = 0; i <= splashAASize * splashAASize; ++i) {+      aaGamma[i] = splashPow((SplashCoord)i /+			       (SplashCoord)(splashAASize * splashAASize),+			     1.5);+    }+  } else {+    aaBuf = NULL;+  }+  clearModRegion();+  debugMode = gFalse;+}++Splash::~Splash() {+  while (state->next) {+    restoreState();+  }+  delete state;+  if (vectorAntialias) {+    delete aaBuf;+  }+}++//------------------------------------------------------------------------+// 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;+}++SplashClip *Splash::getClip() {+  return state->clip;+}++SplashBitmap *Splash::getSoftMask() {+  return state->softMask;+}++GBool Splash::getInNonIsolatedGroup() {+  return state->inNonIsolatedGroup;+}++//------------------------------------------------------------------------+// 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) {+  state->lineCap = lineCap;+}++void Splash::setLineJoin(int lineJoin) {+  state->lineJoin = lineJoin;+}++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(GBool strokeAdjust) {+  state->strokeAdjust = strokeAdjust;+}++void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,+			     SplashCoord x1, SplashCoord y1) {+  state->clip->resetToRect(x0, y0, x1, y1);+}++SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,+			       SplashCoord x1, SplashCoord y1) {+  return state->clip->clipToRect(x0, y0, x1, y1);+}++SplashError Splash::clipToPath(SplashPath *path, GBool eo) {+  return state->clip->clipToPath(path, state->matrix, state->flatness, eo);+}++void Splash::setSoftMask(SplashBitmap *softMask) {+  state->setSoftMask(softMask);+}++void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,+				   int alpha0XA, int alpha0YA) {+  alpha0Bitmap = alpha0BitmapA;+  alpha0X = alpha0XA;+  alpha0Y = alpha0YA;+  state->inNonIsolatedGroup = gTrue;+}++//------------------------------------------------------------------------+// 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[2];+	  *p++ = color[1];+	  *p++ = color[0];+	}+	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[0];+	  *p++ = color[1];+	  *p++ = color[2];+	}+	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->width * bitmap->height);+  }++  updateModX(0);+  updateModY(0);+  updateModX(bitmap->width - 1);+  updateModY(bitmap->height - 1);+}++SplashError Splash::stroke(SplashPath *path) {+  SplashPath *path2, *dPath;++  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);+  if (state->lineDashLength > 0) {+    dPath = makeDashedPath(path2);+    delete path2;+    path2 = dPath;+  }+  if (state->lineWidth == 0) {+    strokeNarrow(path2);+  } else {+    strokeWide(path2);+  }+  delete path2;+  return splashOk;+}++void Splash::strokeNarrow(SplashPath *path) {+  SplashPipe pipe;+  SplashXPath *xPath;+  SplashXPathSeg *seg;+  int x0, x1, x2, x3, y0, y1, x, y, t;+  SplashCoord dx, dy, dxdy;+  SplashClipResult clipRes;+  int nClipRes[3];+  int i;++  nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;++  xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);++  pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha,+	   gFalse, gFalse);++  for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {++    x0 = splashFloor(seg->x0);+    x1 = splashFloor(seg->x1);+    y0 = splashFloor(seg->y0);+    y1 = splashFloor(seg->y1);++    // horizontal segment+    if (y0 == y1) {+      if (x0 > x1) {+	t = x0; x0 = x1; x1 = t;+      }+      if ((clipRes = state->clip->testSpan(x0, x1, y0))+	  != splashClipAllOutside) {+	drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);+      }++    // segment with |dx| > |dy|+    } else if (splashAbs(seg->dxdy) > 1) {+      dx = seg->x1 - seg->x0;+      dy = seg->y1 - seg->y0;+      dxdy = seg->dxdy;+      if (y0 > y1) {+	t = y0; y0 = y1; y1 = t;+	t = x0; x0 = x1; x1 = t;+	dx = -dx;+	dy = -dy;+      }+      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,+					   x0 <= x1 ? x1 : x0, y1))+	  != splashClipAllOutside) {+	if (dx > 0) {+	  x2 = x0;+	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);+	  drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0,+		   clipRes == splashClipAllInside);+	  x2 = x3;+	  for (y = y0 + 1; y <= y1 - 1; ++y) {+	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);+	    drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside);+	    x2 = x3;+	  }+	  drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1,+		   clipRes == splashClipAllInside);+	} else {+	  x2 = x0;+	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);+	  drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0,+		   clipRes == splashClipAllInside);+	  x2 = x3;+	  for (y = y0 + 1; y <= y1 - 1; ++y) {+	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);+	    drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside);+	    x2 = x3;+	  }+	  drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1,+		   clipRes == splashClipAllInside);+	}+      }++    // segment with |dy| > |dx|+    } else {+      dxdy = seg->dxdy;+      if (y0 > y1) {+	t = x0; x0 = x1; x1 = t;+	t = y0; y0 = y1; y1 = t;+      }+      if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,+					   x0 <= x1 ? x1 : x0, y1))+	  != splashClipAllOutside) {+	drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside);+	for (y = y0 + 1; y <= y1 - 1; ++y) {+	  x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy);+	  drawPixel(&pipe, x, y, clipRes == splashClipAllInside);+	}+	drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside);+      }+    }+    ++nClipRes[clipRes];+  }+  if (nClipRes[splashClipPartial] ||+      (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {+    opClipRes = splashClipPartial;+  } else if (nClipRes[splashClipAllInside]) {+    opClipRes = splashClipAllInside;+  } else {+    opClipRes = splashClipAllOutside;+  }++  delete xPath;+}++void Splash::strokeWide(SplashPath *path) {+  SplashPath *path2;++  path2 = makeStrokePath(path, 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();+  flatness2 = flatness * flatness;+  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);+    dx = tx - mx;+    dy = ty - my;+    d1 = dx*dx + dy*dy;+    transform(matrix, xx2, yy2, &tx, &ty);+    dx = tx - mx;+    dy = ty - my;+    d2 = dx*dx + dy*dy;++    // 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 = (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;+    }+  }+}++SplashPath *Splash::makeDashedPath(SplashPath *path) {+  SplashPath *dPath;+  SplashCoord lineDashTotal;+  SplashCoord lineDashStartPhase, lineDashDist, segLen;+  SplashCoord x0, y0, x1, y1, xa, ya;+  GBool lineDashStartOn, lineDashOn, newPath;+  int lineDashStartIdx, lineDashIdx;+  int i, j, k;++  lineDashTotal = 0;+  for (i = 0; i < state->lineDashLength; ++i) {+    lineDashTotal += state->lineDash[i];+  }+  lineDashStartPhase = state->lineDashPhase;+  i = splashFloor(lineDashStartPhase / lineDashTotal);+  lineDashStartPhase -= (SplashCoord)i * lineDashTotal;+  lineDashStartOn = gTrue;+  lineDashStartIdx = 0;+  while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {+    lineDashStartOn = !lineDashStartOn;+    lineDashStartPhase -= state->lineDash[lineDashStartIdx];+    ++lineDashStartIdx;+  }++  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;+    lineDashIdx = lineDashStartIdx;+    lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;++    // 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) {++	if (lineDashDist >= segLen) {+	  if (lineDashOn) {+	    if (newPath) {+	      dPath->moveTo(x0, y0);+	      newPath = gFalse;+	    }+	    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;+	    }+	    dPath->lineTo(xa, ya);+	  }+	  x0 = xa;+	  y0 = ya;+	  segLen -= lineDashDist;+	  lineDashDist = 0;+	}++	// 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;+	}+      }+    }+    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;+  SplashXPath *xPath;+  SplashXPathScanner *scanner;+  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;+  SplashClipResult clipRes, clipRes2;++  if (path->length == 0) {+    return splashErrEmptyPath;+  }+  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);+  if (vectorAntialias) {+    xPath->aaScale();+  }+  xPath->sort();+  scanner = new SplashXPathScanner(xPath, eo);++  // get the min and max x and y values+  if (vectorAntialias) {+    scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);+  } else {+    scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);+  }++  // check clipping+  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))+      != splashClipAllOutside) {++    // limit the y range+    if (yMinI < state->clip->getYMinI()) {+      yMinI = state->clip->getYMinI();+    }+    if (yMaxI > state->clip->getYMaxI()) {+      yMaxI = state->clip->getYMaxI();+    }++    pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse);++    // draw the spans+    if (vectorAntialias) {+      for (y = yMinI; y <= yMaxI; ++y) {+	scanner->renderAALine(aaBuf, &x0, &x1, y);+	if (clipRes != splashClipAllInside) {+	  state->clip->clipAALine(aaBuf, &x0, &x1, y);+	}+	drawAALine(&pipe, x0, x1, y);+      }+    } else {+      for (y = yMinI; y <= yMaxI; ++y) {+	while (scanner->getNextSpan(y, &x0, &x1)) {+	  if (clipRes == splashClipAllInside) {+	    drawSpan(&pipe, x0, x1, y, gTrue);+	  } else {+	    // limit the x range+	    if (x0 < state->clip->getXMinI()) {+	      x0 = state->clip->getXMinI();+	    }+	    if (x1 > state->clip->getXMaxI()) {+	      x1 = state->clip->getXMaxI();+	    }+	    clipRes2 = state->clip->testSpan(x0, x1, y);+	    drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);+	  }+	}+      }+    }+  }+  opClipRes = clipRes;++  delete scanner;+  delete xPath;+  return splashOk;+}++SplashError Splash::xorFill(SplashPath *path, GBool eo) {+  SplashPipe pipe;+  SplashXPath *xPath;+  SplashXPathScanner *scanner;+  int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;+  SplashClipResult clipRes, clipRes2;+  SplashBlendFunc origBlendFunc;++  if (path->length == 0) {+    return splashErrEmptyPath;+  }+  xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);+  xPath->sort();+  scanner = new SplashXPathScanner(xPath, eo);++  // get the min and max x and y values+  scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);++  // check clipping+  if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))+      != splashClipAllOutside) {++    // limit the y range+    if (yMinI < state->clip->getYMinI()) {+      yMinI = state->clip->getYMinI();+    }+    if (yMaxI > state->clip->getYMaxI()) {+      yMaxI = state->clip->getYMaxI();+    }++    origBlendFunc = state->blendFunc;+    state->blendFunc = &blendXor;+    pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse);++    // draw the spans+    for (y = yMinI; y <= yMaxI; ++y) {+      while (scanner->getNextSpan(y, &x0, &x1)) {+	if (clipRes == splashClipAllInside) {+	  drawSpan(&pipe, x0, x1, y, gTrue);+	} else {+	  // limit the x range+	  if (x0 < state->clip->getXMinI()) {+	    x0 = state->clip->getXMinI();+	  }+	  if (x1 > state->clip->getXMaxI()) {+	    x1 = state->clip->getXMaxI();+	  }+	  clipRes2 = state->clip->testSpan(x0, x1, y);+	  drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);+	}+      }+    }+    state->blendFunc = origBlendFunc;+  }+  opClipRes = clipRes;++  delete scanner;+  delete xPath;+  return splashOk;+}++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;+  GBool noClip;+  int alpha0, alpha;+  Guchar *p;+  int x1, y1, xx, xx1, yy;++  if ((clipRes = state->clip->testRect(x0 - glyph->x,+				       y0 - glyph->y,+				       x0 - glyph->x + glyph->w - 1,+				       y0 - glyph->y + glyph->h - 1))+      != splashClipAllOutside) {+    noClip = clipRes == splashClipAllInside;++    if (noClip) {+      if (glyph->aa) {+	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,+		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);+	p = glyph->data;+	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {+	  pipeSetXY(&pipe, x0 - glyph->x, y1);+	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {+	    alpha = *p++;+	    if (alpha != 0) {+	      pipe.shape = (SplashCoord)(alpha / 255.0);+	      pipeRun(&pipe);+	      updateModX(x1);+	      updateModY(y1);+	    } else {+	      pipeIncX(&pipe);+	    }+	  }+	}+      } else {+	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,+		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);+	p = glyph->data;+	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {+	  pipeSetXY(&pipe, x0 - glyph->x, y1);+	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {+	    alpha0 = *p++;+	    for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {+	      if (alpha0 & 0x80) {+		pipeRun(&pipe);+		updateModX(x1);+		updateModY(y1);+	      } else {+		pipeIncX(&pipe);+	      }+	      alpha0 <<= 1;+	    }+	  }+	}+      }+    } else {+      if (glyph->aa) {+	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,+		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);+	p = glyph->data;+	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {+	  pipeSetXY(&pipe, x0 - glyph->x, y1);+	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {+	    if (state->clip->test(x1, y1)) {+	      alpha = *p++;+	      if (alpha != 0) {+		pipe.shape = (SplashCoord)(alpha / 255.0);+		pipeRun(&pipe);+		updateModX(x1);+		updateModY(y1);+	      } else {+		pipeIncX(&pipe);+	      }+	    } else {+	      pipeIncX(&pipe);+	      ++p;+	    }+	  }+	}+      } else {+	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,+		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);+	p = glyph->data;+	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {+	  pipeSetXY(&pipe, x0 - glyph->x, y1);+	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {+	    alpha0 = *p++;+	    for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {+	      if (state->clip->test(x1, y1)) {+		if (alpha0 & 0x80) {+		  pipeRun(&pipe);+		  updateModX(x1);+		  updateModY(y1);+		} else {+		  pipeIncX(&pipe);+		}+	      } else {+		pipeIncX(&pipe);+	      }+	      alpha0 <<= 1;+	    }+	  }+	}+      }+    }+  }+  opClipRes = clipRes;++  return splashOk;+}++SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,+				  int w, int h, SplashCoord *mat,+				  GBool glyphMode) {+  SplashPipe pipe;+  GBool rot;+  SplashCoord xScale, yScale, xShear, yShear, yShear1;+  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;+  int ulx, uly, llx, lly, urx, ury, lrx, lry;+  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;+  int xMin, xMax, yMin, yMax;+  SplashClipResult clipRes, clipRes2;+  int yp, yq, yt, yStep, lastYStep;+  int xp, xq, xt, xStep, xSrc;+  int k1, spanXMin, spanXMax, spanY;+  SplashColorPtr pixBuf, p;+  int pixAcc;+  int x, y, x1, x2, y2;+  SplashCoord y1;+  int n, m, i, j;++  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 (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {+    return splashErrSingularMatrix;+  }++  // compute scale, shear, rotation, translation parameters+  rot = splashAbs(mat[1]) > splashAbs(mat[0]);+  if (rot) {+    xScale = -mat[1];+    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];+    xShear = -mat[3] / yScale;+    yShear = -mat[0] / mat[1];+  } else {+    xScale = mat[0];+    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];+    xShear = mat[2] / yScale;+    yShear = mat[1] / mat[0];+  }+  // Note 1: The PDF spec says that all pixels whose *centers* lie+  // within the region get painted -- but that doesn't seem to match+  // up with what Acrobat actually does: it ends up leaving gaps+  // between image stripes.  So we use the same rule here as for+  // fills: any pixel that overlaps the region gets painted.+  // Note 2: The "glyphMode" flag is a kludge: it switches back to+  // "correct" behavior (matching the spec), for use in rendering Type+  // 3 fonts.+  // Note 3: The +/-0.01 in these computations is to avoid floating+  // point precision problems which can lead to gaps between image+  // stripes (it can cause image stripes to overlap, but that's a much+  // less visible problem).+  if (glyphMode) {+    if (xScale >= 0) {+      tx = splashRound(mat[4]);+      tx2 = splashRound(mat[4] + xScale) - 1;+    } else {+      tx = splashRound(mat[4]) - 1;+      tx2 = splashRound(mat[4] + xScale);+    }+  } else {+    if (xScale >= 0) {+      tx = splashFloor(mat[4] - 0.01);+      tx2 = splashFloor(mat[4] + xScale + 0.01);+    } else {+      tx = splashFloor(mat[4] + 0.01);+      tx2 = splashFloor(mat[4] + xScale - 0.01);+    }+  }+  scaledWidth = abs(tx2 - tx) + 1;+  if (glyphMode) {+    if (yScale >= 0) {+      ty = splashRound(mat[5]);+      ty2 = splashRound(mat[5] + yScale) - 1;+    } else {+      ty = splashRound(mat[5]) - 1;+      ty2 = splashRound(mat[5] + yScale);+    }+  } else {+    if (yScale >= 0) {+      ty = splashFloor(mat[5] - 0.01);+      ty2 = splashFloor(mat[5] + yScale + 0.01);+    } else {+      ty = splashFloor(mat[5] + 0.01);+      ty2 = splashFloor(mat[5] + yScale - 0.01);+    }+  }+  scaledHeight = abs(ty2 - ty) + 1;+  xSign = (xScale < 0) ? -1 : 1;+  ySign = (yScale < 0) ? -1 : 1;+  yShear1 = (SplashCoord)xSign * yShear;++  // clipping+  ulx1 = 0;+  uly1 = 0;+  urx1 = xSign * (scaledWidth - 1);+  ury1 = (int)(yShear * urx1);+  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));+  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);+  lrx1 = xSign * (scaledWidth - 1) ++           splashRound(xShear * ySign * (scaledHeight - 1));+  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);+  if (rot) {+    ulx = tx + uly1;    uly = ty - ulx1;+    urx = tx + ury1;    ury = ty - urx1;+    llx = tx + lly1;    lly = ty - llx1;+    lrx = tx + lry1;    lry = ty - lrx1;+  } else {+    ulx = tx + ulx1;    uly = ty + uly1;+    urx = tx + urx1;    ury = ty + ury1;+    llx = tx + llx1;    lly = ty + lly1;+    lrx = tx + lrx1;    lry = ty + lry1;+  }+  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx+                                   : (llx < lrx) ? llx : lrx+		     : (urx < llx) ? (urx < lrx) ? urx : lrx+                                   : (llx < lrx) ? llx : lrx;+  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx+                                   : (llx > lrx) ? llx : lrx+		     : (urx > llx) ? (urx > lrx) ? urx : lrx+                                   : (llx > lrx) ? llx : lrx;+  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry+                                   : (lly < lry) ? lly : lry+		     : (ury < lly) ? (ury < lry) ? ury : lry+                                   : (lly < lry) ? lly : lry;+  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry+                                   : (lly > lry) ? lly : lry+		     : (ury > lly) ? (ury > lry) ? ury : lry+                                   : (lly > lry) ? lly : lry;+  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);+  opClipRes = clipRes;++  // compute Bresenham parameters for x and y scaling+  yp = h / scaledHeight;+  yq = h % scaledHeight;+  xp = w / scaledWidth;+  xq = w % scaledWidth;++  // allocate pixel buffer+  pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w);++  // initialize the pixel pipe+  pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,+	   gTrue, gFalse);+  if (vectorAntialias) {+    drawAAPixelInit();+  }++  // init y scale Bresenham+  yt = 0;+  lastYStep = 1;++  for (y = 0; y < scaledHeight; ++y) {++    // y scale Bresenham+    yStep = yp;+    yt += yq;+    if (yt >= scaledHeight) {+      yt -= scaledHeight;+      ++yStep;+    }++    // read row(s) from image+    n = (yp > 0) ? yStep : lastYStep;+    if (n > 0) {+      p = pixBuf;+      for (i = 0; i < n; ++i) {+	(*src)(srcData, p);+	p += w;+      }+    }+    lastYStep = yStep;++    // loop-invariant constants+    k1 = splashRound(xShear * ySign * y);++    // clipping test+    if (clipRes != splashClipAllInside &&+	!rot &&+	(int)(yShear * k1) ==+	  (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {+      if (xSign > 0) {+	spanXMin = tx + k1;+	spanXMax = spanXMin + (scaledWidth - 1);+      } else {+	spanXMax = tx + k1;+	spanXMin = spanXMax - (scaledWidth - 1);+      }+      spanY = ty + ySign * y + (int)(yShear * k1);+      clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);+      if (clipRes2 == splashClipAllOutside) {+	continue;+      }+    } else {+      clipRes2 = clipRes;+    }++    // init x scale Bresenham+    xt = 0;+    xSrc = 0;++    // x shear+    x1 = k1;++    // y shear+    y1 = (SplashCoord)ySign * y + yShear * x1;+    // this is a kludge: if yShear1 is negative, then (int)y1 would+    // change immediately after the first pixel, which is not what we+    // want+    if (yShear1 < 0) {+      y1 += 0.999;+    }++    // loop-invariant constants+    n = yStep > 0 ? yStep : 1;++    for (x = 0; x < scaledWidth; ++x) {++      // x scale Bresenham+      xStep = xp;+      xt += xq;+      if (xt >= scaledWidth) {+	xt -= scaledWidth;+	++xStep;+      }++      // rotation+      if (rot) {+	x2 = (int)y1;+	y2 = -x1;+      } else {+	x2 = x1;+	y2 = (int)y1;+      }++      // compute the alpha value for (x,y) after the x and y scaling+      // operations+      m = xStep > 0 ? xStep : 1;+      p = pixBuf + xSrc;+      pixAcc = 0;+      for (i = 0; i < n; ++i) {+	for (j = 0; j < m; ++j) {+	  pixAcc += *p++;+	}+	p += w - m;+      }++      // blend fill color with background+      if (pixAcc != 0) {+	pipe.shape = (pixAcc == n * m)+	                 ? (SplashCoord)1+	                 : (SplashCoord)pixAcc / (SplashCoord)(n * m);+	if (vectorAntialias && clipRes2 != splashClipAllInside) {+	  drawAAPixel(&pipe, tx + x2, ty + y2);+	} else {+	  drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside);+	}+      }++      // x scale Bresenham+      xSrc += xStep;++      // x shear+      x1 += xSign;++      // y shear+      y1 += yShear1;+    }+  }++  // free memory+  gfree(pixBuf);++  return splashOk;+}++SplashError Splash::drawImage(SplashImageSource src, void *srcData,+			      SplashColorMode srcMode, GBool srcAlpha,+			      int w, int h, SplashCoord *mat) {+  SplashPipe pipe;+  GBool ok, rot;+  SplashCoord xScale, yScale, xShear, yShear, yShear1;+  int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;+  int ulx, uly, llx, lly, urx, ury, lrx, lry;+  int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;+  int xMin, xMax, yMin, yMax;+  SplashClipResult clipRes, clipRes2;+  int yp, yq, yt, yStep, lastYStep;+  int xp, xq, xt, xStep, xSrc;+  int k1, spanXMin, spanXMax, spanY;+  SplashColorPtr colorBuf, p;+  SplashColor pix;+  Guchar *alphaBuf, *q;+#if SPLASH_CMYK+  int pixAcc0, pixAcc1, pixAcc2, pixAcc3;+#else+  int pixAcc0, pixAcc1, pixAcc2;+#endif+  int alphaAcc;+  SplashCoord pixMul, alphaMul, alpha;+  int x, y, x1, x2, y2;+  SplashCoord y1;+  int nComps, n, m, i, j;++  if (debugMode) {+    printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",+	   srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],+	   (double)mat[3], (double)mat[4], (double)mat[5]);+  }++  // check color modes+  ok = gFalse; // make gcc happy+  nComps = 0; // make gcc happy+  switch (bitmap->mode) {+  case splashModeMono1:+  case splashModeMono8:+    ok = srcMode == splashModeMono8;+    nComps = 1;+    break;+  case splashModeRGB8:+    ok = srcMode == splashModeRGB8;+    nComps = 3;+    break;+  case splashModeBGR8:+    ok = srcMode == splashModeBGR8;+    nComps = 3;+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    ok = srcMode == splashModeCMYK8;+    nComps = 4;+    break;+#endif+  }+  if (!ok) {+    return splashErrModeMismatch;+  }++  // check for singular matrix+  if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {+    return splashErrSingularMatrix;+  }++  // compute scale, shear, rotation, translation parameters+  rot = splashAbs(mat[1]) > splashAbs(mat[0]);+  if (rot) {+    xScale = -mat[1];+    yScale = mat[2] - (mat[0] * mat[3]) / mat[1];+    xShear = -mat[3] / yScale;+    yShear = -mat[0] / mat[1];+  } else {+    xScale = mat[0];+    yScale = mat[3] - (mat[1] * mat[2]) / mat[0];+    xShear = mat[2] / yScale;+    yShear = mat[1] / mat[0];+  }+  // Note 1: The PDF spec says that all pixels whose *centers* lie+  // within the region get painted -- but that doesn't seem to match+  // up with what Acrobat actually does: it ends up leaving gaps+  // between image stripes.  So we use the same rule here as for+  // fills: any pixel that overlaps the region gets painted.+  // Note 2: The +/-0.01 in these computations is to avoid floating+  // point precision problems which can lead to gaps between image+  // stripes (it can cause image stripes to overlap, but that's a much+  // less visible problem).+  if (xScale >= 0) {+    tx = splashFloor(mat[4] - 0.01);+    tx2 = splashFloor(mat[4] + xScale + 0.01);+  } else {+    tx = splashFloor(mat[4] + 0.01);+    tx2 = splashFloor(mat[4] + xScale - 0.01);+  }+  scaledWidth = abs(tx2 - tx) + 1;+  if (yScale >= 0) {+    ty = splashFloor(mat[5] - 0.01);+    ty2 = splashFloor(mat[5] + yScale + 0.01);+  } else {+    ty = splashFloor(mat[5] + 0.01);+    ty2 = splashFloor(mat[5] + yScale - 0.01);+  }+  scaledHeight = abs(ty2 - ty) + 1;+  xSign = (xScale < 0) ? -1 : 1;+  ySign = (yScale < 0) ? -1 : 1;+  yShear1 = (SplashCoord)xSign * yShear;++  // clipping+  ulx1 = 0;+  uly1 = 0;+  urx1 = xSign * (scaledWidth - 1);+  ury1 = (int)(yShear * urx1);+  llx1 = splashRound(xShear * ySign * (scaledHeight - 1));+  lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);+  lrx1 = xSign * (scaledWidth - 1) ++           splashRound(xShear * ySign * (scaledHeight - 1));+  lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);+  if (rot) {+    ulx = tx + uly1;    uly = ty - ulx1;+    urx = tx + ury1;    ury = ty - urx1;+    llx = tx + lly1;    lly = ty - llx1;+    lrx = tx + lry1;    lry = ty - lrx1;+  } else {+    ulx = tx + ulx1;    uly = ty + uly1;+    urx = tx + urx1;    ury = ty + ury1;+    llx = tx + llx1;    lly = ty + lly1;+    lrx = tx + lrx1;    lry = ty + lry1;+  }+  xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx+                                   : (llx < lrx) ? llx : lrx+		     : (urx < llx) ? (urx < lrx) ? urx : lrx+                                   : (llx < lrx) ? llx : lrx;+  xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx+                                   : (llx > lrx) ? llx : lrx+		     : (urx > llx) ? (urx > lrx) ? urx : lrx+                                   : (llx > lrx) ? llx : lrx;+  yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry+                                   : (lly < lry) ? lly : lry+		     : (ury < lly) ? (ury < lry) ? ury : lry+                                   : (lly < lry) ? lly : lry;+  yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry+                                   : (lly > lry) ? lly : lry+		     : (ury > lly) ? (ury > lry) ? ury : lry+                                   : (lly > lry) ? lly : lry;+  clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);+  opClipRes = clipRes;+  if (clipRes == splashClipAllOutside) {+    return splashOk;+  }++  // compute Bresenham parameters for x and y scaling+  yp = h / scaledHeight;+  yq = h % scaledHeight;+  xp = w / scaledWidth;+  xq = w % scaledWidth;++  // allocate pixel buffers+  colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);+  if (srcAlpha) {+    alphaBuf = (Guchar *)gmalloc((yp + 1) * w);+  } else {+    alphaBuf = NULL;+  }++  pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy+#if SPLASH_CMYK+  pixAcc3 = 0; // make gcc happy+#endif++  // initialize the pixel pipe+  pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha,+	   srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),+	   gFalse);+  if (vectorAntialias) {+    drawAAPixelInit();+  }++  if (srcAlpha) {++    // init y scale Bresenham+    yt = 0;+    lastYStep = 1;++    for (y = 0; y < scaledHeight; ++y) {++      // y scale Bresenham+      yStep = yp;+      yt += yq;+      if (yt >= scaledHeight) {+	yt -= scaledHeight;+	++yStep;+      }++      // read row(s) from image+      n = (yp > 0) ? yStep : lastYStep;+      if (n > 0) {+	p = colorBuf;+	q = alphaBuf;+	for (i = 0; i < n; ++i) {+	  (*src)(srcData, p, q);+	  p += w * nComps;+	  q += w;+	}+      }+      lastYStep = yStep;++      // loop-invariant constants+      k1 = splashRound(xShear * ySign * y);++      // clipping test+      if (clipRes != splashClipAllInside &&+	  !rot &&+	  (int)(yShear * k1) ==+	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {+	if (xSign > 0) {+	  spanXMin = tx + k1;+	  spanXMax = spanXMin + (scaledWidth - 1);+	} else {+	  spanXMax = tx + k1;+	  spanXMin = spanXMax - (scaledWidth - 1);+	}+	spanY = ty + ySign * y + (int)(yShear * k1);+	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);+	if (clipRes2 == splashClipAllOutside) {+	  continue;+	}+      } else {+	clipRes2 = clipRes;+      }++      // init x scale Bresenham+      xt = 0;+      xSrc = 0;++      // x shear+      x1 = k1;++      // y shear+      y1 = (SplashCoord)ySign * y + yShear * x1;+      // this is a kludge: if yShear1 is negative, then (int)y1 would+      // change immediately after the first pixel, which is not what+      // we want+      if (yShear1 < 0) {+	y1 += 0.999;+      }++      // loop-invariant constants+      n = yStep > 0 ? yStep : 1;++      switch (srcMode) {++      case splashModeMono1:+      case splashModeMono8:+	for (x = 0; x < scaledWidth; ++x) {++	  // x scale Bresenham+	  xStep = xp;+	  xt += xq;+	  if (xt >= scaledWidth) {+	    xt -= scaledWidth;+	    ++xStep;+	  }++	  // rotation+	  if (rot) {+	    x2 = (int)y1;+	    y2 = -x1;+	  } else {+	    x2 = x1;+	    y2 = (int)y1;+	  }++	  // compute the filtered pixel at (x,y) after the x and y scaling+	  // operations+	  m = xStep > 0 ? xStep : 1;+	  alphaAcc = 0;+	  p = colorBuf + xSrc;+	  q = alphaBuf + xSrc;+	  pixAcc0 = 0;+	  for (i = 0; i < n; ++i) {+	    for (j = 0; j < m; ++j) {+	      pixAcc0 += *p++;+	      alphaAcc += *q++;+	    }+	    p += w - m;+	    q += w - m;+	  }+	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);+	  alphaMul = pixMul * (1.0 / 255.0);+	  alpha = (SplashCoord)alphaAcc * alphaMul;++	  if (alpha > 0) {+	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);++	    // set pixel+	    pipe.shape = alpha;+	    if (vectorAntialias && clipRes != splashClipAllInside) {+	      drawAAPixel(&pipe, tx + x2, ty + y2);+	    } else {+	      drawPixel(&pipe, tx + x2, ty + y2,+			clipRes2 == splashClipAllInside);+	    }+	  }++	  // x scale Bresenham+	  xSrc += xStep;++	  // x shear+	  x1 += xSign;++	  // y shear+	  y1 += yShear1;+	}+	break;++      case splashModeRGB8:+      case splashModeBGR8:+	for (x = 0; x < scaledWidth; ++x) {++	  // x scale Bresenham+	  xStep = xp;+	  xt += xq;+	  if (xt >= scaledWidth) {+	    xt -= scaledWidth;+	    ++xStep;+	  }++	  // rotation+	  if (rot) {+	    x2 = (int)y1;+	    y2 = -x1;+	  } else {+	    x2 = x1;+	    y2 = (int)y1;+	  }++	  // compute the filtered pixel at (x,y) after the x and y scaling+	  // operations+	  m = xStep > 0 ? xStep : 1;+	  alphaAcc = 0;+	  p = colorBuf + xSrc * 3;+	  q = alphaBuf + xSrc;+	  pixAcc0 = pixAcc1 = pixAcc2 = 0;+	  for (i = 0; i < n; ++i) {+	    for (j = 0; j < m; ++j) {+	      pixAcc0 += *p++;+	      pixAcc1 += *p++;+	      pixAcc2 += *p++;+	      alphaAcc += *q++;+	    }+	    p += 3 * (w - m);+	    q += w - m;+	  }+	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);+	  alphaMul = pixMul * (1.0 / 255.0);+	  alpha = (SplashCoord)alphaAcc * alphaMul;++	  if (alpha > 0) {+	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);+	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);+	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);++	    // set pixel+	    pipe.shape = alpha;+	    if (vectorAntialias && clipRes != splashClipAllInside) {+	      drawAAPixel(&pipe, tx + x2, ty + y2);+	    } else {+	      drawPixel(&pipe, tx + x2, ty + y2,+			clipRes2 == splashClipAllInside);+	    }+	  }++	  // x scale Bresenham+	  xSrc += xStep;++	  // x shear+	  x1 += xSign;++	  // y shear+	  y1 += yShear1;+	}+	break;++#if SPLASH_CMYK+      case splashModeCMYK8:+	for (x = 0; x < scaledWidth; ++x) {++	  // x scale Bresenham+	  xStep = xp;+	  xt += xq;+	  if (xt >= scaledWidth) {+	    xt -= scaledWidth;+	    ++xStep;+	  }++	  // rotation+	  if (rot) {+	    x2 = (int)y1;+	    y2 = -x1;+	  } else {+	    x2 = x1;+	    y2 = (int)y1;+	  }++	  // compute the filtered pixel at (x,y) after the x and y scaling+	  // operations+	  m = xStep > 0 ? xStep : 1;+	  alphaAcc = 0;+	  p = colorBuf + xSrc * 4;+	  q = alphaBuf + xSrc;+	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;+	  for (i = 0; i < n; ++i) {+	    for (j = 0; j < m; ++j) {+	      pixAcc0 += *p++;+	      pixAcc1 += *p++;+	      pixAcc2 += *p++;+	      pixAcc3 += *p++;+	      alphaAcc += *q++;+	    }+	    p += 4 * (w - m);+	    q += w - m;+	  }+	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);+	  alphaMul = pixMul * (1.0 / 255.0);+	  alpha = (SplashCoord)alphaAcc * alphaMul;++	  if (alpha > 0) {+	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);+	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);+	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);+	    pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);++	    // set pixel+	    pipe.shape = alpha;+	    if (vectorAntialias && clipRes != splashClipAllInside) {+	      drawAAPixel(&pipe, tx + x2, ty + y2);+	    } else {+	      drawPixel(&pipe, tx + x2, ty + y2,+			clipRes2 == splashClipAllInside);+	    }+	  }++	  // x scale Bresenham+	  xSrc += xStep;++	  // x shear+	  x1 += xSign;++	  // y shear+	  y1 += yShear1;+	}+	break;+#endif // SPLASH_CMYK+      }+    }++  } else {++    // init y scale Bresenham+    yt = 0;+    lastYStep = 1;++    for (y = 0; y < scaledHeight; ++y) {++      // y scale Bresenham+      yStep = yp;+      yt += yq;+      if (yt >= scaledHeight) {+	yt -= scaledHeight;+	++yStep;+      }++      // read row(s) from image+      n = (yp > 0) ? yStep : lastYStep;+      if (n > 0) {+	p = colorBuf;+	for (i = 0; i < n; ++i) {+	  (*src)(srcData, p, NULL);+	  p += w * nComps;+	}+      }+      lastYStep = yStep;++      // loop-invariant constants+      k1 = splashRound(xShear * ySign * y);++      // clipping test+      if (clipRes != splashClipAllInside &&+	  !rot &&+	  (int)(yShear * k1) ==+	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {+	if (xSign > 0) {+	  spanXMin = tx + k1;+	  spanXMax = spanXMin + (scaledWidth - 1);+	} else {+	  spanXMax = tx + k1;+	  spanXMin = spanXMax - (scaledWidth - 1);+	}+	spanY = ty + ySign * y + (int)(yShear * k1);+	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);+	if (clipRes2 == splashClipAllOutside) {+	  continue;+	}+      } else {+	clipRes2 = clipRes;+      }++      // init x scale Bresenham+      xt = 0;+      xSrc = 0;++      // x shear+      x1 = k1;++      // y shear+      y1 = (SplashCoord)ySign * y + yShear * x1;+      // this is a kludge: if yShear1 is negative, then (int)y1 would+      // change immediately after the first pixel, which is not what+      // we want+      if (yShear1 < 0) {+	y1 += 0.999;+      }++      // loop-invariant constants+      n = yStep > 0 ? yStep : 1;++      switch (srcMode) {++      case splashModeMono1:+      case splashModeMono8:+	for (x = 0; x < scaledWidth; ++x) {++	  // x scale Bresenham+	  xStep = xp;+	  xt += xq;+	  if (xt >= scaledWidth) {+	    xt -= scaledWidth;+	    ++xStep;+	  }++	  // rotation+	  if (rot) {+	    x2 = (int)y1;+	    y2 = -x1;+	  } else {+	    x2 = x1;+	    y2 = (int)y1;+	  }++	  // compute the filtered pixel at (x,y) after the x and y scaling+	  // operations+	  m = xStep > 0 ? xStep : 1;+	  p = colorBuf + xSrc;+	  pixAcc0 = 0;+	  for (i = 0; i < n; ++i) {+	    for (j = 0; j < m; ++j) {+	      pixAcc0 += *p++;+	    }+	    p += w - m;+	  }+	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);++	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);++	  // set pixel+	  if (vectorAntialias && clipRes != splashClipAllInside) {+	    pipe.shape = (SplashCoord)1;+	    drawAAPixel(&pipe, tx + x2, ty + y2);+	  } else {+	    drawPixel(&pipe, tx + x2, ty + y2,+		      clipRes2 == splashClipAllInside);+	  }++	  // x scale Bresenham+	  xSrc += xStep;++	  // x shear+	  x1 += xSign;++	  // y shear+	  y1 += yShear1;+	}+	break;++      case splashModeRGB8:+      case splashModeBGR8:+	for (x = 0; x < scaledWidth; ++x) {++	  // x scale Bresenham+	  xStep = xp;+	  xt += xq;+	  if (xt >= scaledWidth) {+	    xt -= scaledWidth;+	    ++xStep;+	  }++	  // rotation+	  if (rot) {+	    x2 = (int)y1;+	    y2 = -x1;+	  } else {+	    x2 = x1;+	    y2 = (int)y1;+	  }++	  // compute the filtered pixel at (x,y) after the x and y scaling+	  // operations+	  m = xStep > 0 ? xStep : 1;+	  p = colorBuf + xSrc * 3;+	  pixAcc0 = pixAcc1 = pixAcc2 = 0;+	  for (i = 0; i < n; ++i) {+	    for (j = 0; j < m; ++j) {+	      pixAcc0 += *p++;+	      pixAcc1 += *p++;+	      pixAcc2 += *p++;+	    }+	    p += 3 * (w - m);+	  }+	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);++	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);+	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);+	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);++	  // set pixel+	  if (vectorAntialias && clipRes != splashClipAllInside) {+	    pipe.shape = (SplashCoord)1;+	    drawAAPixel(&pipe, tx + x2, ty + y2);+	  } else {+	    drawPixel(&pipe, tx + x2, ty + y2,+		      clipRes2 == splashClipAllInside);+	  }++	  // x scale Bresenham+	  xSrc += xStep;++	  // x shear+	  x1 += xSign;++	  // y shear+	  y1 += yShear1;+	}+	break;++#if SPLASH_CMYK+      case splashModeCMYK8:+	for (x = 0; x < scaledWidth; ++x) {++	  // x scale Bresenham+	  xStep = xp;+	  xt += xq;+	  if (xt >= scaledWidth) {+	    xt -= scaledWidth;+	    ++xStep;+	  }++	  // rotation+	  if (rot) {+	    x2 = (int)y1;+	    y2 = -x1;+	  } else {+	    x2 = x1;+	    y2 = (int)y1;+	  }++	  // compute the filtered pixel at (x,y) after the x and y scaling+	  // operations+	  m = xStep > 0 ? xStep : 1;+	  p = colorBuf + xSrc * 4;+	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;+	  for (i = 0; i < n; ++i) {+	    for (j = 0; j < m; ++j) {+	      pixAcc0 += *p++;+	      pixAcc1 += *p++;+	      pixAcc2 += *p++;+	      pixAcc3 += *p++;+	    }+	    p += 4 * (w - m);+	  }+	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);++	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);+	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);+	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);+	  pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);++	  // set pixel+	  if (vectorAntialias && clipRes != splashClipAllInside) {+	    pipe.shape = (SplashCoord)1;+	    drawAAPixel(&pipe, tx + x2, ty + y2);+	  } else {+	    drawPixel(&pipe, tx + x2, ty + y2,+		      clipRes2 == splashClipAllInside);+	  }++	  // x scale Bresenham+	  xSrc += xStep;++	  // x shear+	  x1 += xSign;++	  // y shear+	  y1 += yShear1;+	}+	break;+#endif // SPLASH_CMYK+      }+    }++  }++  gfree(colorBuf);+  gfree(alphaBuf);++  return splashOk;+}++SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,+			      int xDest, int yDest, int w, int h,+			      GBool noClip, GBool nonIsolated) {+  SplashPipe pipe;+  SplashColor pixel;+  Guchar alpha;+  Guchar *ap;+  int x, y;++  if (src->mode != bitmap->mode) {+    return splashErrModeMismatch;+  }++  if (src->alpha) {+    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,+	     gTrue, nonIsolated);+    for (y = 0; y < h; ++y) {+      pipeSetXY(&pipe, xDest, yDest + y);+      ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;+      for (x = 0; x < w; ++x) {+	src->getPixel(xSrc + x, ySrc + y, pixel);+	alpha = *ap++;+	if (noClip || state->clip->test(xDest + x, yDest + y)) {+	  // this uses shape instead of alpha, which isn't technically+	  // correct, but works out the same+	  pipe.shape = (SplashCoord)(alpha / 255.0);+	  pipeRun(&pipe);+	  updateModX(xDest + x);+	  updateModY(yDest + y);+	} else {+	  pipeIncX(&pipe);+	}+      }+    }+  } else {+    pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,+	     gFalse, nonIsolated);+    for (y = 0; y < h; ++y) {+      pipeSetXY(&pipe, xDest, yDest + y);+      for (x = 0; x < w; ++x) {+	src->getPixel(xSrc + x, ySrc + y, pixel);+	if (noClip || state->clip->test(xDest + x, yDest + y)) {+	  pipeRun(&pipe);+	  updateModX(xDest + x);+	  updateModY(yDest + y);+	} else {+	  pipeIncX(&pipe);+	}+      }+    }+  }++  return splashOk;+}++void Splash::compositeBackground(SplashColorPtr color) {+  SplashColorPtr p;+  Guchar *q;+  Guchar alpha, alpha1, c, color0, color1, color2, color3;+  int x, y, mask;++  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->width];+      mask = 0x80;+      for (x = 0; x < bitmap->width; ++x) {+	alpha = *q++;+	alpha1 = 255 - alpha;+	c = (*p & mask) ? 0xff : 0x00;+	c = div255(alpha1 * color0 + alpha * c);+	if (c & 0x80) {+	  *p |= mask;+	} else {+	  *p &= ~mask;+	}+	if (!(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->width];+      for (x = 0; x < bitmap->width; ++x) {+	alpha = *q++;+	alpha1 = 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->width];+      for (x = 0; x < bitmap->width; ++x) {+	alpha = *q++;+	alpha1 = 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->width];+      for (x = 0; x < bitmap->width; ++x) {+	alpha = *q++;+	alpha1 = 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->width * bitmap->height);+}++SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,+				    int xDest, int yDest, int w, int h) {+  SplashColor pixel;+  SplashColorPtr p;+  Guchar *q;+  int x, y, mask;++  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 = 0x80 >> (xDest & 7);+      for (x = 0; x < w; ++x) {+	src->getPixel(xSrc + x, ySrc + y, pixel);+	if (pixel[0]) {+	  *p |= mask;+	} else {+	  *p &= ~mask;+	}+	if (!(mask >>= 1)) {+	  mask = 0x80;+	  ++p;+	}+      }+    }+    break;+  case splashModeMono8:+    for (y = 0; y < h; ++y) {+      p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];+      for (x = 0; x < w; ++x) {+	src->getPixel(xSrc + x, ySrc + y, pixel);+	*p++ = pixel[0];+      }+    }+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    for (y = 0; y < h; ++y) {+      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];+      for (x = 0; x < w; ++x) {+	src->getPixel(xSrc + x, ySrc + y, pixel);+	*p++ = pixel[0];+	*p++ = pixel[1];+	*p++ = pixel[2];+      }+    }+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    for (y = 0; y < h; ++y) {+      p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];+      for (x = 0; x < w; ++x) {+	src->getPixel(xSrc + x, ySrc + y, pixel);+	*p++ = pixel[0];+	*p++ = pixel[1];+	*p++ = pixel[2];+	*p++ = pixel[3];+      }+    }+    break;+#endif+  }++  if (bitmap->alpha) {+    for (y = 0; y < h; ++y) {+      q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];+      for (x = 0; x < w; ++x) {+	*q++ = 0x00;+      }+    }+  }++  return splashOk;+}++SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) {+  SplashPath *pathIn, *pathOut;+  SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;+  SplashCoord crossprod, dotprod, miter, m;+  GBool first, last, closed;+  int subpathStart, next, i;+  int left0, left1, left2, right0, right1, right2, join0, join1, join2;+  int leftFirst, rightFirst, firstPt;++  if (flatten) {+    pathIn = flattenPath(path, state->matrix, state->flatness);+    if (state->lineDashLength > 0) {+      pathOut = makeDashedPath(pathIn);+      delete pathIn;+      pathIn = pathOut;+    }+  } else {+    pathIn = path;+  }++  subpathStart = 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++  pathOut = new SplashPath();+  w = state->lineWidth;++  for (i = 0; i < pathIn->length - 1; ++i) {+    if (pathIn->flags[i] & splashPathLast) {+      continue;+    }+    if ((first = pathIn->flags[i] & splashPathFirst)) {+      subpathStart = i;+      closed = pathIn->flags[i] & splashPathClosed;+    }+    last = pathIn->flags[i+1] & splashPathLast;++    // compute the deltas for segment (i, i+1)+    d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y,+		   pathIn->pts[i+1].x, pathIn->pts[i+1].y);+    if (d == 0) {+      // we need to draw end caps on zero-length lines+      //~ not clear what the behavior should be for splashLineCapButt+      //~   with d==0+      dx = 0;+      dy = 1;+    } else {+      d = (SplashCoord)1 / d;+      dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x);+      dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y);+    }+    wdx = (SplashCoord)0.5 * w * dx;+    wdy = (SplashCoord)0.5 * w * dy;++    // compute the deltas for segment (i+1, next)+    next = last ? subpathStart + 1 : i + 2;+    d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y,+		   pathIn->pts[next].x, pathIn->pts[next].y);+    if (d == 0) {+      // we need to draw end caps on zero-length lines+      //~ not clear what the behavior should be for splashLineCapButt+      //~   with d==0+      dxNext = 0;+      dyNext = 1;+    } else {+      d = (SplashCoord)1 / d;+      dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x);+      dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y);+    }+    wdxNext = (SplashCoord)0.5 * w * dxNext;+    wdyNext = (SplashCoord)0.5 * w * dyNext;++    // draw the start cap+    pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx);+    if (i == subpathStart) {+      firstPt = pathOut->length - 1;+    }+    if (first && !closed) {+      switch (state->lineCap) {+      case splashLineCapButt:+	pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);+	break;+      case splashLineCapRound:+	pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx,+			 pathIn->pts[i].y + wdx - bezierCircle * wdy,+			 pathIn->pts[i].x - wdx - bezierCircle * wdy,+			 pathIn->pts[i].y - wdy + bezierCircle * wdx,+			 pathIn->pts[i].x - wdx,+			 pathIn->pts[i].y - wdy);+	pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy,+			 pathIn->pts[i].y - wdy - bezierCircle * wdx,+			 pathIn->pts[i].x + wdy - bezierCircle * wdx,+			 pathIn->pts[i].y - wdx - bezierCircle * wdy,+			 pathIn->pts[i].x + wdy,+			 pathIn->pts[i].y - wdx);+	break;+      case splashLineCapProjecting:+	pathOut->lineTo(pathIn->pts[i].x - wdx - wdy,+			pathIn->pts[i].y + wdx - wdy);+	pathOut->lineTo(pathIn->pts[i].x - wdx + wdy,+			pathIn->pts[i].y - wdx - wdy);+	pathOut->lineTo(pathIn->pts[i].x + wdy,+			pathIn->pts[i].y - wdx);+	break;+      }+    } else {+      pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);+    }++    // draw the left side of the segment rectangle+    left2 = pathOut->length - 1;+    pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx);++    // draw the end cap+    if (last && !closed) {+      switch (state->lineCap) {+      case splashLineCapButt:+	pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);+	break;+      case splashLineCapRound:+	pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx,+			 pathIn->pts[i+1].y - wdx + bezierCircle * wdy,+			 pathIn->pts[i+1].x + wdx + bezierCircle * wdy,+			 pathIn->pts[i+1].y + wdy - bezierCircle * wdx,+			 pathIn->pts[i+1].x + wdx,+			 pathIn->pts[i+1].y + wdy);+	pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy,+			 pathIn->pts[i+1].y + wdy + bezierCircle * wdx,+			 pathIn->pts[i+1].x - wdy + bezierCircle * wdx,+			 pathIn->pts[i+1].y + wdx + bezierCircle * wdy,+			 pathIn->pts[i+1].x - wdy,+			 pathIn->pts[i+1].y + wdx);+	break;+      case splashLineCapProjecting:+	pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx,+			pathIn->pts[i+1].y - wdx + wdy);+	pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx,+			pathIn->pts[i+1].y + wdx + wdy);+	pathOut->lineTo(pathIn->pts[i+1].x - wdy,+			pathIn->pts[i+1].y + wdx);+	break;+      }+    } else {+      pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);+    }++    // draw the right side of the segment rectangle+    right2 = pathOut->length - 1;+    pathOut->close();++    // draw the join+    join2 = pathOut->length;+    if (!last || closed) {+      crossprod = dx * dyNext - dy * dxNext;+      dotprod = -(dx * dxNext + dy * dyNext);+      if (dotprod > 0.99999) {+	// 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)+	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 (state->lineJoin == splashLineJoinRound) {+	pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,+			pathIn->pts[i+1].y);+	pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].y + bezierCircle2 * w,+			 pathIn->pts[i+1].x + bezierCircle2 * w,+			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].x,+			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w);+	pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w,+			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].y + bezierCircle2 * w,+			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].y);+	pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].y - bezierCircle2 * w,+			 pathIn->pts[i+1].x - bezierCircle2 * w,+			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].x,+			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w);+	pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w,+			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].y - bezierCircle2 * w,+			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,+			 pathIn->pts[i+1].y);++      } else {+	pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y);++	// angle < 180+	if (crossprod < 0) {+	  pathOut->lineTo(pathIn->pts[i+1].x - wdyNext,+			  pathIn->pts[i+1].y + wdxNext);+	  // miter join inside limit+	  if (state->lineJoin == splashLineJoinMiter &&+	      splashSqrt(miter) <= state->miterLimit) {+	    pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m,+			    pathIn->pts[i+1].y + wdx + wdy * m);+	    pathOut->lineTo(pathIn->pts[i+1].x - wdy,+			    pathIn->pts[i+1].y + wdx);+	  // bevel join or miter join outside limit+	  } else {+	    pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);+	  }++	// angle >= 180+	} else {+	  pathOut->lineTo(pathIn->pts[i+1].x + wdy,+			  pathIn->pts[i+1].y - wdx);+	  // miter join inside limit+	  if (state->lineJoin == splashLineJoinMiter &&+	      splashSqrt(miter) <= state->miterLimit) {+	    pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m,+			    pathIn->pts[i+1].y - wdx + wdy * m);+	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,+			    pathIn->pts[i+1].y - wdxNext);+	  // bevel join or miter join outside limit+	  } else {+	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,+			    pathIn->pts[i+1].y - wdxNext);+	  }+	}+      }++      pathOut->close();+    }++    // add stroke adjustment hints+    if (state->strokeAdjust) {+      if (i >= subpathStart + 1) {+	if (i >= subpathStart + 2) {+	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);+	  pathOut->addStrokeAdjustHint(left1, right1, join0, left2);+	} else {+	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);+	}+	pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);+      }+      left0 = left1;+      left1 = left2;+      right0 = right1;+      right1 = right2;+      join0 = join1;+      join1 = join2;+      if (i == subpathStart) {+	leftFirst = left2;+	rightFirst = right2;+      }+      if (last) {+	if (i >= subpathStart + 2) {+	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);+	  pathOut->addStrokeAdjustHint(left1, right1,+				       join0, pathOut->length - 1);+	} else {+	  pathOut->addStrokeAdjustHint(left1, right1,+				       firstPt, pathOut->length - 1);+	}+	if (closed) {+	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);+	  pathOut->addStrokeAdjustHint(left1, right1,+				       rightFirst + 1, rightFirst + 1);+	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,+				       left1 + 1, right1);+	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,+				       join1, pathOut->length - 1);+	}+      }+    }+  }++  if (pathIn != path) {+    delete pathIn;+  }++  return pathOut;+}++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" : "");+  }+}++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 %s%s%s%s%s%s%s\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].flags	& splashXPathFirst) ? "F" : " ",+	   (path->segs[i].flags	& splashXPathLast) ? "L" : " ",+	   (path->segs[i].flags	& splashXPathEnd0) ? "0" : " ",+	   (path->segs[i].flags	& splashXPathEnd1) ? "1" : " ",+	   (path->segs[i].flags	& splashXPathHoriz) ? "H" : " ",+	   (path->segs[i].flags	& splashXPathVert) ? "V" : " ",+	   (path->segs[i].flags	& splashXPathFlip) ? "P" : " ");+  }+}
+ xpdf/Splash.h view
@@ -0,0 +1,293 @@+//========================================================================+//+// Splash.h+//+//========================================================================++#ifndef SPLASH_H+#define SPLASH_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashTypes.h"+#include "SplashClip.h"++class Splash;+class SplashBitmap;+struct SplashGlyphBitmap;+class SplashState;+class SplashPattern;+class SplashScreen;+class SplashPath;+class SplashXPath;+class SplashFont;+struct SplashPipe;++//------------------------------------------------------------------------++// 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, SplashColorPtr 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 {+#if SPLASH_CMYK+  splashPipeResultColorNoAlphaBlendCMYK,+#endif+  splashPipeResultColorNoAlphaBlendRGB,+  splashPipeResultColorNoAlphaBlendMono,+  splashPipeResultColorAlphaNoBlendMono,+  splashPipeResultColorAlphaNoBlendRGB,+#if SPLASH_CMYK+  splashPipeResultColorAlphaNoBlendCMYK,+#endif+  splashPipeResultColorAlphaBlendMono,+  splashPipeResultColorAlphaBlendRGB+#if SPLASH_CMYK+  ,+  splashPipeResultColorAlphaBlendCMYK+#endif+};++//------------------------------------------------------------------------+// Splash+//------------------------------------------------------------------------++class Splash {+public:++  // Create a new rasterizer object.+  Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,+	 SplashScreenParams *screenParams = NULL);+  Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,+	 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();+  SplashClip *getClip();+  SplashBitmap *getSoftMask();+  GBool getInNonIsolatedGroup();++  //----- 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(GBool 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 setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,+			     int alpha0XA, int alpha0YA);++  //----- 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);++  // Fill a path, XORing with the current fill pattern.+  SplashError xorFill(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(SplashImageMaskSource src, void *srcData,+			    int w, int h, SplashCoord *mat,+			    GBool glyphMode);++  // 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+  //    ------       ------+  //    Mono1        Mono1+  //    Mono8        Mono1   -- with dithering+  //    Mono8        Mono8+  //    RGB8         RGB8+  //    BGR8         BGR8+  //    CMYK8        CMYK8+  // The matrix behaves as for fillImageMask.+  SplashError drawImage(SplashImageSource src, void *srcData,+			SplashColorMode srcMode, GBool srcAlpha,+			int w, int h, SplashCoord *mat);++  // 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 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);++  //----- misc++  // Construct a path for a stroke, given the path to be stroked, and+  // using the current line parameters.  If <flatten> is true, this+  // function will first flatten the path and handle the linedash.+  SplashPath *makeStrokePath(SplashPath *path, GBool flatten = gTrue);++  // Return the associated bitmap.+  SplashBitmap *getBitmap() { return bitmap; }++  // 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; }++#if 1 //~tmp: turn off anti-aliasing temporarily+  GBool getVectorAntialias() { return vectorAntialias; }+  void setVectorAntialias(GBool vaa) { vectorAntialias = vaa; }+#endif++private:++  void pipeInit(SplashPipe *pipe, int x, int y,+		SplashPattern *pattern, SplashColorPtr cSrc,+		SplashCoord aInput, GBool usesShape,+		GBool nonIsolatedGroup);+  void pipeRun(SplashPipe *pipe);+  void pipeSetXY(SplashPipe *pipe, int x, int y);+  void pipeIncX(SplashPipe *pipe);+  void drawPixel(SplashPipe *pipe, int x, int y, GBool noClip);+  void drawAAPixelInit();+  void drawAAPixel(SplashPipe *pipe, int x, int y);+  void drawSpan(SplashPipe *pipe, int x0, int x1, int y, GBool noClip);+  void drawAALine(SplashPipe *pipe, int x0, int x1, 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 strokeWide(SplashPath *path);+  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);+  SplashError fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph);+  void dumpPath(SplashPath *path);+  void dumpXPath(SplashXPath *path);++  static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[];+  static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[];+  static SplashPipeResultColorCtrl pipeResultColorAlphaBlend[];+  static int pipeNonIsoGroupCorrection[];++  SplashBitmap *bitmap;+  SplashState *state;+  SplashBitmap *aaBuf;+  int aaBufY;+  SplashBitmap *alpha0Bitmap;	// for non-isolated groups, this is the+				//   bitmap containing the alpha0 values+  int alpha0X, alpha0Y;		// offset within alpha0Bitmap+  SplashCoord aaGamma[splashAASize * splashAASize + 1];+  int modXMin, modYMin, modXMax, modYMax;+  SplashClipResult opClipRes;+  GBool vectorAntialias;+  GBool debugMode;+};++#endif
+ xpdf/SplashBitmap.cc view
@@ -0,0 +1,188 @@+//========================================================================+//+// SplashBitmap.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdio.h>+#include "gmem.h"+#include "SplashErrorCodes.h"+#include "SplashBitmap.h"++//------------------------------------------------------------------------+// SplashBitmap+//------------------------------------------------------------------------++SplashBitmap::SplashBitmap(int widthA, int heightA, int rowPad,+			   SplashColorMode modeA, GBool alphaA,+			   GBool topDown) {+  width = widthA;+  height = heightA;+  mode = modeA;+  switch (mode) {+  case splashModeMono1:+    rowSize = (width + 7) >> 3;+    break;+  case splashModeMono8:+    rowSize = width;+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    rowSize = width * 3;+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    rowSize = width * 4;+    break;+#endif+  }+  rowSize += rowPad - 1;+  rowSize -= rowSize % rowPad;+  data = (SplashColorPtr)gmalloc(rowSize * height);+  if (!topDown) {+    data += (height - 1) * rowSize;+    rowSize = -rowSize;+  }+  if (alphaA) {+    alpha = (Guchar *)gmalloc(width * height);+  } else {+    alpha = NULL;+  }+}+++SplashBitmap::~SplashBitmap() {+  if (rowSize < 0) {+    gfree(data + (height - 1) * rowSize);+  } else {+    gfree(data);+  }+  gfree(alpha);+}++SplashError SplashBitmap::writePNMFile(char *fileName) {+  FILE *f;+  SplashColorPtr row, p;+  int x, y;++  if (!(f = fopen(fileName, "wb"))) {+    return splashErrOpenFile;+  }++  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) {+      p = row;+      for (x = 0; x < width; ++x) {+	fputc(*p, f);+	++p;+      }+      row += rowSize;+    }+    break;++  case splashModeRGB8:+    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(splashRGB8R(p), f);+	fputc(splashRGB8G(p), f);+	fputc(splashRGB8B(p), f);+	p += 3;+      }+      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:+    // PNM doesn't support CMYK+    break;+#endif+  }++  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 * width + x];+}
+ xpdf/SplashBitmap.h view
@@ -0,0 +1,61 @@+//========================================================================+//+// SplashBitmap.h+//+//========================================================================++#ifndef SPLASHBITMAP_H+#define SPLASHBITMAP_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashTypes.h"++//------------------------------------------------------------------------+// 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 = gTrue);++  ~SplashBitmap();++  int getWidth() { return width; }+  int getHeight() { return height; }+  int getRowSize() { return rowSize; }+  int getAlphaRowSize() { return width; }+  SplashColorMode getMode() { return mode; }+  SplashColorPtr getDataPtr() { return data; }+  Guchar *getAlphaPtr() { return alpha; }++  SplashError writePNMFile(char *fileName);++  void getPixel(int x, int y, SplashColorPtr pixel);+  Guchar getAlpha(int x, int y);++private:++  int width, height;		// size of bitmap+  int rowSize;			// size of one row of data, in bytes+				//   - negative for bottom-up bitmaps+  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)++  friend class Splash;+};++#endif
+ xpdf/SplashClip.cc view
@@ -0,0 +1,382 @@+//========================================================================+//+// SplashClip.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <string.h>+#include "gmem.h"+#include "SplashErrorCodes.h"+#include "SplashPath.h"+#include "SplashXPath.h"+#include "SplashXPathScanner.h"+#include "SplashBitmap.h"+#include "SplashClip.h"++//------------------------------------------------------------------------+// SplashClip.flags+//------------------------------------------------------------------------++#define splashClipEO       0x01	// use even-odd rule++//------------------------------------------------------------------------+// SplashClip+//------------------------------------------------------------------------++SplashClip::SplashClip(SplashCoord x0, SplashCoord y0,+		       SplashCoord x1, SplashCoord y1,+		       GBool antialiasA) {+  antialias = antialiasA;+  if (x0 < x1) {+    xMin = x0;+    xMax = x1;+  } else {+    xMin = x1;+    xMax = x0;+  }+  if (y0 < y1) {+    yMin = y0;+    yMax = y1;+  } else {+    yMin = y1;+    yMax = y0;+  }+  xMinI = splashFloor(xMin);+  yMinI = splashFloor(yMin);+  xMaxI = splashFloor(xMax);+  yMaxI = splashFloor(yMax);+  paths = NULL;+  flags = NULL;+  scanners = NULL;+  length = size = 0;+}++SplashClip::SplashClip(SplashClip *clip) {+  int i;++  antialias = clip->antialias;+  xMin = clip->xMin;+  yMin = clip->yMin;+  xMax = clip->xMax;+  yMax = clip->yMax;+  xMinI = clip->xMinI;+  yMinI = clip->yMinI;+  xMaxI = clip->xMaxI;+  yMaxI = clip->yMaxI;+  length = clip->length;+  size = clip->size;+  paths = (SplashXPath **)gmallocn(size, sizeof(SplashXPath *));+  flags = (Guchar *)gmallocn(size, sizeof(Guchar));+  scanners = (SplashXPathScanner **)+                 gmallocn(size, sizeof(SplashXPathScanner *));+  for (i = 0; i < length; ++i) {+    paths[i] = clip->paths[i]->copy();+    flags[i] = clip->flags[i];+    scanners[i] = new SplashXPathScanner(paths[i], flags[i] & splashClipEO);+  }+}++SplashClip::~SplashClip() {+  int i;++  for (i = 0; i < length; ++i) {+    delete paths[i];+    delete scanners[i];+  }+  gfree(paths);+  gfree(flags);+  gfree(scanners);+}++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 *));+    flags = (Guchar *)greallocn(flags, size, sizeof(Guchar));+    scanners = (SplashXPathScanner **)+                   greallocn(scanners, size, sizeof(SplashXPathScanner *));+  }+}++void SplashClip::resetToRect(SplashCoord x0, SplashCoord y0,+			     SplashCoord x1, SplashCoord y1) {+  int i;++  for (i = 0; i < length; ++i) {+    delete paths[i];+    delete scanners[i];+  }+  gfree(paths);+  gfree(flags);+  gfree(scanners);+  paths = NULL;+  flags = NULL;+  scanners = NULL;+  length = size = 0;++  if (x0 < x1) {+    xMin = x0;+    xMax = x1;+  } else {+    xMin = x1;+    xMax = x0;+  }+  if (y0 < y1) {+    yMin = y0;+    yMax = y1;+  } else {+    yMin = y1;+    yMax = y0;+  }+  xMinI = splashFloor(xMin);+  yMinI = splashFloor(yMin);+  xMaxI = splashFloor(xMax);+  yMaxI = splashFloor(yMax);+}++SplashError SplashClip::clipToRect(SplashCoord x0, SplashCoord y0,+				   SplashCoord x1, SplashCoord y1) {+  if (x0 < x1) {+    if (x0 > xMin) {+      xMin = x0;+      xMinI = splashFloor(xMin);+    }+    if (x1 < xMax) {+      xMax = x1;+      xMaxI = splashFloor(xMax);+    }+  } else {+    if (x1 > xMin) {+      xMin = x1;+      xMinI = splashFloor(xMin);+    }+    if (x0 < xMax) {+      xMax = x0;+      xMaxI = splashFloor(xMax);+    }+  }+  if (y0 < y1) {+    if (y0 > yMin) {+      yMin = y0;+      yMinI = splashFloor(yMin);+    }+    if (y1 < yMax) {+      yMax = y1;+      yMaxI = splashFloor(yMax);+    }+  } else {+    if (y1 > yMin) {+      yMin = y1;+      yMinI = splashFloor(yMin);+    }+    if (y0 < yMax) {+      yMax = y0;+      yMaxI = splashFloor(yMax);+    }+  }+  return splashOk;+}++SplashError SplashClip::clipToPath(SplashPath *path, SplashCoord *matrix,+				   SplashCoord flatness, GBool eo) {+  SplashXPath *xPath;++  xPath = new SplashXPath(path, matrix, flatness, gTrue);++  // check for an empty path+  if (xPath->length == 0) {+    xMax = xMin - 1;+    yMax = yMin - 1;+    xMaxI = splashFloor(xMax);+    yMaxI = splashFloor(yMax);+    delete xPath;++  // check for a rectangle+  } else if (xPath->length == 4 &&+	     ((xPath->segs[0].x0 == xPath->segs[0].x1 &&+	       xPath->segs[0].x0 == xPath->segs[1].x0 &&+	       xPath->segs[0].x0 == xPath->segs[3].x1 &&+	       xPath->segs[2].x0 == xPath->segs[2].x1 &&+	       xPath->segs[2].x0 == xPath->segs[1].x1 &&+	       xPath->segs[2].x0 == xPath->segs[3].x0 &&+	       xPath->segs[1].y0 == xPath->segs[1].y1 &&+	       xPath->segs[1].y0 == xPath->segs[0].y1 &&+	       xPath->segs[1].y0 == xPath->segs[2].y0 &&+	       xPath->segs[3].y0 == xPath->segs[3].y1 &&+	       xPath->segs[3].y0 == xPath->segs[0].y0 &&+	       xPath->segs[3].y0 == xPath->segs[2].y1) ||+	      (xPath->segs[0].y0 == xPath->segs[0].y1 &&+	       xPath->segs[0].y0 == xPath->segs[1].y0 &&+	       xPath->segs[0].y0 == xPath->segs[3].y1 &&+	       xPath->segs[2].y0 == xPath->segs[2].y1 &&+	       xPath->segs[2].y0 == xPath->segs[1].y1 &&+	       xPath->segs[2].y0 == xPath->segs[3].y0 &&+	       xPath->segs[1].x0 == xPath->segs[1].x1 &&+	       xPath->segs[1].x0 == xPath->segs[0].x1 &&+	       xPath->segs[1].x0 == xPath->segs[2].x0 &&+	       xPath->segs[3].x0 == xPath->segs[3].x1 &&+	       xPath->segs[3].x0 == xPath->segs[0].x0 &&+	       xPath->segs[3].x0 == xPath->segs[2].x1))) {+    clipToRect(xPath->segs[0].x0, xPath->segs[0].y0,+	       xPath->segs[2].x0, xPath->segs[2].y0);+    delete xPath;++  } else {+    grow(1);+    if (antialias) {+      xPath->aaScale();+    }+    xPath->sort();+    paths[length] = xPath;+    flags[length] = eo ? splashClipEO : 0;+    scanners[length] = new SplashXPathScanner(xPath, eo);+    ++length;+  }++  return splashOk;+}++GBool SplashClip::test(int x, int y) {+  int i;++  // check the rectangle+  if (x < xMinI || x > xMaxI || y < yMinI || y > yMaxI) {+    return gFalse;+  }++  // check the paths+  if (antialias) {+    for (i = 0; i < length; ++i) {+      if (!scanners[i]->test(x * splashAASize, y * splashAASize)) {+	return gFalse;+      }+    }+  } else {+    for (i = 0; i < length; ++i) {+      if (!scanners[i]->test(x, y)) {+	return gFalse;+      }+    }+  }++  return gTrue;+}++SplashClipResult SplashClip::testRect(int rectXMin, int rectYMin,+				      int rectXMax, int rectYMax) {+  // This tests the rectangle:+  //     x = [rectXMin, rectXMax + 1)    (note: rect coords are ints)+  //     y = [rectYMin, rectYMax + 1)+  // against the clipping region:+  //     x = [xMin, xMax]                (note: clipping coords are fp)+  //     y = [yMin, yMax]+  if ((SplashCoord)(rectXMax + 1) <= xMin || (SplashCoord)rectXMin > xMax ||+      (SplashCoord)(rectYMax + 1) <= yMin || (SplashCoord)rectYMin > yMax) {+    return splashClipAllOutside;+  }+  if ((SplashCoord)rectXMin >= xMin && (SplashCoord)(rectXMax + 1) <= xMax &&+      (SplashCoord)rectYMin >= yMin && (SplashCoord)(rectYMax + 1) <= yMax &&+      length == 0) {+    return splashClipAllInside;+  }+  return splashClipPartial;+}++SplashClipResult SplashClip::testSpan(int spanXMin, int spanXMax, int spanY) {+  int i;++  // This tests the rectangle:+  //     x = [spanXMin, spanXMax + 1)    (note: span coords are ints)+  //     y = [spanY, spanY + 1)+  // against the clipping region:+  //     x = [xMin, xMax]                (note: clipping coords are fp)+  //     y = [yMin, yMax]+  if ((SplashCoord)(spanXMax + 1) <= xMin || (SplashCoord)spanXMin > xMax ||+      (SplashCoord)(spanY + 1) <= yMin || (SplashCoord)spanY > yMax) {+    return splashClipAllOutside;+  }+  if (!((SplashCoord)spanXMin >= xMin && (SplashCoord)(spanXMax + 1) <= xMax &&+	(SplashCoord)spanY >= yMin && (SplashCoord)(spanY + 1) <= yMax)) {+    return splashClipPartial;+  }+  if (antialias) {+    for (i = 0; i < length; ++i) {+      if (!scanners[i]->testSpan(spanXMin * splashAASize,+				 spanXMax * splashAASize + (splashAASize - 1),+				 spanY * splashAASize)) {+	return splashClipPartial;+      }+    }+  } else {+    for (i = 0; i < length; ++i) {+      if (!scanners[i]->testSpan(spanXMin, spanXMax, spanY)) {+	return splashClipPartial;+      }+    }+  }+  return splashClipAllInside;+}++void SplashClip::clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y) {+  int xx0, xx1, xx, yy, i;+  SplashColorPtr p;++  // zero out pixels with x < xMin+  xx0 = *x0 * splashAASize;+  xx1 = splashFloor(xMin * splashAASize);+  if (xx1 > aaBuf->getWidth()) {+    xx1 = aaBuf->getWidth();+  }+  if (xx0 < xx1) {+    xx0 &= ~7;+    for (yy = 0; yy < splashAASize; ++yy) {+      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3);+      for (xx = xx0; xx + 7 < xx1; xx += 8) {+	*p++ = 0;+      }+      if (xx < xx1) {+	*p &= 0xff >> (xx1 & 7);+      }+    }+    *x0 = splashFloor(xMin);+  }++  // zero out pixels with x > xMax+  xx0 = splashFloor(xMax * splashAASize) + 1;+  if (xx0 < 0) {+    xx0 = 0;+  }+  xx1 = (*x1 + 1) * splashAASize;+  if (xx0 < xx1) {+    for (yy = 0; yy < splashAASize; ++yy) {+      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx0 >> 3);+      xx = xx0;+      if (xx & 7) {+	*p &= 0xff00 >> (xx & 7);+	xx = (xx & ~7) + 8;+	++p;+      }+      for (; xx < xx1; xx += 8) {+	*p++ = 0;+      }+    }+    *x1 = splashFloor(xMax);+  }++  // check the paths+  for (i = 0; i < length; ++i) {+    scanners[i]->clipAALine(aaBuf, x0, x1, y);+  }+}
+ xpdf/SplashClip.h view
@@ -0,0 +1,107 @@+//========================================================================+//+// SplashClip.h+//+//========================================================================++#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(SplashCoord x0, SplashCoord y0,+	     SplashCoord x1, SplashCoord y1,+	     GBool antialiasA);++  // 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 eo);++  // Returns true if (<x>,<y>) is inside the clip.+  GBool test(int x, int y);++  // 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);++  // Similar to testRect, but tests a horizontal span.+  SplashClipResult testSpan(int spanXMin, int spanXMax, int spanY);++  // Clips an anti-aliased line by setting pixels to zero.  On entry,+  // all non-zero pixels are between <x0> and <x1>.  This function+  // will update <x0> and <x1>.+  void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y);++  // Get the rectangle part of the clip region, in integer coordinates.+  int getXMinI() { return xMinI; }+  int getXMaxI() { return xMaxI; }+  int getYMinI() { return yMinI; }+  int getYMaxI() { return yMaxI; }++  // Get the number of arbitrary paths used by the clip region.+  int getNumPaths() { return length; }++private:++  SplashClip(SplashClip *clip);+  void grow(int nPaths);++  GBool antialias;+  SplashCoord xMin, yMin, xMax, yMax;+  int xMinI, yMinI, xMaxI, yMaxI;+  SplashXPath **paths;+  Guchar *flags;+  SplashXPathScanner **scanners;+  int length, size;+};++#endif
+ xpdf/SplashErrorCodes.h view
@@ -0,0 +1,32 @@+//========================================================================+//+// SplashErrorCodes.h+//+//========================================================================++#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
+ xpdf/SplashFTFont.cc view
@@ -0,0 +1,357 @@+//========================================================================+//+// SplashFTFont.cc+//+//========================================================================++#include <aconf.h>++#if HAVE_FREETYPE_FREETYPE_H || 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 "SplashMath.h"+#include "SplashGlyphBitmap.h"+#include "SplashPath.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;+  SplashCoord size, div;+  int x, y;++  face = fontFileA->face;+  if (FT_New_Size(face, &sizeObj)) {+    return;+  }+  face->size = sizeObj;+  size = splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);+  if (FT_Set_Pixel_Sizes(face, 0, (int)size)) {+    return;+  }+  // if the textMat values are too small, FreeType's fixed point+  // arithmetic doesn't work so well+  textScale = splashSqrt(textMat[2]*textMat[2] + textMat[3]*textMat[3]) / size;++  div = face->bbox.xMax > 20000 ? 65536 : 1;++  // 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] * face->bbox.xMin + mat[2] * face->bbox.yMin) /+	    (div * face->units_per_EM));+  xMin = xMax = x;+  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /+	    (div * face->units_per_EM));+  yMin = yMax = y;+  x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /+	    (div * face->units_per_EM));+  if (x < xMin) {+    xMin = x;+  } else if (x > xMax) {+    xMax = x;+  }+  y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /+	    (div * face->units_per_EM));+  if (y < yMin) {+    yMin = y;+  } else if (y > yMax) {+    yMax = y;+  }+  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /+	    (div * face->units_per_EM));+  if (x < xMin) {+    xMin = x;+  } else if (x > xMax) {+    xMax = x;+  }+  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /+	    (div * face->units_per_EM));+  if (y < yMin) {+    yMin = y;+  } else if (y > yMax) {+    yMax = y;+  }+  x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /+	    (div * face->units_per_EM));+  if (x < xMin) {+    xMin = x;+  } else if (x > xMax) {+    xMax = x;+  }+  y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /+	    (div * face->units_per_EM));+  if (y < yMin) {+    yMin = y;+  } else if (y > yMax) {+    yMax = y;+  }+  // This is a kludge: some buggy PDF generators embed fonts with+  // zero bounding boxes.+  if (xMax == xMin) {+    xMin = 0;+    xMax = (int)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).getRaw());+  matrix.yx = (FT_Fixed)((mat[1] / size).getRaw());+  matrix.xy = (FT_Fixed)((mat[2] / size).getRaw());+  matrix.yy = (FT_Fixed)((mat[3] / size).getRaw());+  textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)).getRaw());+  textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)).getRaw());+  textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)).getRaw());+  textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)).getRaw());+#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] / (size * textScale)) * 65536);+  textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)) * 65536);+  textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)) * 65536);+  textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)) * 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;+  FT_UInt gid;+  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 = (FT_UInt)ff->codeToGID[c];+  } else {+    gid = (FT_UInt)c;+  }+  if (ff->trueType && gid == 0) {+    // skip the TrueType notdef glyph+    return gFalse;+  }++  // if we have the FT2 bytecode interpreter, autohinting won't be used+#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER+  if (FT_Load_Glyph(ff->face, gid,+		    aa ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT)) {+    return gFalse;+  }+#else+  // FT2's autohinting doesn't always work very well (especially with+  // font subsets), so turn it off if anti-aliasing is enabled; if+  // anti-aliasing is disabled, this seems to be a tossup - some fonts+  // look better with hinting, some without, so leave hinting on+  if (FT_Load_Glyph(ff->face, gid,+		    aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP+                       : FT_LOAD_DEFAULT)) {+    return gFalse;+  }+#endif+  if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal+		               : ft_render_mode_mono)) {+    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 *)gmalloc(rowSize * bitmap->h);+  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;+  FT_UInt 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 = (FT_UInt)c;+  }+  if (ff->trueType && gid == 0) {+    // skip the TrueType notdef glyph+    return NULL;+  }+  if (FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP)) {+    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_FREETYPE_H || HAVE_FREETYPE_H
+ xpdf/SplashFTFont.h view
@@ -0,0 +1,58 @@+//========================================================================+//+// SplashFTFont.h+//+//========================================================================++#ifndef SPLASHFTFONT_H+#define SPLASHFTFONT_H++#include <aconf.h>++#if HAVE_FREETYPE_FREETYPE_H || 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_FREETYPE_H || HAVE_FREETYPE_H++#endif
+ xpdf/SplashFTFontEngine.cc view
@@ -0,0 +1,179 @@+//========================================================================+//+// SplashFTFontEngine.cc+//+//========================================================================++#include <aconf.h>++#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdio.h>+#ifndef WIN32+#  include <unistd.h>+#endif+#include "gmem.h"+#include "GString.h"+#include "gfile.h"+#include "FoFiTrueType.h"+#include "FoFiType1C.h"+#include "SplashFTFontFile.h"+#include "SplashFTFontEngine.h"++#ifdef VMS+#if (__VMS_VER < 70000000)+extern "C" int unlink(char *filename);+#endif+#endif++//------------------------------------------------------------------------++static void fileWrite(void *stream, char *data, int len) {+  fwrite(data, 1, len, (FILE *)stream);+}++//------------------------------------------------------------------------+// SplashFTFontEngine+//------------------------------------------------------------------------++SplashFTFontEngine::SplashFTFontEngine(GBool aaA, FT_Library libA) {+  FT_Int major, minor, patch;++  aa = aaA;+  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) {+  FT_Library libA;++  if (FT_Init_FreeType(&libA)) {+    return NULL;+  }+  return new SplashFTFontEngine(aaA, libA);+}++SplashFTFontEngine::~SplashFTFontEngine() {+  FT_Done_FreeType(lib);+}++SplashFontFile *SplashFTFontEngine::loadType1Font(SplashFontFileID *idA,+						  char *fileName,+						  GBool deleteFile,+						  char **enc) {+  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);+}++SplashFontFile *SplashFTFontEngine::loadType1CFont(SplashFontFileID *idA,+						   char *fileName,+						   GBool deleteFile,+						   char **enc) {+  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);+}++SplashFontFile *SplashFTFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,+							char *fileName,+							GBool deleteFile,+							char **enc) {+  return SplashFTFontFile::loadType1Font(this, idA, fileName, deleteFile, enc);+}++SplashFontFile *SplashFTFontEngine::loadCIDFont(SplashFontFileID *idA,+						char *fileName,+						GBool deleteFile) {+  FoFiType1C *ff;+  Gushort *cidToGIDMap;+  int nCIDs;+  SplashFontFile *ret;++  // check for a CFF font+  if (useCIDs) {+    cidToGIDMap = NULL;+    nCIDs = 0;+  } else if ((ff = FoFiType1C::load(fileName))) {+    cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);+    delete ff;+  } else {+    cidToGIDMap = NULL;+    nCIDs = 0;+  }+  ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile,+				      cidToGIDMap, nCIDs);+  if (!ret) {+    gfree(cidToGIDMap);+  }+  return ret;+}++SplashFontFile *SplashFTFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,+							char *fileName,+							GBool deleteFile) {+  FoFiTrueType *ff;+  GBool isCID;+  Gushort *cidToGIDMap;+  int nCIDs;+  SplashFontFile *ret;++  cidToGIDMap = NULL;+  nCIDs = 0;+  isCID = gFalse;+  if (!useCIDs) {+    if ((ff = FoFiTrueType::load(fileName))) {+      if (ff->isOpenTypeCFF()) {+	cidToGIDMap = ff->getCIDToGIDMap(&nCIDs);+      }+      delete ff;+    }+  }+  ret = SplashFTFontFile::loadCIDFont(this, idA, fileName, deleteFile,+				      cidToGIDMap, nCIDs);+  if (!ret) {+    gfree(cidToGIDMap);+  }+  return ret;+}++SplashFontFile *SplashFTFontEngine::loadTrueTypeFont(SplashFontFileID *idA,+						     char *fileName,+						     GBool deleteFile,+						     Gushort *codeToGID,+						     int codeToGIDLen) {+  FoFiTrueType *ff;+  GString *tmpFileName;+  FILE *tmpFile;+  SplashFontFile *ret;++  if (!(ff = FoFiTrueType::load(fileName))) {+    return NULL;+  }+  tmpFileName = NULL;+  if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {+    delete ff;+    return NULL;+  }+  ff->writeTTF(&fileWrite, tmpFile);+  delete ff;+  fclose(tmpFile);+  ret = SplashFTFontFile::loadTrueTypeFont(this, idA,+					   tmpFileName->getCString(),+					   gTrue, codeToGID, codeToGIDLen);+  if (ret) {+    if (deleteFile) {+      unlink(fileName);+    }+  } else {+    unlink(tmpFileName->getCString());+  }+  delete tmpFileName;+  return ret;+}++#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
+ xpdf/SplashFTFontEngine.h view
@@ -0,0 +1,65 @@+//========================================================================+//+// SplashFTFontEngine.h+//+//========================================================================++#ifndef SPLASHFTFONTENGINE_H+#define SPLASHFTFONTENGINE_H++#include <aconf.h>++#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include <ft2build.h>+#include FT_FREETYPE_H+#include "gtypes.h"++class SplashFontFile;+class SplashFontFileID;++//------------------------------------------------------------------------+// SplashFTFontEngine+//------------------------------------------------------------------------++class SplashFTFontEngine {+public:++  static SplashFTFontEngine *init(GBool aaA);++  ~SplashFTFontEngine();++  // Load fonts.+  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,+				GBool deleteFile, char **enc);+  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,+				 GBool deleteFile, char **enc);+  SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName,+				      GBool deleteFile, char **enc);+  SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,+			      GBool deleteFile);+  SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName,+				      GBool deleteFile);+  SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,+				   GBool deleteFile,+				   Gushort *codeToGID, int codeToGIDLen);++private:++  SplashFTFontEngine(GBool aaA, FT_Library libA);++  GBool aa;+  FT_Library lib;+  GBool useCIDs;++  friend class SplashFTFontFile;+  friend class SplashFTFont;+};++#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H++#endif
+ xpdf/SplashFTFontFile.cc view
@@ -0,0 +1,114 @@+//========================================================================+//+// SplashFTFontFile.cc+//+//========================================================================++#include <aconf.h>++#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include "gmem.h"+#include "SplashFTFontEngine.h"+#include "SplashFTFont.h"+#include "SplashFTFontFile.h"++//------------------------------------------------------------------------+// SplashFTFontFile+//------------------------------------------------------------------------++SplashFontFile *SplashFTFontFile::loadType1Font(SplashFTFontEngine *engineA,+						SplashFontFileID *idA,+						char *fileNameA,+						GBool deleteFileA,+						char **encA) {+  FT_Face faceA;+  Gushort *codeToGIDA;+  char *name;+  int i;++  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {+    return NULL;+  }+  codeToGIDA = (Gushort *)gmallocn(256, sizeof(int));+  for (i = 0; i < 256; ++i) {+    codeToGIDA[i] = 0;+    if ((name = encA[i])) {+      codeToGIDA[i] = (Gushort)FT_Get_Name_Index(faceA, name);+    }+  }++  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,+			      faceA, codeToGIDA, 256, gFalse);+}++SplashFontFile *SplashFTFontFile::loadCIDFont(SplashFTFontEngine *engineA,+					      SplashFontFileID *idA,+					      char *fileNameA,+					      GBool deleteFileA,+					      Gushort *codeToGIDA,+					      int codeToGIDLenA) {+  FT_Face faceA;++  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {+    return NULL;+  }++  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,+			      faceA, codeToGIDA, codeToGIDLenA, gFalse);+}++SplashFontFile *SplashFTFontFile::loadTrueTypeFont(SplashFTFontEngine *engineA,+						   SplashFontFileID *idA,+						   char *fileNameA,+						   GBool deleteFileA,+						   Gushort *codeToGIDA,+						   int codeToGIDLenA) {+  FT_Face faceA;++  if (FT_New_Face(engineA->lib, fileNameA, 0, &faceA)) {+    return NULL;+  }++  return new SplashFTFontFile(engineA, idA, fileNameA, deleteFileA,+			      faceA, codeToGIDA, codeToGIDLenA, gTrue);+}++SplashFTFontFile::SplashFTFontFile(SplashFTFontEngine *engineA,+				   SplashFontFileID *idA,+				   char *fileNameA, GBool deleteFileA,+				   FT_Face faceA,+				   Gushort *codeToGIDA, int codeToGIDLenA,+				   GBool trueTypeA):+  SplashFontFile(idA, fileNameA, deleteFileA)+{+  engine = engineA;+  face = faceA;+  codeToGID = codeToGIDA;+  codeToGIDLen = codeToGIDLenA;+  trueType = trueTypeA;+}++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_FREETYPE_H || HAVE_FREETYPE_H
+ xpdf/SplashFTFontFile.h view
@@ -0,0 +1,73 @@+//========================================================================+//+// SplashFTFontFile.h+//+//========================================================================++#ifndef SPLASHFTFONTFILE_H+#define SPLASHFTFONTFILE_H++#include <aconf.h>++#if HAVE_FREETYPE_FREETYPE_H || 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, char *fileNameA,+				       GBool deleteFileA, char **encA);+  static SplashFontFile *loadCIDFont(SplashFTFontEngine *engineA,+				     SplashFontFileID *idA, char *fileNameA,+				     GBool deleteFileA,+				     Gushort *codeToCIDA, int codeToGIDLenA);+  static SplashFontFile *loadTrueTypeFont(SplashFTFontEngine *engineA,+					  SplashFontFileID *idA,+					  char *fileNameA,+					  GBool deleteFileA,+					  Gushort *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,+		   char *fileNameA, GBool deleteFileA,+		   FT_Face faceA,+		   Gushort *codeToGIDA, int codeToGIDLenA,+		   GBool trueTypeA);++  SplashFTFontEngine *engine;+  FT_Face face;+  Gushort *codeToGID;+  int codeToGIDLen;+  GBool trueType;++  friend class SplashFTFont;+};++#endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H++#endif
+ xpdf/SplashFont.cc view
@@ -0,0 +1,176 @@+//========================================================================+//+// SplashFont.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <string.h>+#include "gmem.h"+#include "SplashMath.h"+#include "SplashGlyphBitmap.h"+#include "SplashFontFile.h"+#include "SplashFont.h"++//------------------------------------------------------------------------++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 (aa) {+    glyphSize = glyphW * glyphH;+  } else {+    glyphSize = ((glyphW + 7) >> 3) * glyphH;+  }++  // set up the glyph pixmap cache+  cacheAssoc = 8;+  if (glyphSize <= 256) {+    cacheSets = 8;+  } else if (glyphSize <= 512) {+    cacheSets = 4;+  } else if (glyphSize <= 1024) {+    cacheSets = 2;+  } else {+    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+  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;+    }+  }++  // 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 (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;+}
+ xpdf/SplashFont.h view
@@ -0,0 +1,104 @@+//========================================================================+//+// SplashFont.h+//+//========================================================================++#ifndef SPLASHFONT_H+#define SPLASHFONT_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "gtypes.h"+#include "SplashTypes.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 &&+           matA[0] == mat[0] && matA[1] == mat[1] &&+           matA[2] == mat[2] && matA[3] == mat[3] &&+           textMatA[0] == textMat[0] && textMatA[1] == textMat[1] &&+           textMatA[2] == textMat[2] && textMatA[3] == textMat[3];+  }++  // 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
+ xpdf/SplashFontEngine.cc view
@@ -0,0 +1,317 @@+//========================================================================+//+// SplashFontEngine.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#if HAVE_T1LIB_H+#include <t1lib.h>+#endif++#include <stdlib.h>+#include <stdio.h>+#ifndef WIN32+#  include <unistd.h>+#endif+#include "gmem.h"+#include "GString.h"+#include "SplashMath.h"+#include "SplashT1FontEngine.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_T1LIB_H+				   GBool enableT1lib,+#endif+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+				   GBool enableFreeType,+#endif+				   GBool aa) {+  int i;++  for (i = 0; i < splashFontCacheSize; ++i) {+    fontCache[i] = NULL;+  }++#if HAVE_T1LIB_H+  if (enableT1lib) {+    t1Engine = SplashT1FontEngine::init(aa);+  } else {+    t1Engine = NULL;+  }+#endif+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  if (enableFreeType) {+    ftEngine = SplashFTFontEngine::init(aa);+  } else {+    ftEngine = NULL;+  }+#endif+}++SplashFontEngine::~SplashFontEngine() {+  int i;++  for (i = 0; i < splashFontCacheSize; ++i) {+    if (fontCache[i]) {+      delete fontCache[i];+    }+  }++#if HAVE_T1LIB_H+  if (t1Engine) {+    delete t1Engine;+  }+#endif+#if HAVE_FREETYPE_FREETYPE_H || 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;+}++SplashFontFile *SplashFontEngine::loadType1Font(SplashFontFileID *idA,+						char *fileName,+						GBool deleteFile, char **enc) {+  SplashFontFile *fontFile;++  fontFile = NULL;+#if HAVE_T1LIB_H+  if (!fontFile && t1Engine) {+    fontFile = t1Engine->loadType1Font(idA, fileName, deleteFile, enc);+  }+#endif+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  if (!fontFile && ftEngine) {+    fontFile = ftEngine->loadType1Font(idA, fileName, deleteFile, enc);+  }+#endif++#ifndef WIN32+  // 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++  return fontFile;+}++SplashFontFile *SplashFontEngine::loadType1CFont(SplashFontFileID *idA,+						 char *fileName,+						 GBool deleteFile,+						 char **enc) {+  SplashFontFile *fontFile;++  fontFile = NULL;+#if HAVE_T1LIB_H+  if (!fontFile && t1Engine) {+    fontFile = t1Engine->loadType1CFont(idA, fileName, deleteFile, enc);+  }+#endif+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  if (!fontFile && ftEngine) {+    fontFile = ftEngine->loadType1CFont(idA, fileName, deleteFile, enc);+  }+#endif++#ifndef WIN32+  // 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++  return fontFile;+}++SplashFontFile *SplashFontEngine::loadOpenTypeT1CFont(SplashFontFileID *idA,+						      char *fileName,+						      GBool deleteFile,+						      char **enc) {+  SplashFontFile *fontFile;++  fontFile = NULL;+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  if (!fontFile && ftEngine) {+    fontFile = ftEngine->loadOpenTypeT1CFont(idA, fileName, deleteFile, enc);+  }+#endif++#ifndef WIN32+  // 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++  return fontFile;+}++SplashFontFile *SplashFontEngine::loadCIDFont(SplashFontFileID *idA,+					      char *fileName,+					      GBool deleteFile) {+  SplashFontFile *fontFile;++  fontFile = NULL;+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  if (!fontFile && ftEngine) {+    fontFile = ftEngine->loadCIDFont(idA, fileName, deleteFile);+  }+#endif++#ifndef WIN32+  // 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++  return fontFile;+}++SplashFontFile *SplashFontEngine::loadOpenTypeCFFFont(SplashFontFileID *idA,+						      char *fileName,+						      GBool deleteFile) {+  SplashFontFile *fontFile;++  fontFile = NULL;+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  if (!fontFile && ftEngine) {+    fontFile = ftEngine->loadOpenTypeCFFFont(idA, fileName, deleteFile);+  }+#endif++#ifndef WIN32+  // 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++  return fontFile;+}++SplashFontFile *SplashFontEngine::loadTrueTypeFont(SplashFontFileID *idA,+						   char *fileName,+						   GBool deleteFile,+						   Gushort *codeToGID,+						   int codeToGIDLen) {+  SplashFontFile *fontFile;++  fontFile = NULL;+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  if (!fontFile && ftEngine) {+    fontFile = ftEngine->loadTrueTypeFont(idA, fileName, deleteFile,+					  codeToGID, codeToGIDLen);+  }+#endif++  if (!fontFile) {+    gfree(codeToGID);+  }++#ifndef WIN32+  // 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++  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 (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 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;+}
+ xpdf/SplashFontEngine.h view
@@ -0,0 +1,91 @@+//========================================================================+//+// SplashFontEngine.h+//+//========================================================================++#ifndef SPLASHFONTENGINE_H+#define SPLASHFONTENGINE_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "gtypes.h"++class SplashT1FontEngine;+class SplashFTFontEngine;+class SplashDTFontEngine;+class SplashDT4FontEngine;+class SplashFontFile;+class SplashFontFileID;+class SplashFont;++//------------------------------------------------------------------------++#define splashFontCacheSize 16++//------------------------------------------------------------------------+// SplashFontEngine+//------------------------------------------------------------------------++class SplashFontEngine {+public:++  // Create a font engine.+  SplashFontEngine(+#if HAVE_T1LIB_H+		   GBool enableT1lib,+#endif+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+		   GBool enableFreeType,+#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);++  // Load fonts - these create new SplashFontFile objects.+  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,+				GBool deleteFile, char **enc);+  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,+				 GBool deleteFile, char **enc);+  SplashFontFile *loadOpenTypeT1CFont(SplashFontFileID *idA, char *fileName,+				      GBool deleteFile, char **enc);+  SplashFontFile *loadCIDFont(SplashFontFileID *idA, char *fileName,+			      GBool deleteFile);+  SplashFontFile *loadOpenTypeCFFFont(SplashFontFileID *idA, char *fileName,+				      GBool deleteFile);+  SplashFontFile *loadTrueTypeFont(SplashFontFileID *idA, char *fileName,+				   GBool deleteFile,+				   Gushort *codeToGID, int codeToGIDLen);++  // 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];++#if HAVE_T1LIB_H+  SplashT1FontEngine *t1Engine;+#endif+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+  SplashFTFontEngine *ftEngine;+#endif+};++#endif
+ xpdf/SplashFontFile.cc view
@@ -0,0 +1,55 @@+//========================================================================+//+// SplashFontFile.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdio.h>+#ifndef WIN32+#  include <unistd.h>+#endif+#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, char *fileNameA,+			       GBool deleteFileA) {+  id = idA;+  fileName = new GString(fileNameA);+  deleteFile = deleteFileA;+  refCnt = 0;+}++SplashFontFile::~SplashFontFile() {+  if (deleteFile) {+    unlink(fileName->getCString());+  }+  delete fileName;+  delete id;+}++void SplashFontFile::incRefCnt() {+  ++refCnt;+}++void SplashFontFile::decRefCnt() {+  if (!--refCnt) {+    delete this;+  }+}
+ xpdf/SplashFontFile.h view
@@ -0,0 +1,60 @@+//========================================================================+//+// SplashFontFile.h+//+//========================================================================++#ifndef SPLASHFONTFILE_H+#define SPLASHFONTFILE_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "gtypes.h"+#include "SplashTypes.h"++class GString;+class SplashFontEngine;+class SplashFont;+class SplashFontFileID;++//------------------------------------------------------------------------+// 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, char *fileNameA,+		 GBool deleteFileA);++  SplashFontFileID *id;+  GString *fileName;+  GBool deleteFile;+  int refCnt;++  friend class SplashFontEngine;+};++#endif
+ xpdf/SplashFontFileID.cc view
@@ -0,0 +1,23 @@+//========================================================================+//+// SplashFontFileID.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include "SplashFontFileID.h"++//------------------------------------------------------------------------+// SplashFontFileID+//------------------------------------------------------------------------++SplashFontFileID::SplashFontFileID() {+}++SplashFontFileID::~SplashFontFileID() {+}
+ xpdf/SplashFontFileID.h view
@@ -0,0 +1,30 @@+//========================================================================+//+// SplashFontFileID.h+//+//========================================================================++#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
+ xpdf/SplashGlyphBitmap.h view
@@ -0,0 +1,26 @@+//========================================================================+//+// SplashGlyphBitmap.h+//+//========================================================================++#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
+ xpdf/SplashMath.h view
@@ -0,0 +1,89 @@+//========================================================================+//+// SplashMath.h+//+//========================================================================++#ifndef SPLASHMATH_H+#define SPLASHMATH_H++#include <aconf.h>+#if USE_FIXEDPONT+#include "FixedPoint.h"+#else+#include <math.h>+#endif+#include "SplashTypes.h"++static inline SplashCoord splashAbs(SplashCoord x) {+#if USE_FIXEDPOINT+  return FixedPoint::abs(x);+#else+  return fabs(x);+#endif+}++static inline int splashFloor(SplashCoord x) {+#if USE_FIXEDPOINT+  return FixedPoint::floor(x);+#else+  return (int)floor(x);+#endif+}++static inline int splashCeil(SplashCoord x) {+#if USE_FIXEDPOINT+  return FixedPoint::ceil(x);+#else+  return (int)ceil(x);+#endif+}++static inline int splashRound(SplashCoord x) {+#if USE_FIXEDPOINT+  return FixedPoint::round(x);+#else+  return (int)floor(x + 0.5);+#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;+  dxa = splashAbs(dx);+  dya = splashAbs(dy);+  if (dxa == 0 && dya == 0) {+    return 0;+  } else if (dxa > dya) {+    return dxa * FixedPoint::sqrt(dya / dxa + 1);+  } else {+    return dya * FixedPoint::sqrt(dxa / dya + 1);+  }+#else+  return sqrt(dx * dx + dy * dy);+#endif+}++#endif
+ xpdf/SplashOutputDev.cc view
@@ -0,0 +1,2845 @@+//========================================================================+//+// SplashOutputDev.cc+//+// Copyright 2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <string.h>+#include <math.h>+#include "gfile.h"+#include "GlobalParams.h"+#include "Error.h"+#include "Object.h"+#include "GfxFont.h"+#include "Link.h"+#include "CharCodeToUnicode.h"+#include "FontEncodingTables.h"+#include "FoFiTrueType.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++//------------------------------------------------------------------------++// 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);+}++//------------------------------------------------------------------------+// Blend functions+//------------------------------------------------------------------------++static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,+				   SplashColorPtr blend, SplashColorMode cm) {+  int i;++  for (i = 0; i < splashColorModeNComps[cm]; ++i) {+    blend[i] = (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] = dest[i] + src[i] - (dest[i] * src[i]) / 255;+  }+}++static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,+				  SplashColorPtr blend, SplashColorMode cm) {+  int i;++  for (i = 0; i < splashColorModeNComps[cm]; ++i) {+    blend[i] = dest[i] < 0x80+                 ? (src[i] * 2 * dest[i]) / 255+                 : 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, x;++  for (i = 0; i < splashColorModeNComps[cm]; ++i) {+    if (src[i] == 255) {+      blend[i] = 255;+    } else {+      x = (dest[i] * 255) / (255 - src[i]);+      blend[i] = x <= 255 ? x : 255;+    }+  }+}++static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,+				    SplashColorPtr blend, SplashColorMode cm) {+  int i, x;++  for (i = 0; i < splashColorModeNComps[cm]; ++i) {+    if (src[i] == 0) {+      blend[i] = 0;+    } else {+      x = ((255 - dest[i]) * 255) / src[i];+      blend[i] = x <= 255 ? 255 - x : 0;+    }+  }+}++static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,+				    SplashColorPtr blend, SplashColorMode cm) {+  int i;++  for (i = 0; i < splashColorModeNComps[cm]; ++i) {+    blend[i] = src[i] < 0x80+                 ? (dest[i] * 2 * src[i]) / 255+                 : 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) {+    if (src[i] < 0x80) {+      blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) /+	         (255 * 255);+    } else {+      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] = 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] ? src[i] - dest[i] : 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] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255;+  }+}++static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) {+  int cmax, cmid, cmin, x;++  if (r >= g) {+    if (g >= b)      { x = 0; cmax = r; cmid = g; cmin = b; }+    else if (b >= r) { x = 4; cmax = b; cmid = r; cmin = g; }+    else             { x = 5; cmax = r; cmid = b; cmin = g; }+  } else {+    if (r >= b)      { x = 1; cmax = g; cmid = r; cmin = b; }+    else if (g >= b) { x = 2; cmax = g; cmid = b; cmin = r; }+    else             { x = 3; cmax = b; cmid = g; cmin = r; }+  }+  if (cmax == cmin) {+    *h = *s = 0;+  } else {+    *h = x * 60;+    if (x & 1) {+      *h += ((cmax - cmid) * 60) / (cmax - cmin);+    } else {+      *h += ((cmid - cmin) * 60) / (cmax - cmin);+    }+    *s = (255 * (cmax - cmin)) / cmax;+  }+  *v = cmax;+}++static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) {+  int x, f, cmax, cmid, cmin;++  if (s == 0) {+    *r = *g = *b = v;+  } else {+    x = h / 60;+    f = h % 60;+    cmax = v;+    if (x & 1) {+      cmid = div255(v * 255 - ((s * f) / 60));+    } else {+      cmid = div255(v * (255 - ((s * (60 - f)) / 60)));+    }+    cmin = div255(v * (255 - s));+    switch (x) {+    case 0: *r = cmax; *g = cmid; *b = cmin; break;+    case 1: *g = cmax; *r = cmid; *b = cmin; break;+    case 2: *g = cmax; *b = cmid; *r = cmin; break;+    case 3: *b = cmax; *g = cmid; *r = cmin; break;+    case 4: *b = cmax; *r = cmid; *g = cmin; break;+    case 5: *r = cmax; *b = cmid; *g = cmin; break;+    }+  }+}++static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,+			      SplashColorPtr blend, SplashColorMode cm) {+  int hs, ss, vs, hd, sd, vd;+#if SPLASH_CMYK+  Guchar r, g, b;+#endif++  switch (cm) {+  case splashModeMono1:+  case splashModeMono8:+    blend[0] = dest[0];+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);+    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);+    cvtHSVToRGB(hs, sd, vd, &blend[0], &blend[1], &blend[2]);+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    //~ (0xff - ...) should be clipped+    cvtRGBToHSV(0xff - (src[0] + src[3]),+		0xff - (src[1] + src[3]),+		0xff - (src[2] + src[3]), &hs, &ss, &vs);+    cvtRGBToHSV(0xff - (dest[0] + dest[3]),+		0xff - (dest[1] + dest[3]),+		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);+    cvtHSVToRGB(hs, sd, vd, &r, &g, &b);+    //~ should do black generation+    blend[0] = 0xff - r;+    blend[1] = 0xff - g;+    blend[2] = 0xff - b;+    blend[3] = 0;+    break;+#endif+  }+}++static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,+				     SplashColorPtr blend,+				     SplashColorMode cm) {+  int hs, ss, vs, hd, sd, vd;+#if SPLASH_CMYK+  Guchar r, g, b;+#endif++  switch (cm) {+  case splashModeMono1:+  case splashModeMono8:+    blend[0] = dest[0];+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);+    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);+    cvtHSVToRGB(hd, ss, vd, &blend[0], &blend[1], &blend[2]);+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    //~ (0xff - ...) should be clipped+    cvtRGBToHSV(0xff - (src[0] + src[3]),+		0xff - (src[1] + src[3]),+		0xff - (src[2] + src[3]), &hs, &ss, &vs);+    cvtRGBToHSV(0xff - (dest[0] + dest[3]),+		0xff - (dest[1] + dest[3]),+		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);+    cvtHSVToRGB(hd, ss, vd, &r, &g, &b);+    //~ should do black generation+    blend[0] = 0xff - r;+    blend[1] = 0xff - g;+    blend[2] = 0xff - b;+    blend[3] = 0;+    break;+#endif+  }+}++static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,+				SplashColorPtr blend, SplashColorMode cm) {+  int hs, ss, vs, hd, sd, vd;+#if SPLASH_CMYK+  Guchar r, g, b;+#endif++  switch (cm) {+  case splashModeMono1:+  case splashModeMono8:+    blend[0] = dest[0];+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);+    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);+    cvtHSVToRGB(hs, ss, vd, &blend[0], &blend[1], &blend[2]);+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    //~ (0xff - ...) should be clipped+    cvtRGBToHSV(0xff - (src[0] + src[3]),+		0xff - (src[1] + src[3]),+		0xff - (src[2] + src[3]), &hs, &ss, &vs);+    cvtRGBToHSV(0xff - (dest[0] + dest[3]),+		0xff - (dest[1] + dest[3]),+		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);+    cvtHSVToRGB(hs, ss, vd, &r, &g, &b);+    //~ should do black generation+    blend[0] = 0xff - r;+    blend[1] = 0xff - g;+    blend[2] = 0xff - b;+    blend[3] = 0;+    break;+#endif+  }+}++static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,+				     SplashColorPtr blend,+				     SplashColorMode cm) {+  int hs, ss, vs, hd, sd, vd;+#if SPLASH_CMYK+  Guchar r, g, b;+#endif++  switch (cm) {+  case splashModeMono1:+  case splashModeMono8:+    blend[0] = dest[0];+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);+    cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);+    cvtHSVToRGB(hd, sd, vs, &blend[0], &blend[1], &blend[2]);+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    //~ (0xff - ...) should be clipped+    cvtRGBToHSV(0xff - (src[0] + src[3]),+		0xff - (src[1] + src[3]),+		0xff - (src[2] + src[3]), &hs, &ss, &vs);+    cvtRGBToHSV(0xff - (dest[0] + dest[3]),+		0xff - (dest[1] + dest[3]),+		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);+    cvtHSVToRGB(hd, sd, vs, &r, &g, &b);+    //~ should do black generation+    blend[0] = 0xff - r;+    blend[1] = 0xff - g;+    blend[2] = 0xff - b;+    blend[3] = 0;+    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+};++//------------------------------------------------------------------------+// Font substitutions+//------------------------------------------------------------------------++struct SplashOutFontSubst {+  char *name;+  double mWidth;+};++// index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic+static SplashOutFontSubst splashOutSubstFonts[16] = {+  {"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},+  {"Courier",               0.600},+  {"Courier-Oblique",       0.600},+  {"Courier-Bold",          0.600},+  {"Courier-BoldOblique",   0.600},+  {"Symbol",                0.576},+  {"Symbol",                0.576},+  {"Symbol",                0.576},+  {"Symbol",                0.576}+};++//------------------------------------------------------------------------+// SplashOutFontFileID+//------------------------------------------------------------------------++class SplashOutFontFileID: public SplashFontFileID {+public:++  SplashOutFontFileID(Ref *rA) { r = *rA; substIdx = -1; }++  ~SplashOutFontFileID() {}++  GBool matches(SplashFontFileID *id) {+    return ((SplashOutFontFileID *)id)->r.num == r.num &&+           ((SplashOutFontFileID *)id)->r.gen == r.gen;+  }++  void setSubstIdx(int substIdxA) { substIdx = substIdxA; }+  int getSubstIdx() { return substIdx; }++private:++  Ref r;+  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 aa, GBool validBBoxA);+  ~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+};++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;+  if (aa) {+    glyphSize = glyphW * glyphH;+  } else {+    glyphSize = ((glyphW + 7) >> 3) * glyphH;+  }+  cacheAssoc = 8;+  if (glyphSize <= 256) {+    cacheSets = 8;+  } else if (glyphSize <= 512) {+    cacheSets = 4;+  } else if (glyphSize <= 1024) {+    cacheSets = 2;+  } else {+    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 = i & (cacheAssoc - 1);+  }+}++T3FontCache::~T3FontCache() {+  gfree(cacheData);+  gfree(cacheTags);+}++struct T3GlyphStack {+  Gushort code;			// character code++  //----- 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;++  T3GlyphStack *next;		// next object on stack+};++//------------------------------------------------------------------------+// SplashTransparencyGroup+//------------------------------------------------------------------------++struct SplashTransparencyGroup {+  int tx, ty;			// translation coordinates+  SplashBitmap *tBitmap;	// bitmap for transparency group+  GfxColorSpace *blendingColorSpace;+  GBool isolated;++  //----- saved state+  SplashBitmap *origBitmap;+  Splash *origSplash;++  SplashTransparencyGroup *next;+};++//------------------------------------------------------------------------+// SplashOutputDev+//------------------------------------------------------------------------++SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,+				 int bitmapRowPadA,+				 GBool reverseVideoA,+				 SplashColorPtr paperColorA,+				 GBool bitmapTopDownA,+				 GBool allowAntialiasA) {+  colorMode = colorModeA;+  bitmapRowPad = bitmapRowPadA;+  bitmapTopDown = bitmapTopDownA;+  allowAntialias = allowAntialiasA;+  vectorAntialias = allowAntialias &&+		      globalParams->getVectorAntialias() &&+		      colorMode != splashModeMono1;+  setupScreenParams(72.0, 72.0);+  reverseVideo = reverseVideoA;+  splashColorCopy(paperColor, paperColorA);++  xref = NULL;++  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,+			    colorMode != splashModeMono1, bitmapTopDown);+  splash = new Splash(bitmap, vectorAntialias, &screenParams);+  splash->clear(paperColor, 0);++  fontEngine = NULL;++  nT3Fonts = 0;+  t3GlyphStack = NULL;++  font = NULL;+  needFontUpdate = gFalse;+  textClipPath = NULL;++  transpGroupStack = 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 = 100;+    }+    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 = 100;+      }+      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;+  }+}++void SplashOutputDev::startDoc(XRef *xrefA) {+  int i;++  xref = xrefA;+  if (fontEngine) {+    delete fontEngine;+  }+  fontEngine = new SplashFontEngine(+#if HAVE_T1LIB_H+				    globalParams->getEnableT1lib(),+#endif+#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H+				    globalParams->getEnableFreeType(),+#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;+  }+  if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {+    if (bitmap) {+      delete bitmap;+    }+    bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,+			      colorMode != splashModeMono1, bitmapTopDown);+  }+  splash = new Splash(bitmap, vectorAntialias, &screenParams);+  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(globalParams->getStrokeAdjust());+  splash->clear(paperColor, 0);+}++void SplashOutputDev::endPage() {+  if (colorMode != splashModeMono1) {+    splash->compositeBackground(paperColor);+  }+}++void SplashOutputDev::saveState(GfxState *state) {+  splash->saveState();+}++void SplashOutputDev::restoreState(GfxState *state) {+  splash->restoreState();+  needFontUpdate = gTrue;+}++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) {+  splash->setFlatness(state->getFlatness());+}++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+  splash->setStrokeAdjust(state->getStrokeAdjust());+#endif+}++void SplashOutputDev::updateFillColor(GfxState *state) {+  GfxGray gray;+  GfxRGB rgb;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif++  state->getFillGray(&gray);+  state->getFillRGB(&rgb);+#if SPLASH_CMYK+  state->getFillCMYK(&cmyk);+  splash->setFillPattern(getColor(gray, &rgb, &cmyk));+#else+  splash->setFillPattern(getColor(gray, &rgb));+#endif+}++void SplashOutputDev::updateStrokeColor(GfxState *state) {+  GfxGray gray;+  GfxRGB rgb;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif++  state->getStrokeGray(&gray);+  state->getStrokeRGB(&rgb);+#if SPLASH_CMYK+  state->getStrokeCMYK(&cmyk);+  splash->setStrokePattern(getColor(gray, &rgb, &cmyk));+#else+  splash->setStrokePattern(getColor(gray, &rgb));+#endif+}++#if SPLASH_CMYK+SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb,+					 GfxCMYK *cmyk) {+#else+SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {+#endif+  SplashPattern *pattern;+  SplashColor color;+  GfxColorComp r, g, b;++  if (reverseVideo) {+    gray = gfxColorComp1 - gray;+    r = gfxColorComp1 - rgb->r;+    g = gfxColorComp1 - rgb->g;+    b = gfxColorComp1 - rgb->b;+  } else {+    r = rgb->r;+    g = rgb->g;+    b = rgb->b;+  }++  pattern = NULL; // make gcc happy+  switch (colorMode) {+  case splashModeMono1:+  case splashModeMono8:+    color[0] = colToByte(gray);+    pattern = new SplashSolidColor(color);+    break;+  case splashModeRGB8:+  case splashModeBGR8:+    color[0] = colToByte(r);+    color[1] = colToByte(g);+    color[2] = colToByte(b);+    pattern = new SplashSolidColor(color);+    break;+#if SPLASH_CMYK+  case splashModeCMYK8:+    color[0] = colToByte(cmyk->c);+    color[1] = colToByte(cmyk->m);+    color[2] = colToByte(cmyk->y);+    color[3] = colToByte(cmyk->k);+    pattern = new SplashSolidColor(color);+    break;+#endif+  }++  return pattern;+}++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::updateFont(GfxState *state) {+  needFontUpdate = gTrue;+}++void SplashOutputDev::doUpdateFont(GfxState *state) {+  GfxFont *gfxFont;+  GfxFontType fontType;+  SplashOutFontFileID *id;+  SplashFontFile *fontFile;+  FoFiTrueType *ff;+  Ref embRef;+  Object refObj, strObj;+  GString *tmpFileName, *fileName, *substName;+  FILE *tmpFile;+  Gushort *codeToGID;+  DisplayFontParam *dfp;+  CharCodeToUnicode *ctu;+  double *textMat;+  double m11, m12, m21, m22, w1, w2, fontSize;+  SplashCoord mat[4];+  char *name;+  Unicode uBuf[8];+  int c, substIdx, n, code, cmap;++  needFontUpdate = gFalse;+  font = NULL;+  tmpFileName = NULL;+  substIdx = -1;+  dfp = NULL;++  if (!(gfxFont = state->getFont())) {+    goto err1;+  }+  fontType = gfxFont->getType();+  if (fontType == fontType3) {+    goto err1;+  }++  // check the font file cache+  id = new SplashOutFontFileID(gfxFont->getID());+  if ((fontFile = fontEngine->getFontFile(id))) {+    delete id;++  } else {++    // if there is an embedded font, write it to disk+    if (gfxFont->getEmbeddedFontID(&embRef)) {+      if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {+	error(-1, "Couldn't create temporary font file");+	goto err2;+      }+      refObj.initRef(embRef.num, embRef.gen);+      refObj.fetch(xref, &strObj);+      refObj.free();+      if (!strObj.isStream()) {+	error(-1, "Embedded font object is wrong type");+	strObj.free();+	fclose(tmpFile);+	goto err2;+      }+      strObj.streamReset();+      while ((c = strObj.streamGetChar()) != EOF) {+	fputc(c, tmpFile);+      }+      strObj.streamClose();+      strObj.free();+      fclose(tmpFile);+      fileName = tmpFileName;++    // if there is an external font file, use it+    } else if (!(fileName = gfxFont->getExtFontFile())) {++      // look for a display font mapping or a substitute font+      if (gfxFont->isCIDFont()) {+	if (((GfxCIDFont *)gfxFont)->getCollection()) {+	  dfp = globalParams->+	          getDisplayCIDFont(gfxFont->getName(),+				    ((GfxCIDFont *)gfxFont)->getCollection());+	}+      } else {+	if (gfxFont->getName()) {+	  dfp = globalParams->getDisplayFont(gfxFont->getName());+	}+	if (!dfp) {+	  // 8-bit font substitution+	  if (gfxFont->isFixedWidth()) {+	    substIdx = 8;+	  } else if (gfxFont->isSerif()) {+	    substIdx = 4;+	  } else {+	    substIdx = 0;+	  }+	  if (gfxFont->isBold()) {+	    substIdx += 2;+	  }+	  if (gfxFont->isItalic()) {+	    substIdx += 1;+	  }+	  substName = new GString(splashOutSubstFonts[substIdx].name);+	  dfp = globalParams->getDisplayFont(substName);+	  delete substName;+	  id->setSubstIdx(substIdx);+	}+      }+      if (!dfp) {+	error(-1, "Couldn't find a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      switch (dfp->kind) {+      case displayFontT1:+	fileName = dfp->t1.fileName;+	fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;+	break;+      case displayFontTT:+	fileName = dfp->tt.fileName;+	fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;+	break;+      }+    }++    // load the font file+    switch (fontType) {+    case fontType1:+      if (!(fontFile = fontEngine->loadType1Font(+			   id,+			   fileName->getCString(),+			   fileName == tmpFileName,+			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {+	error(-1, "Couldn't create a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      break;+    case fontType1C:+      if (!(fontFile = fontEngine->loadType1CFont(+			   id,+			   fileName->getCString(),+			   fileName == tmpFileName,+			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {+	error(-1, "Couldn't create a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      break;+    case fontType1COT:+      if (!(fontFile = fontEngine->loadOpenTypeT1CFont(+			   id,+			   fileName->getCString(),+			   fileName == tmpFileName,+			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {+	error(-1, "Couldn't create a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      break;+    case fontTrueType:+    case fontTrueTypeOT:+      if ((ff = FoFiTrueType::load(fileName->getCString()))) {+	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);+	n = 256;+	delete ff;+      } else {+	codeToGID = NULL;+	n = 0;+      }+      if (!(fontFile = fontEngine->loadTrueTypeFont(+			   id,+			   fileName->getCString(),+			   fileName == tmpFileName,+			   codeToGID, n))) {+	error(-1, "Couldn't create a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      break;+    case fontCIDType0:+    case fontCIDType0C:+      if (!(fontFile = fontEngine->loadCIDFont(+			   id,+			   fileName->getCString(),+			   fileName == tmpFileName))) {+	error(-1, "Couldn't create a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      break;+    case fontCIDType0COT:+      if (!(fontFile = fontEngine->loadOpenTypeCFFFont(+			   id,+			   fileName->getCString(),+			   fileName == tmpFileName))) {+	error(-1, "Couldn't create a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      break;+    case fontCIDType2:+    case fontCIDType2OT:+      codeToGID = NULL;+      n = 0;+      if (dfp) {+	// create a CID-to-GID mapping, via Unicode+	if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {+	  if ((ff = FoFiTrueType::load(fileName->getCString()))) {+	    // look for a Unicode cmap+	    for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {+	      if ((ff->getCmapPlatform(cmap) == 3 &&+		   ff->getCmapEncoding(cmap) == 1) ||+		  ff->getCmapPlatform(cmap) == 0) {+		break;+	      }+	    }+	    if (cmap < ff->getNumCmaps()) {+	      // map CID -> Unicode -> GID+	      n = ctu->getLength();+	      codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));+	      for (code = 0; code < n; ++code) {+		if (ctu->mapToUnicode(code, uBuf, 8) > 0) {+		  codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);+		} else {+		  codeToGID[code] = 0;+		}+	      }+	    }+	    delete ff;+	  }+	  ctu->decRefCnt();+	} else {+	  error(-1, "Couldn't find a mapping to Unicode for font '%s'",+		gfxFont->getName() ? gfxFont->getName()->getCString()+		                   : "(unnamed)");+	}+      } else {+	if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {+	  n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();+	  codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));+	  memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),+		 n * sizeof(Gushort));+	}+      }+      if (!(fontFile = fontEngine->loadTrueTypeFont(+			   id,+			   fileName->getCString(),+			   fileName == tmpFileName,+			   codeToGID, n))) {+	error(-1, "Couldn't create a font for '%s'",+	      gfxFont->getName() ? gfxFont->getName()->getCString()+	                         : "(unnamed)");+	goto err2;+      }+      break;+    default:+      // this shouldn't happen+      goto err2;+    }+  }++  // get the font matrix+  textMat = state->getTextMat();+  fontSize = state->getFontSize();+  m11 = textMat[0] * fontSize * state->getHorizScaling();+  m12 = textMat[1] * fontSize * state->getHorizScaling();+  m21 = textMat[2] * fontSize;+  m22 = textMat[3] * fontSize;++  // for substituted fonts: adjust the font matrix -- compare the+  // width of 'm' in the original font and the substituted font+  substIdx = ((SplashOutFontFileID *)fontFile->getID())->getSubstIdx();+  if (substIdx >= 0) {+    for (code = 0; code < 256; ++code) {+      if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&+	  name[0] == 'm' && name[1] == '\0') {+	break;+      }+    }+    if (code < 256) {+      w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);+      w2 = splashOutSubstFonts[substIdx].mWidth;+      if (!gfxFont->isSymbolic()) {+	// if real font is substantially narrower than substituted+	// font, reduce the font size accordingly+	if (w1 > 0.01 && w1 < 0.9 * w2) {+	  w1 /= w2;+	  m11 *= w1;+	  m21 *= w1;+	}+      }+    }+  }++  // create the scaled font+  mat[0] = m11;  mat[1] = m12;+  mat[2] = m21;  mat[3] = m22;+  font = fontEngine->getFont(fontFile, mat, splash->getMatrix());++  if (tmpFileName) {+    delete tmpFileName;+  }+  return;++ err2:+  delete id;+ err1:+  if (tmpFileName) {+    unlink(tmpFileName->getCString());+    delete tmpFileName;+  }+  return;+}++void SplashOutputDev::stroke(GfxState *state) {+  SplashPath *path;++  if (state->getStrokeColorSpace()->isNonMarking()) {+    return;+  }+  path = convertPath(state, state->getPath());+  splash->stroke(path);+  delete path;+}++void SplashOutputDev::fill(GfxState *state) {+  SplashPath *path;++  if (state->getFillColorSpace()->isNonMarking()) {+    return;+  }+  path = convertPath(state, state->getPath());+  splash->fill(path, gFalse);+  delete path;+}++void SplashOutputDev::eoFill(GfxState *state) {+  SplashPath *path;++  if (state->getFillColorSpace()->isNonMarking()) {+    return;+  }+  path = convertPath(state, state->getPath());+  splash->fill(path, gTrue);+  delete path;+}++void SplashOutputDev::clip(GfxState *state) {+  SplashPath *path;++  path = convertPath(state, state->getPath());+  splash->clipToPath(path, gFalse);+  delete path;+}++void SplashOutputDev::eoClip(GfxState *state) {+  SplashPath *path;++  path = convertPath(state, state->getPath());+  splash->clipToPath(path, gTrue);+  delete path;+}++void SplashOutputDev::clipToStrokePath(GfxState *state) {+  SplashPath *path, *path2;++  path = convertPath(state, state->getPath());+  path2 = splash->makeStrokePath(path);+  delete path;+  splash->clipToPath(path2, gFalse);+  delete path2;+}++SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {+  SplashPath *sPath;+  GfxSubpath *subpath;+  int i, j;++  sPath = new SplashPath();+  for (i = 0; i < path->getNumSubpaths(); ++i) {+    subpath = path->getSubpath(i);+    if (subpath->getNumPoints() > 0) {+      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;++  // 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;++  // fill+  if (!(render & 1)) {+    if (!state->getFillColorSpace()->isNonMarking()) {+      splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);+    }+  }++  // stroke+  if ((render & 3) == 1 || (render & 3) == 2) {+    if (!state->getStrokeColorSpace()->isNonMarking()) {+      if ((path = font->getGlyphPath(code))) {+	path->offset((SplashCoord)x, (SplashCoord)y);+	splash->stroke(path);+	delete path;+      }+    }+  }++  // clip+  if (render & 4) {+    if ((path = font->getGlyphPath(code))) {+      path->offset((SplashCoord)x, (SplashCoord)y);+      if (textClipPath) {+	textClipPath->append(path);+	delete path;+      } else {+	textClipPath = 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 x1, y1, xMin, yMin, xMax, yMax, xt, yt;+  int i, j;++  if (!(gfxFont = state->getFont())) {+    return gFalse;+  }+  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) {+	delete t3FontCache[nT3Fonts - 1];+	--nT3Fonts;+      }+      for (j = nT3Fonts; 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),+				       (int)floor(yMin - yt),+				       (int)ceil(xMax) - (int)floor(xMin) + 3,+				       (int)ceil(yMax) - (int)floor(yMin) + 3,+				       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(t3Font, &t3Font->cacheTags[i+j],+		     t3Font->cacheData + (i+j) * t3Font->glyphSize);+      return gTrue;+    }+  }++  // push a new Type 3 glyph record+  t3gs = new T3GlyphStack();+  t3gs->next = t3GlyphStack;+  t3GlyphStack = t3gs;+  t3GlyphStack->code = code;+  t3GlyphStack->cache = t3Font;+  t3GlyphStack->cacheTag = NULL;+  t3GlyphStack->cacheData = NULL;++  return gFalse;+}++void SplashOutputDev::endType3Char(GfxState *state) {+  T3GlyphStack *t3gs;+  double *ctm;++  if (t3GlyphStack->cacheTag) {+    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),+	   t3GlyphStack->cache->glyphSize);+    delete bitmap;+    delete splash;+    bitmap = t3GlyphStack->origBitmap;+    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(t3GlyphStack->cache,+		   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);+  }+  t3gs = t3GlyphStack;+  t3GlyphStack = t3gs->next;+  delete t3gs;+}++void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {+}++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;++  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(-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) {+    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,+			      splashModeMono1, gFalse);+    splash = new Splash(bitmap, gFalse,+			t3GlyphStack->origSplash->getScreen());+    color[0] = 0;+    splash->clear(color);+    color[0] = 1;+  } else {+    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,+			      splashModeMono8, gFalse);+    splash = new Splash(bitmap, vectorAntialias,+			t3GlyphStack->origSplash->getScreen());+    color[0] = 0x00;+    splash->clear(color);+    color[0] = 0xff;+  }+  splash->setFillPattern(new SplashSolidColor(color));+  splash->setStrokePattern(new SplashSolidColor(color));+  //~ this should copy other state from t3GlyphStack->origSplash?+  state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],+		-t3Font->glyphX, -t3Font->glyphY);+  updateCTM(state, 0, 0, 0, 0, 0, 0);+}++void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,+				     T3FontCacheTag *tag, Guchar *data) {+  SplashGlyphBitmap glyph;++  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;+  GBool invert;+  int width, height, y;+};++GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {+  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;+  Guchar *p;+  SplashColorPtr q;+  int x;++  if (imgMaskData->y == imgMaskData->height) {+    return gFalse;+  }+  for (x = 0, p = imgMaskData->imgStr->getLine(), 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) {+  double *ctm;+  SplashCoord mat[6];+  SplashOutImageMaskData imgMaskData;++  if (state->getFillColorSpace()->isNonMarking()) {+    return;+  }++  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];++  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;++  splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat,+			t3GlyphStack != NULL);+  if (inlineImg) {+    while (imgMaskData.y < height) {+      imgMaskData.imgStr->getLine();+      ++imgMaskData.y;+    }+  }++  delete imgMaskData.imgStr;+  str->close();+}++struct SplashOutImageData {+  ImageStream *imgStr;+  GfxImageColorMap *colorMap;+  SplashColorPtr lookup;+  int *maskColors;+  SplashColorMode colorMode;+  int width, height, y;+};++GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,+				Guchar *alphaLine) {+  SplashOutImageData *imgData = (SplashOutImageData *)data;+  Guchar *p;+  SplashColorPtr q, col;+  GfxRGB rgb;+  GfxGray gray;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif+  int nComps, x;++  if (imgData->y == imgData->height) {+    return gFalse;+  }++  nComps = imgData->colorMap->getNumPixelComps();++  if (imgData->lookup) {+    switch (imgData->colorMode) {+    case splashModeMono1:+    case splashModeMono8:+      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;+	   x < imgData->width;+	   ++x, ++p) {+	*q++ = imgData->lookup[*p];+      }+      break;+    case splashModeRGB8:+    case splashModeBGR8:+      for (x = 0, p = imgData->imgStr->getLine(), 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 = imgData->imgStr->getLine(), 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:+      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;+	   x < imgData->width;+	   ++x, p += nComps) {+	imgData->colorMap->getGray(p, &gray);+	*q++ = colToByte(gray);+      }+      break;+    case splashModeRGB8:+    case splashModeBGR8:+      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;+	   x < imgData->width;+	   ++x, p += nComps) {+	imgData->colorMap->getRGB(p, &rgb);+	*q++ = colToByte(rgb.r);+	*q++ = colToByte(rgb.g);+	*q++ = colToByte(rgb.b);+      }+      break;+#if SPLASH_CMYK+    case splashModeCMYK8:+      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;+	   x < imgData->width;+	   ++x, p += nComps) {+	imgData->colorMap->getCMYK(p, &cmyk);+	*q++ = colToByte(cmyk.c);+	*q++ = colToByte(cmyk.m);+	*q++ = colToByte(cmyk.y);+	*q++ = colToByte(cmyk.k);+      }+      break;+#endif+    }+  }++  ++imgData->y;+  return gTrue;+}++GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,+				     Guchar *alphaLine) {+  SplashOutImageData *imgData = (SplashOutImageData *)data;+  Guchar *p, *aq;+  SplashColorPtr q, col;+  GfxRGB rgb;+  GfxGray gray;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif+  Guchar alpha;+  int nComps, x, i;++  if (imgData->y == imgData->height) {+    return gFalse;+  }++  nComps = imgData->colorMap->getNumPixelComps();++  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, 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;+      }+    }+    if (imgData->lookup) {+      switch (imgData->colorMode) {+      case splashModeMono1:+      case splashModeMono8:+	*q++ = imgData->lookup[*p];+	*aq++ = alpha;+	break;+      case splashModeRGB8:+      case splashModeBGR8:+	col = &imgData->lookup[3 * *p];+	*q++ = col[0];+	*q++ = col[1];+	*q++ = col[2];+	*aq++ = alpha;+	break;+#if SPLASH_CMYK+      case splashModeCMYK8:+	col = &imgData->lookup[4 * *p];+	*q++ = col[0];+	*q++ = col[1];+	*q++ = col[2];+	*q++ = col[3];+	*aq++ = alpha;+	break;+#endif+      }+    } else {+      switch (imgData->colorMode) {+      case splashModeMono1:+      case splashModeMono8:+	imgData->colorMap->getGray(p, &gray);+	*q++ = colToByte(gray);+	*aq++ = alpha;+	break;+      case splashModeRGB8:+      case splashModeBGR8:+	imgData->colorMap->getRGB(p, &rgb);+	*q++ = colToByte(rgb.r);+	*q++ = colToByte(rgb.g);+	*q++ = colToByte(rgb.b);+	*aq++ = alpha;+	break;+#if SPLASH_CMYK+      case splashModeCMYK8:+	imgData->colorMap->getCMYK(p, &cmyk);+	*q++ = colToByte(cmyk.c);+	*q++ = colToByte(cmyk.m);+	*q++ = colToByte(cmyk.y);+	*q++ = colToByte(cmyk.k);+	*aq++ = alpha;+	break;+#endif+      }+    }+  }++  ++imgData->y;+  return gTrue;+}++void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,+				int width, int height,+				GfxImageColorMap *colorMap,+				int *maskColors, GBool inlineImg) {+  double *ctm;+  SplashCoord mat[6];+  SplashOutImageData imgData;+  SplashColorMode srcMode;+  SplashImageSource src;+  GfxGray gray;+  GfxRGB rgb;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif+  Guchar pix;+  int n, i;++  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];++  imgData.imgStr = new ImageStream(str, width,+				   colorMap->getNumPixelComps(),+				   colorMap->getBits());+  imgData.imgStr->reset();+  imgData.colorMap = colorMap;+  imgData.maskColors = maskColors;+  imgData.colorMode = colorMode;+  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) {+    n = 1 << colorMap->getBits();+    switch (colorMode) {+    case splashModeMono1:+    case splashModeMono8:+      imgData.lookup = (SplashColorPtr)gmalloc(n);+      for (i = 0; i < n; ++i) {+	pix = (Guchar)i;+	colorMap->getGray(&pix, &gray);+	imgData.lookup[i] = colToByte(gray);+      }+      break;+    case splashModeRGB8:+    case splashModeBGR8:+      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);+      for (i = 0; i < n; ++i) {+	pix = (Guchar)i;+	colorMap->getRGB(&pix, &rgb);+	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)gmalloc(4 * n);+      for (i = 0; i < n; ++i) {+	pix = (Guchar)i;+	colorMap->getCMYK(&pix, &cmyk);+	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+      break;+    }+  }++  if (colorMode == splashModeMono1) {+    srcMode = splashModeMono8;+  } else {+    srcMode = colorMode;+  }+  src = maskColors ? &alphaImageSrc : &imageSrc;+  splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,+		    width, height, mat);+  if (inlineImg) {+    while (imgData.y < height) {+      imgData.imgStr->getLine();+      ++imgData.y;+    }+  }++  gfree(imgData.lookup);+  delete imgData.imgStr;+  str->close();+}++struct SplashOutMaskedImageData {+  ImageStream *imgStr;+  GfxImageColorMap *colorMap;+  SplashBitmap *mask;+  SplashColorPtr lookup;+  SplashColorMode colorMode;+  int width, height, y;+};++GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,+				      Guchar *alphaLine) {+  SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;+  Guchar *p, *aq;+  SplashColor maskColor;+  SplashColorPtr q, col;+  GfxRGB rgb;+  GfxGray gray;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif+  Guchar alpha;+  int nComps, x;++  if (imgData->y == imgData->height) {+    return gFalse;+  }++  nComps = imgData->colorMap->getNumPixelComps();++  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;+       x < imgData->width;+       ++x, p += nComps) {+    imgData->mask->getPixel(x, imgData->y, maskColor);+    alpha = maskColor[0] ? 0xff : 0x00;+    if (imgData->lookup) {+      switch (imgData->colorMode) {+      case splashModeMono1:+      case splashModeMono8:+	*q++ = imgData->lookup[*p];+	*aq++ = alpha;+	break;+      case splashModeRGB8:+      case splashModeBGR8:+	col = &imgData->lookup[3 * *p];+	*q++ = col[0];+	*q++ = col[1];+	*q++ = col[2];+	*aq++ = alpha;+	break;+#if SPLASH_CMYK+      case splashModeCMYK8:+	col = &imgData->lookup[4 * *p];+	*q++ = col[0];+	*q++ = col[1];+	*q++ = col[2];+	*q++ = col[3];+	*aq++ = alpha;+	break;+#endif+      }+    } else {+      switch (imgData->colorMode) {+      case splashModeMono1:+      case splashModeMono8:+	imgData->colorMap->getGray(p, &gray);+	*q++ = colToByte(gray);+	*aq++ = alpha;+	break;+      case splashModeRGB8:+      case splashModeBGR8:+	imgData->colorMap->getRGB(p, &rgb);+	*q++ = colToByte(rgb.r);+	*q++ = colToByte(rgb.g);+	*q++ = colToByte(rgb.b);+	*aq++ = alpha;+	break;+#if SPLASH_CMYK+      case splashModeCMYK8:+	imgData->colorMap->getCMYK(p, &cmyk);+	*q++ = colToByte(cmyk.c);+	*q++ = colToByte(cmyk.m);+	*q++ = colToByte(cmyk.y);+	*q++ = colToByte(cmyk.k);+	*aq++ = alpha;+	break;+#endif+      }+    }+  }++  ++imgData->y;+  return gTrue;+}++void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,+				      Stream *str, int width, int height,+				      GfxImageColorMap *colorMap,+				      Stream *maskStr, int maskWidth,+				      int maskHeight, GBool maskInvert) {+  GfxImageColorMap *maskColorMap;+  Object maskDecode, decodeLow, decodeHigh;+  double *ctm;+  SplashCoord mat[6];+  SplashOutMaskedImageData imgData;+  SplashOutImageMaskData imgMaskData;+  SplashColorMode srcMode;+  SplashBitmap *maskBitmap;+  Splash *maskSplash;+  SplashColor maskColor;+  GfxGray gray;+  GfxRGB rgb;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif+  Guchar pix;+  int n, i;++  // 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,+			maskStr, maskWidth, maskHeight, maskColorMap);+    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;+    maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);+    maskSplash = new Splash(maskBitmap, gFalse);+    maskColor[0] = 0;+    maskSplash->clear(maskColor);+    maskColor[0] = 0xff;+    maskSplash->setFillPattern(new SplashSolidColor(maskColor));+    maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,+			      maskWidth, maskHeight, mat, gFalse);+    delete imgMaskData.imgStr;+    maskStr->close();+    delete maskSplash;++    //----- draw the source image++    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];++    imgData.imgStr = new ImageStream(str, width,+				     colorMap->getNumPixelComps(),+				     colorMap->getBits());+    imgData.imgStr->reset();+    imgData.colorMap = colorMap;+    imgData.mask = maskBitmap;+    imgData.colorMode = colorMode;+    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) {+      n = 1 << colorMap->getBits();+      switch (colorMode) {+      case splashModeMono1:+      case splashModeMono8:+	imgData.lookup = (SplashColorPtr)gmalloc(n);+	for (i = 0; i < n; ++i) {+	  pix = (Guchar)i;+	  colorMap->getGray(&pix, &gray);+	  imgData.lookup[i] = colToByte(gray);+	}+	break;+      case splashModeRGB8:+      case splashModeBGR8:+	imgData.lookup = (SplashColorPtr)gmalloc(3 * n);+	for (i = 0; i < n; ++i) {+	  pix = (Guchar)i;+	  colorMap->getRGB(&pix, &rgb);+	  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)gmalloc(4 * n);+	for (i = 0; i < n; ++i) {+	  pix = (Guchar)i;+	  colorMap->getCMYK(&pix, &cmyk);+	  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 {+      srcMode = colorMode;+    }+    splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,+		      width, height, mat);++    delete maskBitmap;+    gfree(imgData.lookup);+    delete imgData.imgStr;+    str->close();+  }+}++void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,+					  Stream *str, int width, int height,+					  GfxImageColorMap *colorMap,+					  Stream *maskStr,+					  int maskWidth, int maskHeight,+					  GfxImageColorMap *maskColorMap) {+  double *ctm;+  SplashCoord mat[6];+  SplashOutImageData imgData;+  SplashOutImageData imgMaskData;+  SplashColorMode srcMode;+  SplashBitmap *maskBitmap;+  Splash *maskSplash;+  SplashColor maskColor;+  GfxGray gray;+  GfxRGB rgb;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif+  Guchar pix;+  int n, i;++  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];++  //----- set up the soft mask++  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,+				       maskColorMap->getNumPixelComps(),+				       maskColorMap->getBits());+  imgMaskData.imgStr->reset();+  imgMaskData.colorMap = maskColorMap;+  imgMaskData.maskColors = NULL;+  imgMaskData.colorMode = splashModeMono8;+  imgMaskData.width = maskWidth;+  imgMaskData.height = maskHeight;+  imgMaskData.y = 0;+  n = 1 << maskColorMap->getBits();+  imgMaskData.lookup = (SplashColorPtr)gmalloc(n);+  for (i = 0; i < n; ++i) {+    pix = (Guchar)i;+    maskColorMap->getGray(&pix, &gray);+    imgMaskData.lookup[i] = colToByte(gray);+  }+  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),+				1, splashModeMono8, gFalse);+  maskSplash = new Splash(maskBitmap, vectorAntialias);+  maskColor[0] = 0;+  maskSplash->clear(maskColor);+  maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,+			maskWidth, maskHeight, mat);+  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.maskColors = NULL;+  imgData.colorMode = colorMode;+  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) {+    n = 1 << colorMap->getBits();+    switch (colorMode) {+    case splashModeMono1:+    case splashModeMono8:+      imgData.lookup = (SplashColorPtr)gmalloc(n);+      for (i = 0; i < n; ++i) {+	pix = (Guchar)i;+	colorMap->getGray(&pix, &gray);+	imgData.lookup[i] = colToByte(gray);+      }+      break;+    case splashModeRGB8:+    case splashModeBGR8:+      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);+      for (i = 0; i < n; ++i) {+	pix = (Guchar)i;+	colorMap->getRGB(&pix, &rgb);+	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)gmalloc(4 * n);+      for (i = 0; i < n; ++i) {+	pix = (Guchar)i;+	colorMap->getCMYK(&pix, &cmyk);+	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 {+    srcMode = colorMode;+  }+  splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);++  splash->setSoftMask(NULL);+  gfree(imgData.lookup);+  delete imgData.imgStr;+  str->close();+}++void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,+					     GfxColorSpace *blendingColorSpace,+					     GBool isolated, GBool knockout,+					     GBool forSoftMask) {+  SplashTransparencyGroup *transpGroup;+  SplashColor color;+  double xMin, yMin, xMax, yMax, x, y;+  int tx, ty, w, h;++  // 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;+  }+  tx = (int)floor(xMin);+  if (tx < 0) {+    tx = 0;+  } else if (tx > bitmap->getWidth()) {+    tx = bitmap->getWidth();+  }+  ty = (int)floor(yMin);+  if (ty < 0) {+    ty = 0;+  } else if (ty > bitmap->getHeight()) {+    ty = bitmap->getHeight();+  }+  w = (int)ceil(xMax) - tx + 1;+  if (tx + w > bitmap->getWidth()) {+    w = bitmap->getWidth() - tx;+  }+  if (w < 1) {+    w = 1;+  }+  h = (int)ceil(yMax) - ty + 1;+  if (ty + h > bitmap->getHeight()) {+    h = bitmap->getHeight() - 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 ignores the blendingColorSpace arg++  // create the temporary bitmap+  bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,+			    bitmapTopDown); +  splash = new Splash(bitmap, vectorAntialias,+		      transpGroup->origSplash->getScreen());+  if (isolated) {+    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+    default:+      // make gcc happy+      break;+    }+    splash->clear(color, 0);+  } else {+    splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);+    splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty);+  }+  transpGroup->tBitmap = bitmap;+  state->shiftCTM(-tx, -ty);+  updateCTM(state, 0, 0, 0, 0, 0, 0);+}++void SplashOutputDev::endTransparencyGroup(GfxState *state) {+  double *ctm;++  // restore state+  delete splash;+  bitmap = transpGroupStack->origBitmap;+  splash = transpGroupStack->origSplash;+  ctm = state->getCTM();+  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 tx, ty;++  tx = transpGroupStack->tx;+  ty = transpGroupStack->ty;+  tBitmap = transpGroupStack->tBitmap;+  isolated = transpGroupStack->isolated;++  // paint the transparency group onto the parent bitmap+  // - the clip path was set in the parent's state)+  splash->composite(tBitmap, 0, 0, tx, ty,+		    tBitmap->getWidth(), tBitmap->getHeight(),+		    gFalse, !isolated);++  // 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;+  GfxGray gray;+  GfxRGB rgb;+#if SPLASH_CMYK+  GfxCMYK cmyk;+#endif+  double lum, lum2;+  int tx, ty, x, y;++  tx = transpGroupStack->tx;+  ty = transpGroupStack->ty;+  tBitmap = transpGroupStack->tBitmap;++  // composite with backdrop color+  if (!alpha && colorMode != splashModeMono1) {+    //~ need to correctly handle the case where no blending color+    //~ space is given+    tSplash = new Splash(tBitmap, vectorAntialias,+			 transpGroupStack->origSplash->getScreen());+    if (transpGroupStack->blendingColorSpace) {+      switch (colorMode) {+      case splashModeMono1:+	// transparency is not supported in mono1 mode+	break;+      case splashModeMono8:+	transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);+	color[0] = colToByte(gray);+	tSplash->compositeBackground(color);+	break;+      case splashModeRGB8:+      case splashModeBGR8:+	transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);+	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);+	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;+    }+  }++  softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),+			      1, splashModeMono8, gFalse);+  memset(softMask->getDataPtr(), 0,+	 softMask->getRowSize() * softMask->getHeight());+  p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;+  for (y = 0; y < tBitmap->getHeight(); ++y) {+    for (x = 0; x < tBitmap->getWidth(); ++x) {+      tBitmap->getPixel(x, y, color);+      if (alpha) {+	//~ unimplemented+      } else {+	// convert to luminosity+	switch (colorMode) {+	case splashModeMono1:+	case splashModeMono8:+	  lum = color[0] / 255.0;+	  break;+	case splashModeRGB8:+	case splashModeBGR8:+	  lum = (0.3 / 255.0) * color[0] ++	        (0.59 / 255.0) * color[1] ++	        (0.11 / 255.0) * color[2];+	  break;+#if SPLASH_CMYK+	case splashModeCMYK8:+	  lum = (1 - color[4] / 255.0)+	        - (0.3 / 255.0) * color[0]+	        - (0.59 / 255.0) * color[1]+	        - (0.11 / 255.0) * color[2];+	  if (lum < 0) {+	    lum = 0;+	  }+	  break;+#endif+	}+	if (transferFunc) {+	  transferFunc->transform(&lum, &lum2);+	} else {+	  lum2 = lum;+	}+	p[x] = (int)(lum2 * 255.0 + 0.5);+      }+    }+    p += softMask->getRowSize();+  }+  splash->setSoftMask(softMask);++  // 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);+  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(r);+  rgb.g = byteToCol(g);+  rgb.b = byteToCol(b);+  gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);+  if (gray > gfxColorComp1) {+    gray = gfxColorComp1;+  }+#if SPLASH_CMYK+  cmyk.c = gfxColorComp1 - rgb.r;+  cmyk.m = gfxColorComp1 - rgb.g;+  cmyk.y = gfxColorComp1 - rgb.b;+  cmyk.k = 0;+  splash->setFillPattern(getColor(gray, &rgb, &cmyk));+#else+  splash->setFillPattern(getColor(gray, &rgb));+#endif+}++SplashFont *SplashOutputDev::getFont(GString *name, double *textMatA) {+  DisplayFontParam *dfp;+  Ref ref;+  SplashOutFontFileID *id;+  SplashFontFile *fontFile;+  SplashFont *fontObj;+  FoFiTrueType *ff;+  Gushort *codeToGID;+  Unicode u;+  SplashCoord textMat[4];+  int cmap, i;++  for (i = 0; i < 16; ++i) {+    if (!name->cmp(splashOutSubstFonts[i].name)) {+      break;+    }+  }+  if (i == 16) {+    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 {+    dfp = globalParams->getDisplayFont(name);+    if (dfp && dfp->kind == displayFontT1) {+      fontFile = fontEngine->loadType1Font(id, dfp->t1.fileName->getCString(),+					   gFalse, winAnsiEncoding);+    } else if (dfp && dfp->kind == displayFontTT) {+      if (!(ff = FoFiTrueType::load(dfp->tt.fileName->getCString()))) {+	return NULL;+      }+      for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {+	if ((ff->getCmapPlatform(cmap) == 3 &&+	     ff->getCmapEncoding(cmap) == 1) ||+	    ff->getCmapPlatform(cmap) == 0) {+	  break;+	}+      }+      if (cmap == ff->getNumCmaps()) {+	delete ff;+	return NULL;+      }+      codeToGID = (Gushort *)gmallocn(256, sizeof(Gushort));+      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,+					      dfp->tt.fileName->getCString(),+					      gFalse, codeToGID, 256);+    } else {+      return NULL;+    }+  }++  // create the scaled font+  textMat[0] = (SplashCoord)textMatA[0];+  textMat[1] = (SplashCoord)textMatA[1];+  textMat[2] = (SplashCoord)textMatA[2];+  textMat[3] = (SplashCoord)textMatA[3];+  fontObj = fontEngine->getFont(fontFile, textMat, splash->getMatrix());++  return fontObj;+}++#if 1 //~tmp: turn off anti-aliasing temporarily+GBool SplashOutputDev::getVectorAntialias() {+  return splash->getVectorAntialias();+}++void SplashOutputDev::setVectorAntialias(GBool vaa) {+  splash->setVectorAntialias(vaa);+}+#endif
+ xpdf/SplashOutputDev.h view
@@ -0,0 +1,247 @@+//========================================================================+//+// 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 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 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);++  //----- 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);++  //----- 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);+  virtual void drawImage(GfxState *state, Object *ref, Stream *str,+			 int width, int height, GfxImageColorMap *colorMap,+			 int *maskColors, GBool inlineImg);+  virtual void drawMaskedImage(GfxState *state, Object *ref, Stream *str,+			       int width, int height,+			       GfxImageColorMap *colorMap,+			       Stream *maskStr, int maskWidth, int maskHeight,+			       GBool maskInvert);+  virtual void drawSoftMaskedImage(GfxState *state, Object *ref, Stream *str,+				   int width, int height,+				   GfxImageColorMap *colorMap,+				   Stream *maskStr,+				   int maskWidth, int maskHeight,+				   GfxImageColorMap *maskColorMap);++  //----- 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 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();++  // 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, double *textMatA);++  SplashFont *getCurrentFont() { return font; }++#if 1 //~tmp: turn off anti-aliasing temporarily+  virtual GBool getVectorAntialias();+  virtual void setVectorAntialias(GBool vaa);+#endif++private:++  void setupScreenParams(double hDPI, double vDPI);+#if SPLASH_CMYK+  SplashPattern *getColor(GfxGray gray, GfxRGB *rgb, GfxCMYK *cmyk);+#else+  SplashPattern *getColor(GfxGray gray, GfxRGB *rgb);+#endif+  SplashPath *convertPath(GfxState *state, GfxPath *path);+  void doUpdateFont(GfxState *state);+  void drawType3Glyph(T3FontCache *t3Font,+		      T3FontCacheTag *tag, Guchar *data);+  static GBool imageMaskSrc(void *data, SplashColorPtr 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);++  SplashColorMode colorMode;+  int bitmapRowPad;+  GBool bitmapTopDown;+  GBool allowAntialias;+  GBool vectorAntialias;+  GBool reverseVideo;		// reverse video mode+  SplashColor paperColor;	// paper color+  SplashScreenParams screenParams;++  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;+};++#endif
+ xpdf/SplashPath.cc view
@@ -0,0 +1,184 @@+//========================================================================+//+// SplashPath.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <string.h>+#include "gmem.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;+  }+}++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] &= ~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] &= ~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() {+  if (noCurrentPoint()) {+    return splashErrNoCurPt;+  }+  if (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) {+  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;+  ++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;+}
+ xpdf/SplashPath.h view
@@ -0,0 +1,121 @@+//========================================================================+//+// SplashPath.h+//+//========================================================================++#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;+};++//------------------------------------------------------------------------+// 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.+  SplashError close();++  // 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>.+  void addStrokeAdjustHint(int ctrl0, int ctrl1, int firstPt, int lastPt);++  // 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, double *x, double *y, Guchar *f)+    { *x = pts[i].x; *y = pts[i].y; *f = flags[i]; }++  // Get the current point.+  GBool getCurPt(SplashCoord *x, SplashCoord *y);++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
+ xpdf/SplashPattern.cc view
@@ -0,0 +1,40 @@+//========================================================================+//+// SplashPattern.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#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);+}
+ xpdf/SplashPattern.h view
@@ -0,0 +1,65 @@+//========================================================================+//+// SplashPattern.h+//+//========================================================================++#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
+ xpdf/SplashScreen.cc view
@@ -0,0 +1,383 @@+//========================================================================+//+// SplashScreen.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <string.h>+#include "gmem.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;+};++static int cmpDistances(const void *p0, const void *p1) {+  return ((SplashScreenPoint *)p0)->dist - ((SplashScreenPoint *)p1)->dist;+}++//------------------------------------------------------------------------+// 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, black, white;+  int i;++  if (!params) {+    params = &defaultParams;+  }++  switch (params->type) {++  case splashScreenDispersed:+    // size must be a power of 2+    for (size = 1; size < params->size; size <<= 1) ;+    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));+    buildDispersedMatrix(size/2, size/2, 1, size/2, 1);+    break;++  case splashScreenClustered:+    // size must be even+    size = (params->size >> 1) << 1;+    if (size < 2) {+      size = 2;+    }+    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));+    buildClusteredMatrix();+    break;++  case splashScreenStochasticClustered:+    // size must be at least 2*r+    if (params->size < 2 * params->dotRadius) {+      size = 2 * params->dotRadius;+    } else {+      size = params->size;+    }+    mat = (Guchar *)gmallocn(size * size, sizeof(Guchar));+    buildSCDMatrix(params->dotRadius);+    break;+  }++  // 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 = splashRound((SplashCoord)255.0 *+		    splashPow((SplashCoord)mat[i] / 255.0, params->gamma));+    if (u < black) {+      u = black;+    } else if (u >= white) {+      u = 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 * size + j] = 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 * size + 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+  minVal = 1;+  maxVal = 0;+  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 * size + 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 = 1 + (254 * (2*i)) / (2*size*size2 - 1);+    mat[y1 * size + x1] = val;+    val = 1 + (254 * (2*i+1)) / (2*size*size2 - 1);+    if (y1 < size2) {+      mat[(y1 + size2) * size + x1 + size2] = val;+    } else {+      mat[(y1 - size2) * size + 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*size + 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*size + 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*size + x0] = 1;+	    grid[y0*size + x1] = 1;+	    grid[y1*size + x0] = 1;+	    grid[y1*size + 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*size + x] = iMin;+      dist[y*size + 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*size + x] == i) {+	  pts[n].x = x;+	  pts[n].y = y;+	  pts[n].dist = distance(dots[i].x, dots[i].y, x, y);+	  ++n;+	}+      }+    }+    qsort(pts, n, sizeof(SplashScreenPoint), &cmpDistances);+    for (j = 0; j < n; ++j) {+      // map values in [0 .. n-1] --> [255 .. 1]+      mat[pts[j].y * size + pts[j].x] = 255 - (254 * j) / (n - 1);+    }+  }++  gfree(pts);+  gfree(region);+  gfree(dist);++  gfree(dots);+}++SplashScreen::SplashScreen(SplashScreen *screen) {+  size = screen->size;+  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);+}++int SplashScreen::test(int x, int y, Guchar value) {+  int xx, yy;++  if (value < minVal) {+    return 0;+  }+  if (value >= maxVal) {+    return 1;+  }+  if ((xx = x % size) < 0) {+    xx = -xx;+  }+  if ((yy = y % size) < 0) {+    yy = -yy;+  }+  return value < mat[yy * size + xx] ? 0 : 1;+}++GBool SplashScreen::isStatic(Guchar value) {+  return value < minVal || value >= maxVal;+}
+ xpdf/SplashScreen.h view
@@ -0,0 +1,56 @@+//========================================================================+//+// SplashScreen.h+//+//========================================================================++#ifndef SPLASHSCREEN_H+#define SPLASHSCREEN_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashTypes.h"++//------------------------------------------------------------------------+// SplashScreen+//------------------------------------------------------------------------++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);++  // 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);++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+  Guchar minVal;		// any pixel value below minVal generates+				//   solid black+  Guchar maxVal;		// any pixel value above maxVal generates+				//   solid white+};++#endif
+ xpdf/SplashState.cc view
@@ -0,0 +1,165 @@+//========================================================================+//+// SplashState.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <string.h>+#include "gmem.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, 4+};++SplashState::SplashState(int width, int height, GBool vectorAntialias,+			 SplashScreenParams *screenParams) {+  SplashColor color;++  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 = 0;+  lineCap = splashLineCapButt;+  lineJoin = splashLineJoinMiter;+  miterLimit = 10;+  flatness = 1;+  lineDash = NULL;+  lineDashLength = 0;+  lineDashPhase = 0;+  strokeAdjust = gFalse;+  clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias);+  softMask = NULL;+  deleteSoftMask = gFalse;+  inNonIsolatedGroup = gFalse;+  next = NULL;+}++SplashState::SplashState(int width, int height, GBool vectorAntialias,+			 SplashScreen *screenA) {+  SplashColor color;++  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 = 0;+  lineCap = splashLineCapButt;+  lineJoin = splashLineJoinMiter;+  miterLimit = 10;+  flatness = 1;+  lineDash = NULL;+  lineDashLength = 0;+  lineDashPhase = 0;+  strokeAdjust = gFalse;+  clip = new SplashClip(0, 0, width - 0.001, height - 0.001, vectorAntialias);+  softMask = NULL;+  deleteSoftMask = gFalse;+  inNonIsolatedGroup = 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->copy();+  softMask = state->softMask;+  deleteSoftMask = gFalse;+  inNonIsolatedGroup = state->inNonIsolatedGroup;+  next = NULL;+}++SplashState::~SplashState() {+  delete strokePattern;+  delete fillPattern;+  delete screen;+  gfree(lineDash);+  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;+}++void SplashState::setSoftMask(SplashBitmap *softMaskA) {+  if (deleteSoftMask) {+    delete softMask;+  }+  softMask = softMaskA;+  deleteSoftMask = gTrue;+}
+ xpdf/SplashState.h view
@@ -0,0 +1,103 @@+//========================================================================+//+// SplashState.h+//+//========================================================================++#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;++//------------------------------------------------------------------------+// 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);++  // Set the soft mask bitmap.+  void setSoftMask(SplashBitmap *softMaskA);++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;+  GBool strokeAdjust;+  SplashClip *clip;+  SplashBitmap *softMask;+  GBool deleteSoftMask;+  GBool inNonIsolatedGroup;++  SplashState *next;		// used by Splash class++  friend class Splash;+};++#endif
+ xpdf/SplashT1Font.cc view
@@ -0,0 +1,287 @@+//========================================================================+//+// SplashT1Font.cc+//+//========================================================================++#include <aconf.h>++#if HAVE_T1LIB_H++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <t1lib.h>+#include "gmem.h"+#include "SplashMath.h"+#include "SplashGlyphBitmap.h"+#include "SplashPath.h"+#include "SplashT1FontEngine.h"+#include "SplashT1FontFile.h"+#include "SplashT1Font.h"++//------------------------------------------------------------------------++static Guchar bitReverse[256] = {+  0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,+  0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,+  0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,+  0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,+  0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,+  0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,+  0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,+  0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,+  0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,+  0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,+  0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,+  0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,+  0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,+  0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,+  0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,+  0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,+  0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,+  0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,+  0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,+  0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,+  0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,+  0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,+  0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,+  0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,+  0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,+  0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,+  0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,+  0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,+  0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,+  0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,+  0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,+  0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff+};++//------------------------------------------------------------------------+// SplashT1Font+//------------------------------------------------------------------------++SplashT1Font::SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA,+			   SplashCoord *textMatA):+  SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa)+{+  T1_TMATRIX matrix;+  BBox bbox;+  SplashCoord bbx0, bby0, bbx1, bby1;+  int x, y;++  t1libID = T1_CopyFont(fontFileA->t1libID);+  outlineID = -1;++  // compute font size+  size = (float)splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);++  // transform the four corners of the font bounding box -- the min+  // and max values form the bounding box of the transformed font+  bbox = T1_GetFontBBox(t1libID);+  bbx0 = 0.001 * bbox.llx;+  bby0 = 0.001 * bbox.lly;+  bbx1 = 0.001 * bbox.urx;+  bby1 = 0.001 * bbox.ury;+  // some fonts are completely broken, so we fake it (with values+  // large enough that most glyphs should fit)+  if (bbx0 == 0 && bby0 == 0 && bbx1 == 0 && bby1 == 0) {+    bbx0 = bby0 = -0.5;+    bbx1 = bby1 = 1.5;+  }+  x = (int)(mat[0] * bbx0 + mat[2] * bby0);+  xMin = xMax = x;+  y = (int)(mat[1] * bbx0 + mat[3] * bby0);+  yMin = yMax = y;+  x = (int)(mat[0] * bbx0 + mat[2] * bby1);+  if (x < xMin) {+    xMin = x;+  } else if (x > xMax) {+    xMax = x;+  }+  y = (int)(mat[1] * bbx0 + mat[3] * bby1);+  if (y < yMin) {+    yMin = y;+  } else if (y > yMax) {+    yMax = y;+  }+  x = (int)(mat[0] * bbx1 + mat[2] * bby0);+  if (x < xMin) {+    xMin = x;+  } else if (x > xMax) {+    xMax = x;+  }+  y = (int)(mat[1] * bbx1 + mat[3] * bby0);+  if (y < yMin) {+    yMin = y;+  } else if (y > yMax) {+    yMax = y;+  }+  x = (int)(mat[0] * bbx1 + mat[2] * bby1);+  if (x < xMin) {+    xMin = x;+  } else if (x > xMax) {+    xMax = x;+  }+  y = (int)(mat[1] * bbx1 + mat[3] * bby1);+  if (y < yMin) {+    yMin = y;+  } else if (y > yMax) {+    yMax = y;+  }+  // This is a kludge: some buggy PDF generators embed fonts with+  // zero bounding boxes.+  if (xMax == xMin) {+    xMin = 0;+    xMax = (int)size;+  }+  if (yMax == yMin) {+    yMin = 0;+    yMax = (int)(1.2 * size);+  }+  // Another kludge: an unusually large xMin or yMin coordinate is+  // probably wrong.+  if (xMin > 0) {+    xMin = 0;+  }+  if (yMin > 0) {+    yMin = 0;+  }+  // Another kludge: t1lib doesn't correctly handle fonts with+  // real (non-integer) bounding box coordinates.+  if (xMax - xMin > 5000) {+    xMin = 0;+    xMax = (int)size;+  }+  if (yMax - yMin > 5000) {+    yMin = 0;+    yMax = (int)(1.2 * size);+  }++  // transform the font+  matrix.cxx = (double)mat[0] / size;+  matrix.cxy = (double)mat[1] / size;+  matrix.cyx = (double)mat[2] / size;+  matrix.cyy = (double)mat[3] / size;+  T1_TransformFont(t1libID, &matrix);+}++SplashT1Font::~SplashT1Font() {+  T1_DeleteFont(t1libID);+  if (outlineID >= 0) {+    T1_DeleteFont(outlineID);+  }+}++GBool SplashT1Font::getGlyph(int c, int xFrac, int yFrac,+			     SplashGlyphBitmap *bitmap) {+  return SplashFont::getGlyph(c, 0, 0, bitmap);+}++GBool SplashT1Font::makeGlyph(int c, int xFrac, int yFrac,+			      SplashGlyphBitmap *bitmap) {+  GLYPH *glyph;+  int n, i;++  if (aa) {+    glyph = T1_AASetChar(t1libID, c, size, NULL);+  } else {+    glyph = T1_SetChar(t1libID, c, size, NULL);+  }+  if (!glyph) {+    return gFalse;+  }++  bitmap->x = -glyph->metrics.leftSideBearing;+  bitmap->y = glyph->metrics.ascent;+  bitmap->w = glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing;+  bitmap->h = glyph->metrics.ascent - glyph->metrics.descent;+  bitmap->aa = aa;+  if (aa) {+    bitmap->data = (Guchar *)glyph->bits;+    bitmap->freeData = gFalse;+  } else {+    n = bitmap->h * ((bitmap->w + 7) >> 3);+    bitmap->data = (Guchar *)gmalloc(n);+    for (i = 0; i < n; ++i) {+      bitmap->data[i] = bitReverse[glyph->bits[i] & 0xff];+    }+    bitmap->freeData = gTrue;+  }++  return gTrue;+}++SplashPath *SplashT1Font::getGlyphPath(int c) {+  T1_TMATRIX matrix;+  SplashPath *path;+  T1_OUTLINE *outline;+  T1_PATHSEGMENT *seg;+  T1_BEZIERSEGMENT *bez;+  SplashCoord x, y, x1, y1;+  GBool needClose;++  if (outlineID < 0) {+    outlineID = T1_CopyFont(((SplashT1FontFile *)fontFile)->t1libID);+    outlineSize = (float)splashSqrt(textMat[2]*textMat[2] ++				    textMat[3]*textMat[3]);+    matrix.cxx = (double)textMat[0] / outlineSize;+    matrix.cxy = (double)textMat[1] / outlineSize;+    matrix.cyx = (double)textMat[2] / outlineSize;+    matrix.cyy = (double)textMat[3] / outlineSize;+    // t1lib doesn't seem to handle small sizes correctly here, so set+    // the size to 1000, and scale the resulting coordinates later+    outlineMul = (float)(outlineSize / 65536000.0);+    outlineSize = 1000;+    T1_TransformFont(outlineID, &matrix);+  }++  path = new SplashPath();+  if ((outline = T1_GetCharOutline(outlineID, c, outlineSize, NULL))) {+    x = 0;+    y = 0;+    needClose = gFalse;+    for (seg = outline; seg; seg = seg->link) {+      switch (seg->type) {+      case T1_PATHTYPE_MOVE:+	if (needClose) {+	  path->close();+	  needClose = gFalse;+	}+	x += seg->dest.x * outlineMul;+	y += seg->dest.y * outlineMul;+	path->moveTo(x, -y);+	break;+      case T1_PATHTYPE_LINE:+	x += seg->dest.x * outlineMul;+	y += seg->dest.y * outlineMul;+	path->lineTo(x, -y);+	needClose = gTrue;+	break;+      case T1_PATHTYPE_BEZIER:+	bez = (T1_BEZIERSEGMENT *)seg;+	x1 = x + (SplashCoord)(bez->dest.x * outlineMul);+	y1 = y + (SplashCoord)(bez->dest.y * outlineMul);+	path->curveTo(x + (SplashCoord)(bez->B.x * outlineMul),+		      -(y + (SplashCoord)(bez->B.y * outlineMul)),+		      x + (SplashCoord)(bez->C.x * outlineMul),+		      -(y + (SplashCoord)(bez->C.y * outlineMul)),+		      x1, -y1);+	x = x1;+	y = y1;+	needClose = gTrue;+	break;+      }+    }+    if (needClose) {+      path->close();+    }+    T1_FreeOutline(outline);+  }++  return path;+}++#endif // HAVE_T1LIB_H
+ xpdf/SplashT1Font.h view
@@ -0,0 +1,57 @@+//========================================================================+//+// SplashT1Font.h+//+//========================================================================++#ifndef SPLASHT1FONT_H+#define SPLASHT1FONT_H++#include <aconf.h>++#if HAVE_T1LIB_H++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashFont.h"++class SplashT1FontFile;++//------------------------------------------------------------------------+// SplashT1Font+//------------------------------------------------------------------------++class SplashT1Font: public SplashFont {+public:++  SplashT1Font(SplashT1FontFile *fontFileA, SplashCoord *matA,+	       SplashCoord *textMatA);++  virtual ~SplashT1Font();++  // 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:++  int t1libID;			// t1lib font ID+  int outlineID;		// t1lib font ID for glyph outlines+  float size;+  float outlineSize;		// size for glyph outlines+  float outlineMul;+};++#endif // HAVE_T1LIB_H++#endif
+ xpdf/SplashT1FontEngine.cc view
@@ -0,0 +1,124 @@+//========================================================================+//+// SplashT1FontEngine.cc+//+//========================================================================++#include <aconf.h>++#if HAVE_T1LIB_H++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <stdio.h>+#ifndef WIN32+#  include <unistd.h>+#endif+#include <t1lib.h>+#include "GString.h"+#include "gfile.h"+#include "FoFiType1C.h"+#include "SplashT1FontFile.h"+#include "SplashT1FontEngine.h"++#ifdef VMS+#if (__VMS_VER < 70000000)+extern "C" int unlink(char *filename);+#endif+#endif++//------------------------------------------------------------------------++int SplashT1FontEngine::t1libInitCount = 0;++//------------------------------------------------------------------------++static void fileWrite(void *stream, char *data, int len) {+  fwrite(data, 1, len, (FILE *)stream);+}++//------------------------------------------------------------------------+// SplashT1FontEngine+//------------------------------------------------------------------------++SplashT1FontEngine::SplashT1FontEngine(GBool aaA) {+  aa = aaA;+}++SplashT1FontEngine *SplashT1FontEngine::init(GBool aaA) {+  // grayVals[i] = round(i * 255 / 16)+  static unsigned long grayVals[17] = {+    0, 16, 32, 48, 64, 80, 96, 112, 128, 143, 159, 175, 191, 207, 223, 239, 255+  };++  //~ for multithreading: need a mutex here+  if (t1libInitCount == 0) {+    T1_SetBitmapPad(8);+    if (!T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE |+		    T1_NO_AFM)) {+      return NULL;+    }+    if (aaA) {+      T1_AASetBitsPerPixel(8);+      T1_AASetLevel(T1_AA_HIGH);+      T1_AAHSetGrayValues(grayVals);+    } else {+      T1_AANSetGrayValues(0, 1);+    }+  }+  ++t1libInitCount;++  return new SplashT1FontEngine(aaA);+}++SplashT1FontEngine::~SplashT1FontEngine() {+  //~ for multithreading: need a mutex here+  if (--t1libInitCount == 0) {+    T1_CloseLib();+  }+}++SplashFontFile *SplashT1FontEngine::loadType1Font(SplashFontFileID *idA,+						  char *fileName,+						  GBool deleteFile,+						  char **enc) {+  return SplashT1FontFile::loadType1Font(this, idA, fileName, deleteFile, enc);+}++SplashFontFile *SplashT1FontEngine::loadType1CFont(SplashFontFileID *idA,+						   char *fileName,+						   GBool deleteFile,+						   char **enc) {+  FoFiType1C *ff;+  GString *tmpFileName;+  FILE *tmpFile;+  SplashFontFile *ret;++  if (!(ff = FoFiType1C::load(fileName))) {+    return NULL;+  }+  tmpFileName = NULL;+  if (!openTempFile(&tmpFileName, &tmpFile, "wb", NULL)) {+    delete ff;+    return NULL;+  }+  ff->convertToType1(NULL, NULL, gTrue, &fileWrite, tmpFile);+  delete ff;+  fclose(tmpFile);+  ret = SplashT1FontFile::loadType1Font(this, idA, tmpFileName->getCString(),+					gTrue, enc);+  if (ret) {+    if (deleteFile) {+      unlink(fileName);+    }+  } else {+    unlink(tmpFileName->getCString());+  }+  delete tmpFileName;+  return ret;+}++#endif // HAVE_T1LIB_H
+ xpdf/SplashT1FontEngine.h view
@@ -0,0 +1,53 @@+//========================================================================+//+// SplashT1FontEngine.h+//+//========================================================================++#ifndef SPLASHT1FONTENGINE_H+#define SPLASHT1FONTENGINE_H++#include <aconf.h>++#if HAVE_T1LIB_H++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "gtypes.h"++class SplashFontFile;+class SplashFontFileID;++//------------------------------------------------------------------------+// SplashT1FontEngine+//------------------------------------------------------------------------++class SplashT1FontEngine {+public:++  static SplashT1FontEngine *init(GBool aaA);++  ~SplashT1FontEngine();++  // Load fonts.+  SplashFontFile *loadType1Font(SplashFontFileID *idA, char *fileName,+				GBool deleteFile, char **enc);+  SplashFontFile *loadType1CFont(SplashFontFileID *idA, char *fileName,+				 GBool deleteFile, char **enc);++private:++  SplashT1FontEngine(GBool aaA);++  static int t1libInitCount;+  GBool aa;++  friend class SplashT1FontFile;+  friend class SplashT1Font;+};++#endif // HAVE_T1LIB_H++#endif
+ xpdf/SplashT1FontFile.cc view
@@ -0,0 +1,97 @@+//========================================================================+//+// SplashT1FontFile.cc+//+//========================================================================++#include <aconf.h>++#if HAVE_T1LIB_H++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <string.h>+#include <t1lib.h>+#include "gmem.h"+#include "SplashT1FontEngine.h"+#include "SplashT1Font.h"+#include "SplashT1FontFile.h"++//------------------------------------------------------------------------+// SplashT1FontFile+//------------------------------------------------------------------------++SplashFontFile *SplashT1FontFile::loadType1Font(SplashT1FontEngine *engineA,+						SplashFontFileID *idA,+						char *fileNameA,+						GBool deleteFileA,+						char **encA) {+  int t1libIDA;+  char **encTmp;+  char *encStrTmp;+  int encStrSize;+  char *encPtr;+  int i;++  // load the font file+  if ((t1libIDA = T1_AddFont(fileNameA)) < 0) {+    return NULL;+  }+  T1_LoadFont(t1libIDA);++  // reencode it+  encStrSize = 0;+  for (i = 0; i < 256; ++i) {+    if (encA[i]) {+      encStrSize += strlen(encA[i]) + 1;+    }+  }+  encTmp = (char **)gmallocn(257, sizeof(char *));+  encStrTmp = (char *)gmallocn(encStrSize, sizeof(char));+  encPtr = encStrTmp;+  for (i = 0; i < 256; ++i) {+    if (encA[i]) {+      strcpy(encPtr, encA[i]);+      encTmp[i] = encPtr;+      encPtr += strlen(encPtr) + 1;+    } else {+      encTmp[i] = ".notdef";+    }+  }+  encTmp[256] = "custom";+  T1_ReencodeFont(t1libIDA, encTmp);++  return new SplashT1FontFile(engineA, idA, fileNameA, deleteFileA,+			      t1libIDA, encTmp, encStrTmp);+}++SplashT1FontFile::SplashT1FontFile(SplashT1FontEngine *engineA,+				   SplashFontFileID *idA,+				   char *fileNameA, GBool deleteFileA,+				   int t1libIDA, char **encA, char *encStrA):+  SplashFontFile(idA, fileNameA, deleteFileA)+{+  engine = engineA;+  t1libID = t1libIDA;+  enc = encA;+  encStr = encStrA;+}++SplashT1FontFile::~SplashT1FontFile() {+  gfree(encStr);+  gfree(enc);+  T1_DeleteFont(t1libID);+}++SplashFont *SplashT1FontFile::makeFont(SplashCoord *mat,+				       SplashCoord *textMat) {+  SplashFont *font;++  font = new SplashT1Font(this, mat, textMat);+  font->initCache();+  return font;+}++#endif // HAVE_T1LIB_H
+ xpdf/SplashT1FontFile.h view
@@ -0,0 +1,58 @@+//========================================================================+//+// SplashT1FontFile.h+//+//========================================================================++#ifndef SPLASHT1FONTFILE_H+#define SPLASHT1FONTFILE_H++#include <aconf.h>++#if HAVE_T1LIB_H++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashFontFile.h"++class SplashT1FontEngine;++//------------------------------------------------------------------------+// SplashT1FontFile+//------------------------------------------------------------------------++class SplashT1FontFile: public SplashFontFile {+public:++  static SplashFontFile *loadType1Font(SplashT1FontEngine *engineA,+				       SplashFontFileID *idA,+				       char *fileNameA, GBool deleteFileA,+				       char **encA);++  virtual ~SplashT1FontFile();++  // Create a new SplashT1Font, i.e., a scaled instance of this font+  // file.+  virtual SplashFont *makeFont(SplashCoord *mat,+			       SplashCoord *textMat);++private:++  SplashT1FontFile(SplashT1FontEngine *engineA,+		   SplashFontFileID *idA,+		   char *fileNameA, GBool deleteFileA,+		   int t1libIDA, char **encA, char *encStrA);++  SplashT1FontEngine *engine;+  int t1libID;			// t1lib font ID+  char **enc;+  char *encStr;++  friend class SplashT1Font;+};++#endif // HAVE_T1LIB_H++#endif
+ xpdf/SplashTypes.h view
@@ -0,0 +1,132 @@+//========================================================================+//+// SplashTypes.h+//+//========================================================================++#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+#if SPLASH_CMYK+#  define splashMaxColorComps 4+#else+#  define splashMaxColorComps 3+#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);++//------------------------------------------------------------------------+// 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
+ xpdf/SplashXPath.cc view
@@ -0,0 +1,438 @@+//========================================================================+//+// SplashXPath.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <string.h>+#include "gmem.h"+#include "SplashMath.h"+#include "SplashPath.h"+#include "SplashXPath.h"++//------------------------------------------------------------------------++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::SplashXPath() {+  segs = NULL;+  length = size = 0;+}++SplashXPath::SplashXPath(SplashPath *path, SplashCoord *matrix,+			 SplashCoord flatness, GBool closeSubpaths) {+  SplashPathHint *hint;+  SplashXPathPoint *pts;+  SplashXPathAdjust *adjusts, *adjust;+  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xsp, ysp;+  SplashCoord adj0, adj1, w;+  int ww;+  int curSubpath, curSubpathX, i, j;++  // 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);+  }++  // set up the stroke adjustment hints+  if (path->hints) {+    adjusts = (SplashXPathAdjust *)gmallocn(path->hintsLength,+					    sizeof(SplashXPathAdjust));+    for (i = 0; i < path->hintsLength; ++i) {+      hint = &path->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;+      if (x0 == x1 && x2 == x3) {+	adjusts[i].vert = gTrue;+	adj0 = x0;+	adj1 = x2;+      } else if (y0 == y1 && y2 == y3) {+	adjusts[i].vert = gFalse;+	adj0 = y0;+	adj1 = y2;+      } else {+	gfree(adjusts);+	adjusts = NULL;+	break;+      }+      if (adj0 > adj1) {+	x0 = adj0;+	adj0 = adj1;+	adj1 = x0;+      }+      w = adj1 - adj0;+      ww = splashRound(w);+      if (ww == 0) {+	ww = 1;+      }+      adjusts[i].x0a = adj0 - 0.01;+      adjusts[i].x0b = adj0 + 0.01;+      adjusts[i].xma = (SplashCoord)0.5 * (adj0 + adj1) - 0.01;+      adjusts[i].xmb = (SplashCoord)0.5 * (adj0 + adj1) + 0.01;+      adjusts[i].x1a = adj1 - 0.01;+      adjusts[i].x1b = adj1 + 0.01;+      adjusts[i].x0 = (SplashCoord)splashRound(adj0);+      adjusts[i].x1 = adjusts[i].x0 + ww - 0.01;+      adjusts[i].xm = (SplashCoord)0.5 * (adjusts[i].x0 + adjusts[i].x1);+      adjusts[i].firstPt = hint->firstPt;+      adjusts[i].lastPt = hint->lastPt;+    }++  } else {+    adjusts = NULL;+  }++  // perform stroke adjustment+  if (adjusts) {+    for (i = 0, adjust = adjusts; i < path->hintsLength; ++i, ++adjust) {+      for (j = adjust->firstPt; j <= adjust->lastPt; ++j) {+	strokeAdjust(adjust, &pts[j].x, &pts[j].y);+      }+    }+    gfree(adjusts);+  }++  segs = NULL;+  length = size = 0;++  x0 = y0 = xsp = ysp = 0; // make gcc happy+  adj0 = adj1 = 0; // make gcc happy+  curSubpath = 0;+  curSubpathX = 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;+      curSubpathX = length;+      ++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,+		   path->flags[i-1] & splashPathFirst,+		   path->flags[i] & splashPathLast,+		   !closeSubpaths &&+		     (path->flags[i-1] & splashPathFirst) &&+		     !(path->flags[i-1] & splashPathClosed),+		   !closeSubpaths &&+		     (path->flags[i] & splashPathLast) &&+		     !(path->flags[i] & splashPathClosed));+	x0 = x1;+	y0 = y1;+	++i;+      }++      // close a subpath+      if (closeSubpaths &&+	  (path->flags[i-1] & splashPathLast) &&+	  (pts[i-1].x != pts[curSubpath].x ||+	   pts[i-1].y != pts[curSubpath].y)) {+	addSegment(x0, y0, xsp, ysp,+		   gFalse, gTrue, gFalse, gFalse);+      }+    }+  }++  gfree(pts);+}++// Apply the stroke adjust hints to point <pt>: (*<xp>, *<yp>).+void SplashXPath::strokeAdjust(SplashXPathAdjust *adjust,+			       SplashCoord *xp, SplashCoord *yp) {+  SplashCoord x, y;++  if (adjust->vert) {+    x = *xp;+    if (x > adjust->x0a && x < adjust->x0b) {+      *xp = adjust->x0;+    } else if (x > adjust->xma && x < adjust->xmb) {+      *xp = adjust->xm;+    } else if (x > adjust->x1a && x < adjust->x1b) {+      *xp = adjust->x1;+    }+  } else {+    y = *yp;+    if (y > adjust->x0a && y < adjust->x0b) {+      *yp = adjust->x0;+    } else if (y > adjust->xma && y < adjust->xmb) {+      *yp = adjust->xm;+    } else if (y > adjust->x1a && y < adjust->x1b) {+      *yp = adjust->x1;+    }+  }+}++SplashXPath::SplashXPath(SplashXPath *xPath) {+  length = xPath->length;+  size = xPath->size;+  segs = (SplashXPathSeg *)gmallocn(size, sizeof(SplashXPathSeg));+  memcpy(segs, xPath->segs, length * sizeof(SplashXPathSeg));+}++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;++  flatness2 = flatness * flatness;++  // 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;+    dx = xx1 - mx;+    dy = yy1 - my;+    d1 = dx*dx + dy*dy;+    dx = xx2 - mx;+    dy = yy2 - my;+    d2 = dx*dx + dy*dy;++    // 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 == 0 && first,+		 p2 == splashMaxCurveSplits && last,+		 p1 == 0 && end0,+		 p2 == splashMaxCurveSplits && end1);+      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,+			     GBool first, GBool last, GBool end0, GBool end1) {+  grow(1);+  segs[length].x0 = x0;+  segs[length].y0 = y0;+  segs[length].x1 = x1;+  segs[length].y1 = y1;+  segs[length].flags = 0;+  if (first) {+    segs[length].flags |= splashXPathFirst;+  }+  if (last) {+    segs[length].flags |= splashXPathLast;+  }+  if (end0) {+    segs[length].flags |= splashXPathEnd0;+  }+  if (end1) {+    segs[length].flags |= splashXPathEnd1;+  }+  if (y1 == y0) {+    segs[length].dxdy = segs[length].dydx = 0;+    segs[length].flags |= splashXPathHoriz;+    if (x1 == x0) {+      segs[length].flags |= splashXPathVert;+    }+  } else if (x1 == x0) {+    segs[length].dxdy = segs[length].dydx = 0;+    segs[length].flags |= splashXPathVert;+  } else {+#if USE_FIXEDPOINT+    if (FixedPoint::divCheck(x1 - x0, y1 - y0, &segs[length].dxdy)) {+      segs[length].dydx = (SplashCoord)1 / segs[length].dxdy;+    } else {+      segs[length].dxdy = segs[length].dydx = 0;+      if (splashAbs(x1 - x0) > splashAbs(y1 - y0)) {+	segs[length].flags |= splashXPathHoriz;+      } else {+	segs[length].flags |= splashXPathVert;+      }+    }+#else+    segs[length].dxdy = (x1 - x0) / (y1 - y0);+    segs[length].dydx = (SplashCoord)1 / segs[length].dxdy;+#endif+  }+  if (y0 > y1) {+    segs[length].flags |= splashXPathFlip;+  }+  ++length;+}++static int cmpXPathSegs(const void *arg0, const void *arg1) {+  SplashXPathSeg *seg0 = (SplashXPathSeg *)arg0;+  SplashXPathSeg *seg1 = (SplashXPathSeg *)arg1;+  SplashCoord x0, y0, x1, y1;++  if (seg0->flags & splashXPathFlip) {+    x0 = seg0->x1;+    y0 = seg0->y1;+  } else {+    x0 = seg0->x0;+    y0 = seg0->y0;+  }+  if (seg1->flags & splashXPathFlip) {+    x1 = seg1->x1;+    y1 = seg1->y1;+  } else {+    x1 = seg1->x0;+    y1 = seg1->y0;+  }+  if (y0 != y1) {+    return (y0 > y1) ? 1 : -1;+  }+  if (x0 != x1) {+    return (x0 > x1) ? 1 : -1;+  }+  return 0;+}++void SplashXPath::aaScale() {+  SplashXPathSeg *seg;+  int i;++  for (i = 0, seg = segs; i < length; ++i, ++seg) {+    seg->x0 *= splashAASize;+    seg->y0 *= splashAASize;+    seg->x1 *= splashAASize;+    seg->y1 *= splashAASize;+  }+}++void SplashXPath::sort() {+  qsort(segs, length, sizeof(SplashXPathSeg), &cmpXPathSegs);+}
+ xpdf/SplashXPath.h view
@@ -0,0 +1,100 @@+//========================================================================+//+// SplashXPath.h+//+//========================================================================++#ifndef SPLASHXPATH_H+#define SPLASHXPATH_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashTypes.h"++class SplashPath;+struct SplashXPathAdjust;++//------------------------------------------------------------------------++#define splashMaxCurveSplits (1 << 10)++//------------------------------------------------------------------------+// SplashXPathSeg+//------------------------------------------------------------------------++struct SplashXPathSeg {+  SplashCoord x0, y0;		// first endpoint+  SplashCoord x1, y1;		// second endpoint+  SplashCoord dxdy;		// slope: delta-x / delta-y+  SplashCoord dydx;		// slope: delta-y / delta-x+  Guint flags;+};++#define splashXPathFirst   0x01	// first segment of a subpath+#define splashXPathLast    0x02	// last segment of a subpath+#define splashXPathEnd0    0x04	// first endpoint is end of an open subpath+#define splashXPathEnd1    0x08 // second endpoint is end of an open subpath+#define splashXPathHoriz   0x10 // segment is vertical (y0 == y1)+				//   (dxdy is undef)+#define splashXPathVert    0x20 // segment is horizontal (x0 == x1)+				//   (dydx is undef)+#define splashXPathFlip	   0x40	// y0 > y1++//------------------------------------------------------------------------+// 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);++  // Copy an expanded path.+  SplashXPath *copy() { return new SplashXPath(this); }++  ~SplashXPath();++  // Multiply all coordinates by splashAASize, in preparation for+  // anti-aliased rendering.+  void aaScale();++  // Sort by upper coordinate (lower y), in y-major order.+  void sort();++private:++  SplashXPath();+  SplashXPath(SplashXPath *xPath);+  void transform(SplashCoord *matrix, SplashCoord xi, SplashCoord yi,+		 SplashCoord *xo, SplashCoord *yo);+  void strokeAdjust(SplashXPathAdjust *adjust,+		    SplashCoord *xp, SplashCoord *yp);+  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 addSegment(SplashCoord x0, SplashCoord y0,+		  SplashCoord x1, SplashCoord y1,+		  GBool first, GBool last, GBool end0, GBool end1);++  SplashXPathSeg *segs;+  int length, size;		// length and size of segs array++  friend class SplashXPathScanner;+  friend class SplashClip;+  friend class Splash;+};++#endif
+ xpdf/SplashXPathScanner.cc view
@@ -0,0 +1,428 @@+//========================================================================+//+// SplashXPathScanner.cc+//+//========================================================================++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma implementation+#endif++#include <stdlib.h>+#include <string.h>+#include "gmem.h"+#include "SplashMath.h"+#include "SplashXPath.h"+#include "SplashBitmap.h"+#include "SplashXPathScanner.h"++//------------------------------------------------------------------------++struct SplashIntersect {+  int x0, x1;			// intersection of segment with [y, y+1)+  int count;			// EO/NZWN counter increment+};++static int cmpIntersect(const void *p0, const void *p1) {+  return ((SplashIntersect *)p0)->x0 - ((SplashIntersect *)p1)->x0;+}++//------------------------------------------------------------------------+// SplashXPathScanner+//------------------------------------------------------------------------++SplashXPathScanner::SplashXPathScanner(SplashXPath *xPathA, GBool eoA) {+  SplashXPathSeg *seg;+  SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP;+  int i;++  xPath = xPathA;+  eo = eoA;++  // compute the bbox+  if (xPath->length == 0) {+    xMin = yMin = 1;+    xMax = yMax = 0;+  } else {+    seg = &xPath->segs[0];+    if (seg->x0 <= seg->x1) {+      xMinFP = seg->x0;+      xMaxFP = seg->x1;+    } else {+      xMinFP = seg->x1;+      xMaxFP = seg->x0;+    }+    if (seg->flags & splashXPathFlip) {+      yMinFP = seg->y1;+      yMaxFP = seg->y0;+    } else {+      yMinFP = seg->y0;+      yMaxFP = seg->y1;+    }+    for (i = 1; i < xPath->length; ++i) {+      seg = &xPath->segs[i];+      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->flags & splashXPathFlip) {+	if (seg->y0 > yMaxFP) {+	  yMaxFP = seg->y0;+	}+      } else {+	if (seg->y1 > yMaxFP) {+	  yMaxFP = seg->y1;+	}+      }+    }+    xMin = splashFloor(xMinFP);+    xMax = splashFloor(xMaxFP);+    yMin = splashFloor(yMinFP);+    yMax = splashFloor(yMaxFP);+  }++  interY = yMin - 1;+  xPathIdx = 0;+  inter = NULL;+  interLen = interSize = 0;+}++SplashXPathScanner::~SplashXPathScanner() {+  gfree(inter);+}++void SplashXPathScanner::getBBoxAA(int *xMinA, int *yMinA,+				   int *xMaxA, int *yMaxA) {+  *xMinA = xMin / splashAASize;+  *yMinA = yMin / splashAASize;+  *xMaxA = xMax / splashAASize;+  *yMaxA = yMax / splashAASize;+}++void SplashXPathScanner::getSpanBounds(int y, int *spanXMin, int *spanXMax) {+  if (interY != y) {+    computeIntersections(y);+  }+  if (interLen > 0) {+    *spanXMin = inter[0].x0;+    *spanXMax = inter[interLen - 1].x1;+  } else {+    *spanXMin = xMax + 1;+    *spanXMax = xMax;+  }+}++GBool SplashXPathScanner::test(int x, int y) {+  int count, i;++  if (interY != y) {+    computeIntersections(y);+  }+  count = 0;+  for (i = 0; i < interLen && inter[i].x0 <= x; ++i) {+    if (x <= inter[i].x1) {+      return gTrue;+    }+    count += inter[i].count;+  }+  return eo ? (count & 1) : (count != 0);+}++GBool SplashXPathScanner::testSpan(int x0, int x1, int y) {+  int count, xx1, i;++  if (interY != y) {+    computeIntersections(y);+  }++  count = 0;+  for (i = 0; i < interLen && inter[i].x1 < x0; ++i) {+    count += inter[i].count;+  }++  // invariant: the subspan [x0,xx1] is inside the path+  xx1 = x0 - 1;+  while (xx1 < x1) {+    if (i >= interLen) {+      return gFalse;+    }+    if (inter[i].x0 > xx1 + 1 &&+	!(eo ? (count & 1) : (count != 0))) {+      return gFalse;+    }+    if (inter[i].x1 > xx1) {+      xx1 = inter[i].x1;+    }+    count += inter[i].count;+    ++i;+  }++  return gTrue;+}++GBool SplashXPathScanner::getNextSpan(int y, int *x0, int *x1) {+  int xx0, xx1;++  if (interY != y) {+    computeIntersections(y);+  }+  if (interIdx >= interLen) {+    return gFalse;+  }+  xx0 = inter[interIdx].x0;+  xx1 = inter[interIdx].x1;+  interCount += inter[interIdx].count;+  ++interIdx;+  while (interIdx < interLen &&+	 (inter[interIdx].x0 <= xx1 ||+	  (eo ? (interCount & 1) : (interCount != 0)))) {+    if (inter[interIdx].x1 > xx1) {+      xx1 = inter[interIdx].x1;+    }+    interCount += inter[interIdx].count;+    ++interIdx;+  }+  *x0 = xx0;+  *x1 = xx1;+  return gTrue;+}++void SplashXPathScanner::computeIntersections(int y) {+  SplashCoord xSegMin, xSegMax, ySegMin, ySegMax, xx0, xx1;+  SplashXPathSeg *seg;+  int i, j;++  // find the first segment that intersects [y, y+1)+  i = (y >= interY) ? xPathIdx : 0;+  while (i < xPath->length &&+	 xPath->segs[i].y0 < y && xPath->segs[i].y1 < y) {+    ++i;+  }+  xPathIdx = i;++  // find all of the segments that intersect [y, y+1) and create an+  // Intersect element for each one+  interLen = 0;+  for (j = i; j < xPath->length; ++j) {+    seg = &xPath->segs[j];+    if (seg->flags & splashXPathFlip) {+      ySegMin = seg->y1;+      ySegMax = seg->y0;+    } else {+      ySegMin = seg->y0;+      ySegMax = seg->y1;+    }++    // ensure that:      ySegMin < y+1+    //              y <= ySegMax+    if (ySegMin >= y + 1) {+      break;+    }+    if (ySegMax < y) {+      continue;+    }++    if (interLen == interSize) {+      if (interSize == 0) {+	interSize = 16;+      } else {+	interSize *= 2;+      }+      inter = (SplashIntersect *)greallocn(inter, interSize,+					   sizeof(SplashIntersect));+    }++    if (seg->flags & splashXPathHoriz) {+      xx0 = seg->x0;+      xx1 = seg->x1;+    } else if (seg->flags & splashXPathVert) {+      xx0 = xx1 = seg->x0;+    } else {+      if (seg->x0 < seg->x1) {+	xSegMin = seg->x0;+	xSegMax = seg->x1;+      } else {+	xSegMin = seg->x1;+	xSegMax = seg->x0;+      }+      // intersection with top edge+      xx0 = seg->x0 + ((SplashCoord)y - seg->y0) * seg->dxdy;+      // intersection with bottom edge+      xx1 = seg->x0 + ((SplashCoord)y + 1 - seg->y0) * seg->dxdy;+      // the segment may not actually extend to the top and/or bottom edges+      if (xx0 < xSegMin) {+	xx0 = xSegMin;+      } else if (xx0 > xSegMax) {+	xx0 = xSegMax;+      }+      if (xx1 < xSegMin) {+	xx1 = xSegMin;+      } else if (xx1 > xSegMax) {+	xx1 = xSegMax;+      }+    }+    if (xx0 < xx1) {+      inter[interLen].x0 = splashFloor(xx0);+      inter[interLen].x1 = splashFloor(xx1);+    } else {+      inter[interLen].x0 = splashFloor(xx1);+      inter[interLen].x1 = splashFloor(xx0);+    }+    if (ySegMin <= y &&+	(SplashCoord)y < ySegMax &&+	!(seg->flags & splashXPathHoriz)) {+      inter[interLen].count = eo ? 1+	                         : (seg->flags & splashXPathFlip) ? 1 : -1;+    } else {+      inter[interLen].count = 0;+    }+    ++interLen;+  }++  qsort(inter, interLen, sizeof(SplashIntersect), &cmpIntersect);++  interY = y;+  interIdx = 0;+  interCount = 0;+}++void SplashXPathScanner::renderAALine(SplashBitmap *aaBuf,+				      int *x0, int *x1, int y) {+  int xx0, xx1, xx, xxMin, xxMax, yy;+  Guchar mask;+  SplashColorPtr p;++  memset(aaBuf->getDataPtr(), 0, aaBuf->getRowSize() * aaBuf->getHeight());+  xxMin = aaBuf->getWidth();+  xxMax = -1;+  for (yy = 0; yy < splashAASize; ++yy) {+    computeIntersections(splashAASize * y + yy);+    while (interIdx < interLen) {+      xx0 = inter[interIdx].x0;+      xx1 = inter[interIdx].x1;+      interCount += inter[interIdx].count;+      ++interIdx;+      while (interIdx < interLen &&+	     (inter[interIdx].x0 <= xx1 ||+	      (eo ? (interCount & 1) : (interCount != 0)))) {+	if (inter[interIdx].x1 > xx1) {+	  xx1 = inter[interIdx].x1;+	}+	interCount += inter[interIdx].count;+	++interIdx;+      }+      if (xx0 < 0) {+	xx0 = 0;+      }+      ++xx1;+      if (xx1 > aaBuf->getWidth()) {+	xx1 = aaBuf->getWidth();+      }+      // set [xx0, xx1) to 1+      if (xx0 < xx1) {+	xx = xx0;+	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3);+	if (xx & 7) {+	  mask = 0xff >> (xx & 7);+	  if ((xx & ~7) == (xx1 & ~7)) {+	    mask &= (Guchar)(0xff00 >> (xx1 & 7));+	  }+	  *p++ |= mask;+	  xx = (xx & ~7) + 8;+	}+	for (; xx + 7 < xx1; xx += 8) {+	  *p++ |= 0xff;+	}+	if (xx < xx1) {+	  *p |= (Guchar)(0xff00 >> (xx1 & 7));+	}+      }+      if (xx0 < xxMin) {+	xxMin = xx0;+      }+      if (xx1 > xxMax) {+	xxMax = xx1;+      }+    }+  }+  *x0 = xxMin / splashAASize;+  *x1 = (xxMax - 1) / splashAASize;+}++void SplashXPathScanner::clipAALine(SplashBitmap *aaBuf,+				    int *x0, int *x1, int y) {+  int xx0, xx1, xx, yy;+  Guchar mask;+  SplashColorPtr p;++  for (yy = 0; yy < splashAASize; ++yy) {+    xx = *x0 * splashAASize;+    computeIntersections(splashAASize * y + yy);+    while (interIdx < interLen && xx < (*x1 + 1) * splashAASize) {+      xx0 = inter[interIdx].x0;+      xx1 = inter[interIdx].x1;+      interCount += inter[interIdx].count;+      ++interIdx;+      while (interIdx < interLen &&+	     (inter[interIdx].x0 <= xx1 ||+	      (eo ? (interCount & 1) : (interCount != 0)))) {+	if (inter[interIdx].x1 > xx1) {+	  xx1 = inter[interIdx].x1;+	}+	interCount += inter[interIdx].count;+	++interIdx;+      }+      if (xx0 > aaBuf->getWidth()) {+	xx0 = aaBuf->getWidth();+      }+      // set [xx, xx0) to 0+      if (xx < xx0) {+	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3);+	if (xx & 7) {+	  mask = (Guchar)(0xff00 >> (xx & 7));+	  if ((xx & ~7) == (xx0 & ~7)) {+	    mask |= 0xff >> (xx0 & 7);+	  }+	  *p++ &= mask;+	  xx = (xx & ~7) + 8;+	}+	for (; xx + 7 <= xx0; xx += 8) {+	  *p++ = 0x00;+	}+	if (xx <= xx0) {+	  *p &= 0xff >> (xx0 & 7);+	}+      }+      if (xx1 >= xx) {+	xx = xx1 + 1;+      }+    }+    xx0 = (*x1 + 1) * splashAASize;+    // set [xx, xx0) to 0+    if (xx < xx0) {+      p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() + (xx >> 3);+      if (xx & 7) {+	mask = (Guchar)(0xff00 >> (xx & 7));+	if ((xx & ~7) == (xx0 & ~7)) {+	  mask &= 0xff >> (xx0 & 7);+	}+	*p++ &= mask;+	xx = (xx & ~7) + 8;+      }+      for (; xx + 7 <= xx0; xx += 8) {+	*p++ = 0x00;+      }+      if (xx <= xx0) {+	*p &= 0xff >> (xx0 & 7);+      }+    }+  }+}
+ xpdf/SplashXPathScanner.h view
@@ -0,0 +1,87 @@+//========================================================================+//+// SplashXPathScanner.h+//+//========================================================================++#ifndef SPLASHXPATHSCANNER_H+#define SPLASHXPATHSCANNER_H++#include <aconf.h>++#ifdef USE_GCC_PRAGMAS+#pragma interface+#endif++#include "SplashTypes.h"++class SplashXPath;+class SplashBitmap;+struct SplashIntersect;++//------------------------------------------------------------------------+// SplashXPathScanner+//------------------------------------------------------------------------++class SplashXPathScanner {+public:++  // Create a new SplashXPathScanner object.  <xPathA> must be sorted.+  SplashXPathScanner(SplashXPath *xPathA, GBool eoA);++  ~SplashXPathScanner();++  // Return the path's bounding box.+  void getBBox(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA)+    { *xMinA = xMin; *yMinA = yMin; *xMaxA = xMax; *yMaxA = yMax; }++  // Return the path's bounding box.+  void getBBoxAA(int *xMinA, int *yMinA, int *xMaxA, int *yMaxA);++  // Return the min/max x values for the span at <y>.+  void getSpanBounds(int y, int *spanXMin, int *spanXMax);++  // Returns true if (<x>,<y>) is inside the path.+  GBool test(int x, int y);++  // Returns true if the entire span ([<x0>,<x1>], <y>) is inside the+  // path.+  GBool testSpan(int x0, int x1, int y);++  // Returns the next span inside the path at <y>.  If <y> is+  // different than the previous call to getNextSpan, this returns the+  // first span at <y>; otherwise it returns the next span (relative+  // to the previous call to getNextSpan).  Returns false if there are+  // no more spans at <y>.+  GBool getNextSpan(int y, int *x0, int *x1);++  // Renders one anti-aliased line into <aaBuf>.  Returns the min and+  // max x coordinates with non-zero pixels in <x0> and <x1>.+  void renderAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y);++  // Clips an anti-aliased line by setting pixels to zero.  On entry,+  // all non-zero pixels are between <x0> and <x1>.  This function+  // will update <x0> and <x1>.+  void clipAALine(SplashBitmap *aaBuf, int *x0, int *x1, int y);++private:++  void computeIntersections(int y);++  SplashXPath *xPath;+  GBool eo;+  int xMin, yMin, xMax, yMax;++  int interY;			// current y value+  int interIdx;			// current index into <inter> - used by+				//   getNextSpan +  int interCount;		// current EO/NZWN counter - used by+				//   getNextSpan+  int xPathIdx;			// current index into <xPath> - used by+				//   computeIntersections+  SplashIntersect *inter;	// intersections array for <interY>+  int interLen;			// number of intersections in <inter>+  int interSize;		// size of the <inter> array+};++#endif
+ xpdf/Stream-CCITT.h view
@@ -0,0 +1,459 @@+//========================================================================+//+// Stream-CCITT.h+//+// Tables for CCITT Fax decoding.+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++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}+};
+ xpdf/Stream.cc view
@@ -0,0 +1,4686 @@+//========================================================================+//+// 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>+#ifndef WIN32+#include <unistd.h>+#endif+#include <string.h>+#include <ctype.h>+#include "gmem.h"+#include "gfile.h"+#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() {+  ref = 1;+}++Stream::~Stream() {+}++void Stream::close() {+}++int Stream::getRawChar() {+  error(-1, "Internal: called getRawChar() on non-predictor stream");+  return EOF;+}++char *Stream::getLine(char *buf, int size) {+  int i;+  int c;++  if (lookChar() == EOF)+    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] = c;+  }+  buf[i] = '\0';+  return buf;+}++GString *Stream::getPSFilter(int psLevel, char *indent) {+  return new GString();+}++Stream *Stream::addFilters(Object *dict) {+  Object obj, obj2;+  Object params, params2;+  Stream *str;+  int i;++  str = this;+  dict->dictLookup("Filter", &obj);+  if (obj.isNull()) {+    obj.free();+    dict->dictLookup("F", &obj);+  }+  dict->dictLookup("DecodeParms", &params);+  if (params.isNull()) {+    params.free();+    dict->dictLookup("DP", &params);+  }+  if (obj.isName()) {+    str = makeFilter(obj.getName(), str, &params);+  } else if (obj.isArray()) {+    for (i = 0; i < obj.arrayGetLength(); ++i) {+      obj.arrayGet(i, &obj2);+      if (params.isArray())+	params.arrayGet(i, &params2);+      else+	params2.initNull();+      if (obj2.isName()) {+	str = makeFilter(obj2.getName(), str, &params2);+      } else {+	error(getPos(), "Bad filter name");+	str = new EOFStream(str);+      }+      obj2.free();+      params2.free();+    }+  } else if (!obj.isNull()) {+    error(getPos(), "Bad 'Filter' attribute in stream");+  }+  obj.free();+  params.free();++  return str;+}++Stream *Stream::makeFilter(char *name, Stream *str, Object *params) {+  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);+      if (obj.isInt())+	pred = obj.getInt();+      obj.free();+      params->dictLookup("Columns", &obj);+      if (obj.isInt())+	columns = obj.getInt();+      obj.free();+      params->dictLookup("Colors", &obj);+      if (obj.isInt())+	colors = obj.getInt();+      obj.free();+      params->dictLookup("BitsPerComponent", &obj);+      if (obj.isInt())+	bits = obj.getInt();+      obj.free();+      params->dictLookup("EarlyChange", &obj);+      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);+      if (obj.isInt()) {+	encoding = obj.getInt();+      }+      obj.free();+      params->dictLookup("EndOfLine", &obj);+      if (obj.isBool()) {+	endOfLine = obj.getBool();+      }+      obj.free();+      params->dictLookup("EncodedByteAlign", &obj);+      if (obj.isBool()) {+	byteAlign = obj.getBool();+      }+      obj.free();+      params->dictLookup("Columns", &obj);+      if (obj.isInt()) {+	columns = obj.getInt();+      }+      obj.free();+      params->dictLookup("Rows", &obj);+      if (obj.isInt()) {+	rows = obj.getInt();+      }+      obj.free();+      params->dictLookup("EndOfBlock", &obj);+      if (obj.isBool()) {+	endOfBlock = obj.getBool();+      }+      obj.free();+      params->dictLookup("BlackIs1", &obj);+      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)->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);+      if (obj.isInt())+	pred = obj.getInt();+      obj.free();+      params->dictLookup("Columns", &obj);+      if (obj.isInt())+	columns = obj.getInt();+      obj.free();+      params->dictLookup("Colors", &obj);+      if (obj.isInt())+	colors = obj.getInt();+      obj.free();+      params->dictLookup("BitsPerComponent", &obj);+      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);+    }+    str = new JBIG2Stream(str, &globals);+    globals.free();+  } else if (!strcmp(name, "JPXDecode")) {+    str = new JPXStream(str);+  } else {+    error(getPos(), "Unknown filter '%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(Guint pos, int dir) {+  error(-1, "Internal: 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;+  if (nBits == 1) {+    imgLineSize = (nVals + 7) & ~7;+  } else {+    imgLineSize = nVals;+  }+  imgLine = (Guchar *)gmallocn(imgLineSize, sizeof(Guchar));+  imgIdx = nVals;+}++ImageStream::~ImageStream() {+  gfree(imgLine);+}++void ImageStream::reset() {+  str->reset();+}++GBool ImageStream::getPixel(Guchar *pix) {+  int i;++  if (imgIdx >= nVals) {+    getLine();+    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;++  if (nBits == 1) {+    for (i = 0; i < nVals; i += 8) {+      c = str->getChar();+      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) {+    for (i = 0; i < nVals; ++i) {+      imgLine[i] = str->getChar();+    }+  } else {+    bitMask = (1 << nBits) - 1;+    buf = 0;+    bits = 0;+    for (i = 0; i < nVals; ++i) {+      if (bits < nBits) {+	buf = (buf << 8) | (str->getChar() & 0xff);+	bits += 8;+      }+      imgLine[i] = (Guchar)((buf >> (bits - nBits)) & bitMask);+      bits -= nBits;+    }+  }+  return imgLine;+}++void ImageStream::skipLine() {+  int n, i;++  n = (nVals * nBits + 7) >> 3;+  for (i = 0; i < n; ++i) {+    str->getChar();+  }+}++//------------------------------------------------------------------------+// 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);+  memset(predLine, 0, rowBytes);+  predIdx = rowBytes;++  ok = gTrue;+}++StreamPredictor::~StreamPredictor() {+  gfree(predLine);+}++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++];+}++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] = predLine[i - pixBytes] + (Guchar)c;+      break;+    case 12:			// PNG up+      predLine[i] = predLine[i] + (Guchar)c;+      break;+    case 13:			// PNG average+      predLine[i] = ((predLine[i - pixBytes] + predLine[i]) >> 1) ++	            (Guchar)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] = left + (Guchar)c;+      else if (pb <= pc)+	predLine[i] = up + (Guchar)c;+      else+	predLine[i] = upLeft + (Guchar)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 == 1) {+      inBuf = predLine[pixBytes - 1];+      for (i = pixBytes; i < rowBytes; i += 8) {+	// 1-bit add is just xor+	inBuf = (inBuf << 8) | predLine[i];+	predLine[i] ^= inBuf >> nComps;+      }+    } else if (nBits == 8) {+      for (i = pixBytes; i < rowBytes; ++i) {+	predLine[i] += predLine[i - nComps];+      }+    } else {+      memset(upLeftBuf, 0, nComps + 1);+      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;+}++//------------------------------------------------------------------------+// FileStream+//------------------------------------------------------------------------++FileStream::FileStream(FILE *fA, Guint startA, GBool limitedA,+		       Guint lengthA, Object *dictA):+    BaseStream(dictA) {+  f = fA;+  start = startA;+  limited = limitedA;+  length = lengthA;+  bufPtr = bufEnd = buf;+  bufPos = start;+  savePos = 0;+  saved = gFalse;+}++FileStream::~FileStream() {+  close();+}++Stream *FileStream::makeSubStream(Guint startA, GBool limitedA,+				  Guint lengthA, Object *dictA) {+  return new FileStream(f, startA, limitedA, lengthA, dictA);+}++void FileStream::reset() {+#if HAVE_FSEEKO+  savePos = (Guint)ftello(f);+  fseeko(f, start, SEEK_SET);+#elif HAVE_FSEEK64+  savePos = (Guint)ftell64(f);+  fseek64(f, start, SEEK_SET);+#else+  savePos = (Guint)ftell(f);+  fseek(f, start, SEEK_SET);+#endif+  saved = gTrue;+  bufPtr = bufEnd = buf;+  bufPos = start;+}++void FileStream::close() {+  if (saved) {+#if HAVE_FSEEKO+    fseeko(f, savePos, SEEK_SET);+#elif HAVE_FSEEK64+    fseek64(f, savePos, SEEK_SET);+#else+    fseek(f, savePos, SEEK_SET);+#endif+    saved = gFalse;+  }+}++GBool FileStream::fillBuf() {+  int n;++  bufPos += bufEnd - buf;+  bufPtr = bufEnd = buf;+  if (limited && bufPos >= start + length) {+    return gFalse;+  }+  if (limited && bufPos + fileStreamBufSize > start + length) {+    n = start + length - bufPos;+  } else {+    n = fileStreamBufSize;+  }+  n = fread(buf, 1, n, f);+  bufEnd = buf + n;+  if (bufPtr >= bufEnd) {+    return gFalse;+  }+  return gTrue;+}++void FileStream::setPos(Guint pos, int dir) {+  Guint size;++  if (dir >= 0) {+#if HAVE_FSEEKO+    fseeko(f, pos, SEEK_SET);+#elif HAVE_FSEEK64+    fseek64(f, pos, SEEK_SET);+#else+    fseek(f, pos, SEEK_SET);+#endif+    bufPos = pos;+  } else {+#if HAVE_FSEEKO+    fseeko(f, 0, SEEK_END);+    size = (Guint)ftello(f);+#elif HAVE_FSEEK64+    fseek64(f, 0, SEEK_END);+    size = (Guint)ftell64(f);+#else+    fseek(f, 0, SEEK_END);+    size = (Guint)ftell(f);+#endif+    if (pos > size)+      pos = (Guint)size;+#ifdef __CYGWIN32__+    //~ work around a bug in cygwin's implementation of fseek+    rewind(f);+#endif+#if HAVE_FSEEKO+    fseeko(f, -(int)pos, SEEK_END);+    bufPos = (Guint)ftello(f);+#elif HAVE_FSEEK64+    fseek64(f, -(int)pos, SEEK_END);+    bufPos = (Guint)ftell64(f);+#else+    fseek(f, -(int)pos, SEEK_END);+    bufPos = (Guint)ftell(f);+#endif+  }+  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::makeSubStream(Guint startA, GBool limited,+				 Guint lengthA, Object *dictA) {+  MemStream *subStr;+  Guint newLength;++  if (!limited || startA + lengthA > start + length) {+    newLength = start + length - startA;+  } else {+    newLength = lengthA;+  }+  subStr = new MemStream(buf, startA, newLength, dictA);+  return subStr;+}++void MemStream::reset() {+  bufPtr = buf + start;+}++void MemStream::close() {+}++void MemStream::setPos(Guint pos, int dir) {+  Guint i;++  if (dir >= 0) {+    i = pos;+  } else {+    i = 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, Guint lengthA):+    BaseStream(dictA) {+  str = strA;+  limited = limitedA;+  length = lengthA;+}++EmbedStream::~EmbedStream() {+}++Stream *EmbedStream::makeSubStream(Guint start, GBool limitedA,+				   Guint lengthA, Object *dictA) {+  error(-1, "Internal: 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();+}++void EmbedStream::setPos(Guint pos, int dir) {+  error(-1, "Internal: called setPos() on EmbedStream");+}++Guint EmbedStream::getStart() {+  error(-1, "Internal: called getStart() on EmbedStream");+  return 0;+}++void EmbedStream::moveStart(int delta) {+  error(-1, "Internal: called moveStart() on EmbedStream");+}++//------------------------------------------------------------------------+// ASCIIHexStream+//------------------------------------------------------------------------++ASCIIHexStream::ASCIIHexStream(Stream *strA):+    FilterStream(strA) {+  buf = EOF;+  eof = gFalse;+}++ASCIIHexStream::~ASCIIHexStream() {+  delete str;+}++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(getPos(), "Illegal character <%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(getPos(), "Illegal character <%02x> in ASCIIHex stream", c2);+  }+  buf = x & 0xff;+  return buf;+}++GString *ASCIIHexStream::getPSFilter(int psLevel, char *indent) {+  GString *s;++  if (psLevel < 2) {+    return NULL;+  }+  if (!(s = str->getPSFilter(psLevel, indent))) {+    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;+}++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, char *indent) {+  GString *s;++  if (psLevel < 2) {+    return NULL;+  }+  if (!(s = str->getPSFilter(psLevel, indent))) {+    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;+}++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++];+}++void LZWStream::reset() {+  str->reset();+  eof = gFalse;+  inputBits = 0;+  clearTable();+}++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(getPos(), "Bad LZW stream - expected clear-table code");+    clearTable();+  }++  // process the next code+  nextLength = seqLength + 1;+  if (code < 256) {+    seqBuf[0] = 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] = j;+  } else if (code == nextCode) {+    seqBuf[seqLength] = newChar;+    ++seqLength;+  } else {+    error(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 = newChar;+    ++nextCode;+    if (nextCode + early == 512)+      nextBits = 10;+    else if (nextCode + early == 1024)+      nextBits = 11;+    else if (nextCode + early == 2048)+      nextBits = 12;+  }+  prevCode = code;++  // 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;+  }+  code = (inputBuf >> (inputBits - nextBits)) & ((1 << nextBits) - 1);+  inputBits -= nextBits;+  return code;+}++GString *LZWStream::getPSFilter(int psLevel, char *indent) {+  GString *s;++  if (psLevel < 2 || pred) {+    return NULL;+  }+  if (!(s = str->getPSFilter(psLevel, indent))) {+    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;+}++void RunLengthStream::reset() {+  str->reset();+  bufPtr = bufEnd = buf;+  eof = gFalse;+}++GString *RunLengthStream::getPSFilter(int psLevel, char *indent) {+  GString *s;++  if (psLevel < 2) {+    return NULL;+  }+  if (!(s = str->getPSFilter(psLevel, indent))) {+    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 - 2) {+    columns = INT_MAX - 2;+  }+  rows = rowsA;+  endOfBlock = endOfBlockA;+  black = blackA;+  // 0 <= codingLine[0] < codingLine[1] < ... < codingLine[n] = columns+  // ---> max codingLine size = columns + 1+  // refLine has one extra guard entry at the end+  // ---> max refLine size = columns + 2+  codingLine = (int *)gmallocn(columns + 1, sizeof(int));+  refLine = (int *)gmallocn(columns + 2, sizeof(int));++  eof = gFalse;+  row = 0;+  nextLine2D = encoding < 0;+  inputBits = 0;+  codingLine[0] = columns;+  a0i = 0;+  outputBits = 0;++  buf = EOF;+}++CCITTFaxStream::~CCITTFaxStream() {+  delete str;+  gfree(refLine);+  gfree(codingLine);+}++void CCITTFaxStream::reset() {+  short code1;++  str->reset();+  eof = gFalse;+  row = 0;+  nextLine2D = encoding < 0;+  inputBits = 0;+  codingLine[0] = columns;+  a0i = 0;+  outputBits = 0;+  buf = EOF;++  // 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);+  }+  if (encoding > 0) {+    nextLine2D = !lookBits(1);+    eatBits(1);+  }+}++inline void CCITTFaxStream::addPixels(int a1, int blackPixels) {+  if (a1 > codingLine[a0i]) {+    if (a1 > columns) {+      error(getPos(), "CCITTFax row is wrong length (%d)", a1);+      err = gTrue;+      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(getPos(), "CCITTFax row is wrong length (%d)", a1);+      err = gTrue;+      a1 = columns;+    }+    if ((a0i & 1) ^ blackPixels) {+      ++a0i;+    }+    codingLine[a0i] = a1;+  } else if (a1 < codingLine[a0i]) {+    if (a1 < 0) {+      error(getPos(), "Invalid CCITTFax code");+      err = gTrue;+      a1 = 0;+    }+    while (a0i > 0 && a1 <= codingLine[a0i - 1]) {+      --a0i;+    }+    codingLine[a0i] = a1;+  }+}++int CCITTFaxStream::lookChar() {+  short code1, code2, code3;+  int b1i, blackPixels, i, bits;+  GBool gotEOL;++  if (buf != EOF) {+    return buf;+  }++  // read the next row+  if (outputBits == 0) {++    // if at eof just return EOF+    if (eof) {+      return EOF;+    }++    err = gFalse;++    // 2-D encoding+    if (nextLine2D) {+      for (i = 0; codingLine[i] < columns; ++i) {+	refLine[i] = codingLine[i];+      }+      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);+	  eof = gTrue;+	  break;+	default:+	  error(getPos(), "Bad 2D code %04x in CCITTFax stream", code1);+	  addPixels(columns, 0);+	  err = gTrue;+	  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;+      }+    }++    // byte-align the row+    if (byteAlign) {+      inputBits &= ~7;+    }++    // check for end-of-line marker, skipping over any extra zero bits+    gotEOL = gFalse;+    if (!endOfBlock && row == rows - 1) {+      eof = gTrue;+    } else {+      code1 = lookBits(12);+      while (code1 == 0) {+	eatBits(1);+	code1 = lookBits(12);+      }+      if (code1 == 0x001) {+	eatBits(12);+	gotEOL = gTrue;+      } else if (code1 == EOF) {+	eof = gTrue;+      }+    }++    // get 2D encoding tag+    if (!eof && encoding > 0) {+      nextLine2D = !lookBits(1);+      eatBits(1);+    }++    // check for end-of-block marker+    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(getPos(), "Bad RTC code in CCITTFax stream");+	    }+	    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 EOF;+	}+	if ((code1 >> 1) == 0x001) {+	  break;+	}+	eatBits(1);+      }+      eatBits(12); +      if (encoding > 0) {+	eatBits(1);+	nextLine2D = !(code1 & 1);+      }+    }++    // set up for output+    if (codingLine[0] > 0) {+      outputBits = codingLine[a0i = 0];+    } else {+      outputBits = codingLine[a0i = 1];+    }++    ++row;+  }++  // get a byte+  if (outputBits >= 8) {+    buf = (a0i & 1) ? 0x00 : 0xff;+    outputBits -= 8;+    if (outputBits == 0 && codingLine[a0i] < columns) {+      ++a0i;+      outputBits = codingLine[a0i] - codingLine[a0i - 1];+    }+  } else {+    bits = 8;+    buf = 0;+    do {+      if (outputBits > bits) {+	buf <<= bits;+	if (!(a0i & 1)) {+	  buf |= 0xff >> (8 - bits);+	}+	outputBits -= bits;+	bits = 0;+      } else {+	buf <<= outputBits;+	if (!(a0i & 1)) {+	  buf |= 0xff >> (8 - outputBits);+	}+	bits -= outputBits;+	outputBits = 0;+	if (codingLine[a0i] < columns) {+	  ++a0i;+	  outputBits = codingLine[a0i] - codingLine[a0i - 1];+	} else if (bits > 0) {+	  buf <<= bits;+	  bits = 0;+	}+      }+    } while (bits);+  }+  if (black) {+    buf ^= 0xff;+  }+  return buf;+}++short CCITTFaxStream::getTwoDimCode() {+  short code;+  CCITTCode *p;+  int n;++  code = 0; // make gcc happy+  if (endOfBlock) {+    code = lookBits(7);+    p = &twoDimTab1[code];+    if (p->bits > 0) {+      eatBits(p->bits);+      return p->n;+    }+  } else {+    for (n = 1; n <= 7; ++n) {+      code = lookBits(n);+      if (n < 7) {+	code <<= 7 - n;+      }+      p = &twoDimTab1[code];+      if (p->bits == n) {+	eatBits(n);+	return p->n;+      }+    }+  }+  error(getPos(), "Bad two dim code (%04x) in CCITTFax stream", code);+  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 <<= 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 <<= 12 - n;+      }+      p = &whiteTab1[code];+      if (p->bits == n) {+	eatBits(n);+	return p->n;+      }+    }+  }+  error(getPos(), "Bad white code (%04x) in CCITTFax stream", code);+  // 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 <<= 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 <<= 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 <<= 13 - n;+      }+      p = &blackTab1[code];+      if (p->bits == n) {+	eatBits(n);+	return p->n;+      }+    }+  }+  error(getPos(), "Bad black code (%04x) in CCITTFax stream", code);+  // 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 (inputBuf << (n - inputBits)) & (0xffff >> (16 - n));+    }+    inputBuf = (inputBuf << 8) + c;+    inputBits += 8;+  }+  return (inputBuf >> (inputBits - n)) & (0xffff >> (16 - n));+}++GString *CCITTFaxStream::getPSFilter(int psLevel, char *indent) {+  GString *s;+  char s1[50];++  if (psLevel < 2) {+    return NULL;+  }+  if (!(s = str->getPSFilter(psLevel, indent))) {+    return NULL;+  }+  s->append(indent)->append("<< ");+  if (encoding != 0) {+    sprintf(s1, "/K %d ", encoding);+    s->append(s1);+  }+  if (endOfLine) {+    s->append("/EndOfLine true ");+  }+  if (byteAlign) {+    s->append("/EncodedByteAlign true ");+  }+  sprintf(s1, "/Columns %d ", columns);+  s->append(s1);+  if (rows != 0) {+    sprintf(s1, "/Rows %d ", rows);+    s->append(s1);+  }+  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+//------------------------------------------------------------------------++// IDCT constants (20.12 fixed point format)+#define dctCos1    4017		// cos(pi/16)+#define dctSin1     799		// sin(pi/16)+#define dctCos3    3406		// cos(3*pi/16)+#define dctSin3    2276		// sin(3*pi/16)+#define dctCos6    1567		// cos(6*pi/16)+#define dctSin6    3784		// sin(6*pi/16)+#define dctSqrt2   5793		// sqrt(2)+#define dctSqrt1d2 2896		// sqrt(2) / 2++// 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++// clip [-256,511] --> [0,255]+#define dctClipOffset 256+static Guchar dctClip[768];+static int dctClipInit = 0;++// 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, j;++  colorXform = colorXformA;+  progressive = interleaved = gFalse;+  width = height = 0;+  mcuWidth = mcuHeight = 0;+  numComps = 0;+  comp = 0;+  x = y = dy = 0;+  for (i = 0; i < 4; ++i) {+    for (j = 0; j < 32; ++j) {+      rowBuf[i][j] = NULL;+    }+    frameBuf[i] = NULL;+  }++  if (!dctClipInit) {+    for (i = -256; i < 0; ++i)+      dctClip[dctClipOffset + i] = 0;+    for (i = 0; i < 256; ++i)+      dctClip[dctClipOffset + i] = i;+    for (i = 256; i < 512; ++i)+      dctClip[dctClipOffset + i] = 255;+    dctClipInit = 1;+  }+}++DCTStream::~DCTStream() {+  close();+  delete str;+}++void DCTStream::reset() {+  int i, j;++  str->reset();++  progressive = interleaved = gFalse;+  width = height = 0;+  numComps = 0;+  numQuantTables = 0;+  numDCHuffTables = 0;+  numACHuffTables = 0;+  gotJFIFMarker = gFalse;+  gotAdobeMarker = gFalse;+  restartInterval = 0;++  if (!readHeader()) {+    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(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());++    // decode+    decodeImage();++    // initialize counters+    comp = 0;+    x = 0;+    y = 0;++  } else {++    // allocate a buffer for one row of MCUs+    bufWidth = ((width + mcuWidth - 1) / mcuWidth) * mcuWidth;+    for (i = 0; i < numComps; ++i) {+      for (j = 0; j < mcuHeight; ++j) {+	rowBuf[i][j] = (Guchar *)gmallocn(bufWidth, sizeof(Guchar));+      }+    }++    // initialize counters+    comp = 0;+    x = 0;+    y = 0;+    dy = mcuHeight;++    restartMarker = 0xd0;+    restart();+  }+}++void DCTStream::close() {+  int i, j;++  for (i = 0; i < 4; ++i) {+    for (j = 0; j < 32; ++j) {+      gfree(rowBuf[i][j]);+      rowBuf[i][j] = NULL;+    }+    gfree(frameBuf[i]);+    frameBuf[i] = NULL;+  }+  FilterStream::close();+}++int DCTStream::getChar() {+  int c;++  if (y >= height) {+    return EOF;+  }+  if (progressive || !interleaved) {+    c = frameBuf[comp][y * bufWidth + x];+    if (++comp == numComps) {+      comp = 0;+      if (++x == width) {+	x = 0;+	++y;+      }+    }+  } else {+    if (dy >= mcuHeight) {+      if (!readMCURow()) {+	y = height;+	return EOF;+      }+      comp = 0;+      x = 0;+      dy = 0;+    }+    c = rowBuf[comp][dy][x];+    if (++comp == numComps) {+      comp = 0;+      if (++x == width) {+	x = 0;+	++y;+	++dy;+	if (y == height) {+	  readTrailer();+	}+      }+    }+  }+  return c;+}++int DCTStream::lookChar() {+  if (y >= height) {+    return EOF;+  }+  if (progressive || !interleaved) {+    return frameBuf[comp][y * bufWidth + x];+  } else {+    if (dy >= mcuHeight) {+      if (!readMCURow()) {+	y = height;+	return EOF;+      }+      comp = 0;+      x = 0;+      dy = 0;+    }+    return rowBuf[comp][dy][x];+  }+}++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 (x1 = 0; x1 < width; x1 += mcuWidth) {++    // deal with restart marker+    if (restartInterval > 0 && restartCtr == 0) {+      c = readMarker();+      if (c != restartMarker) {+	error(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) {+	    for (y3 = 0, i = 0; y3 < 8; ++y3, i += 8) {+	      p1 = &rowBuf[cc][y2+y3][x1+x2];+	      p1[0] = data2[i];+	      p1[1] = data2[i+1];+	      p1[2] = data2[i+2];+	      p1[3] = data2[i+3];+	      p1[4] = data2[i+4];+	      p1[5] = data2[i+5];+	      p1[6] = data2[i+6];+	      p1[7] = data2[i+7];+	    }+	  } else if (hSub == 2 && vSub == 2) {+	    for (y3 = 0, i = 0; y3 < 16; y3 += 2, i += 8) {+	      p1 = &rowBuf[cc][y2+y3][x1+x2];+	      p2 = &rowBuf[cc][y2+y3+1][x1+x2];+	      p1[0] = p1[1] = p2[0] = p2[1] = data2[i];+	      p1[2] = p1[3] = p2[2] = p2[3] = data2[i+1];+	      p1[4] = p1[5] = p2[4] = p2[5] = data2[i+2];+	      p1[6] = p1[7] = p2[6] = p2[7] = data2[i+3];+	      p1[8] = p1[9] = p2[8] = p2[9] = data2[i+4];+	      p1[10] = p1[11] = p2[10] = p2[11] = data2[i+5];+	      p1[12] = p1[13] = p2[12] = p2[13] = data2[i+6];+	      p1[14] = p1[15] = p2[14] = p2[15] = data2[i+7];+	    }+	  } else {+	    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; ++x5)+		    rowBuf[cc][y2+y4+y5][x1+x2+x4+x5] = data2[i];+		++i;+	      }+	    }+	  }+	}+      }+    }+    --restartCtr;++    // color space conversion+    if (colorXform) {+      // convert YCbCr to RGB+      if (numComps == 3) {+	for (y2 = 0; y2 < mcuHeight; ++y2) {+	  for (x2 = 0; x2 < mcuWidth; ++x2) {+	    pY = rowBuf[0][y2][x1+x2];+	    pCb = rowBuf[1][y2][x1+x2] - 128;+	    pCr = rowBuf[2][y2][x1+x2] - 128;+	    pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;+	    rowBuf[0][y2][x1+x2] = dctClip[dctClipOffset + pR];+	    pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;+	    rowBuf[1][y2][x1+x2] = dctClip[dctClipOffset + pG];+	    pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;+	    rowBuf[2][y2][x1+x2] = dctClip[dctClipOffset + pB];+	  }+	}+      // convert YCbCrK to CMYK (K is passed through unchanged)+      } else if (numComps == 4) {+	for (y2 = 0; y2 < mcuHeight; ++y2) {+	  for (x2 = 0; x2 < mcuWidth; ++x2) {+	    pY = rowBuf[0][y2][x1+x2];+	    pCb = rowBuf[1][y2][x1+x2] - 128;+	    pCr = rowBuf[2][y2][x1+x2] - 128;+	    pR = ((pY << 16) + dctCrToR * pCr + 32768) >> 16;+	    rowBuf[0][y2][x1+x2] = 255 - dctClip[dctClipOffset + pR];+	    pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr + 32768) >> 16;+	    rowBuf[1][y2][x1+x2] = 255 - dctClip[dctClipOffset + pG];+	    pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;+	    rowBuf[2][y2][x1+x2] = 255 - dctClip[dctClipOffset + pB];+	  }+	}+      }+    }+  }+  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;++  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(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 sequential 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;+      }+      data[0] += bit << 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) {+	  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) {+	j = dctZigZag[i++];+	if (data[j] == 0) {+	  ++k;+	} else {+	  if ((bit = readBit()) == EOF) {+	    return gFalse;+	  }+	  if (bit) {+	    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) {+	    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;+      }+      k = 0;+      do {+	j = dctZigZag[i++];+	while (data[j] != 0) {+	  if ((bit = readBit()) == EOF) {+	    return gFalse;+	  }+	  if (bit) {+	    data[j] += 1 << scanInfo.al;+	  }+	  j = dctZigZag[i++];+	}+	++k;+      } while (k <= run);+      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[dctClipOffset + pR];+	      pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr ++		    32768) >> 16;+	      *p1++ = dctClip[dctClipOffset + pG];+	      pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;+	      *p2++ = dctClip[dctClipOffset + 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[dctClipOffset + pR];+	      pG = ((pY << 16) + dctCbToG * pCb + dctCrToG * pCr ++		    32768) >> 16;+	      *p1++ = 255 - dctClip[dctClipOffset + pG];+	      pB = ((pY << 16) + dctCbToB * pCb + 32768) >> 16;+	      *p2++ = 255 - dctClip[dctClipOffset + pB];+	    }+	  }+	}+      }+    }+  }+}++// Transform one data unit -- this performs the dequantization and+// IDCT steps.  This IDCT algorithm is taken from:+//   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 this+// paper.+void DCTStream::transformDataUnit(Gushort *quantTable,+				  int dataIn[64], Guchar dataOut[64]) {+  int v0, v1, v2, v3, v4, v5, v6, v7, t;+  int *p;+  int i;++  // dequant+  for (i = 0; i < 64; ++i) {+    dataIn[i] *= quantTable[i];+  }++  // inverse DCT on rows+  for (i = 0; i < 64; i += 8) {+    p = dataIn + 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) {+      t = (dctSqrt2 * p[0] + 512) >> 10;+      p[0] = t;+      p[1] = t;+      p[2] = t;+      p[3] = t;+      p[4] = t;+      p[5] = t;+      p[6] = t;+      p[7] = t;+      continue;+    }++    // stage 4+    v0 = (dctSqrt2 * p[0] + 128) >> 8;+    v1 = (dctSqrt2 * p[4] + 128) >> 8;+    v2 = p[2];+    v3 = p[6];+    v4 = (dctSqrt1d2 * (p[1] - p[7]) + 128) >> 8;+    v7 = (dctSqrt1d2 * (p[1] + p[7]) + 128) >> 8;+    v5 = p[3] << 4;+    v6 = p[5] << 4;++    // stage 3+    t = (v0 - v1+ 1) >> 1;+    v0 = (v0 + v1 + 1) >> 1;+    v1 = t;+    t = (v2 * dctSin6 + v3 * dctCos6 + 128) >> 8;+    v2 = (v2 * dctCos6 - v3 * dctSin6 + 128) >> 8;+    v3 = t;+    t = (v4 - v6 + 1) >> 1;+    v4 = (v4 + v6 + 1) >> 1;+    v6 = t;+    t = (v7 + v5 + 1) >> 1;+    v5 = (v7 - v5 + 1) >> 1;+    v7 = t;++    // stage 2+    t = (v0 - v3 + 1) >> 1;+    v0 = (v0 + v3 + 1) >> 1;+    v3 = t;+    t = (v1 - v2 + 1) >> 1;+    v1 = (v1 + v2 + 1) >> 1;+    v2 = t;+    t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;+    v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;+    v7 = t;+    t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;+    v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;+    v6 = t;++    // 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) {+      t = (dctSqrt2 * dataIn[i+0] + 8192) >> 14;+      p[0*8] = t;+      p[1*8] = t;+      p[2*8] = t;+      p[3*8] = t;+      p[4*8] = t;+      p[5*8] = t;+      p[6*8] = t;+      p[7*8] = t;+      continue;+    }++    // stage 4+    v0 = (dctSqrt2 * p[0*8] + 2048) >> 12;+    v1 = (dctSqrt2 * p[4*8] + 2048) >> 12;+    v2 = p[2*8];+    v3 = p[6*8];+    v4 = (dctSqrt1d2 * (p[1*8] - p[7*8]) + 2048) >> 12;+    v7 = (dctSqrt1d2 * (p[1*8] + p[7*8]) + 2048) >> 12;+    v5 = p[3*8];+    v6 = p[5*8];++    // stage 3+    t = (v0 - v1 + 1) >> 1;+    v0 = (v0 + v1 + 1) >> 1;+    v1 = t;+    t = (v2 * dctSin6 + v3 * dctCos6 + 2048) >> 12;+    v2 = (v2 * dctCos6 - v3 * dctSin6 + 2048) >> 12;+    v3 = t;+    t = (v4 - v6 + 1) >> 1;+    v4 = (v4 + v6 + 1) >> 1;+    v6 = t;+    t = (v7 + v5 + 1) >> 1;+    v5 = (v7 - v5 + 1) >> 1;+    v7 = t;++    // stage 2+    t = (v0 - v3 + 1) >> 1;+    v0 = (v0 + v3 + 1) >> 1;+    v3 = t;+    t = (v1 - v2 + 1) >> 1;+    v1 = (v1 + v2 + 1) >> 1;+    v2 = t;+    t = (v4 * dctSin3 + v7 * dctCos3 + 2048) >> 12;+    v4 = (v4 * dctCos3 - v7 * dctSin3 + 2048) >> 12;+    v7 = t;+    t = (v5 * dctSin1 + v6 * dctCos1 + 2048) >> 12;+    v5 = (v5 * dctCos1 - v6 * dctSin1 + 2048) >> 12;+    v6 = t;++    // 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[dctClipOffset + 128 + ((dataIn[i] + 8) >> 4)];+  }+}++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 = (code << 1) + bit;+    ++codeBits;++    // look up code+    if (code - table->firstCode[codeBits] < table->numCodes[codeBits]) {+      code -= table->firstCode[codeBits];+      return table->sym[table->firstSym[codeBits] + code];+    }+  } while (codeBits < 16);++  error(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(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 doScan;+  int n;+  int c = 0;+  int i;++  // read headers+  doScan = gFalse;+  while (!doScan) {+    c = readMarker();+    switch (c) {+    case 0xc0:			// SOF0 (sequential)+    case 0xc1:			// SOF1 (extended sequential)+      if (!readBaselineSOF()) {+	return gFalse;+      }+      break;+    case 0xc2:			// SOF2 (progressive)+      if (!readProgressiveSOF()) {+	return gFalse;+      }+      break;+    case 0xc4:			// DHT+      if (!readHuffmanTables()) {+	return gFalse;+      }+      break;+    case 0xd8:			// SOI+      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 (!readJFIFMarker()) {+	return gFalse;+      }+      break;+    case 0xee:			// APP14+      if (!readAdobeMarker()) {+	return gFalse;+      }+      break;+    case EOF:+      error(getPos(), "Bad DCT header");+      return gFalse;+    default:+      // skip APPn / COM / etc.+      if (c >= 0xe0) {+	n = read16() - 2;+	for (i = 0; i < n; ++i) {+	  str->getChar();+	}+      } else {+	error(getPos(), "Unknown DCT marker <%02x>", c);+	return gFalse;+      }+      break;+    }+  }++  return gTrue;+}++GBool DCTStream::readBaselineSOF() {+  int length;+  int prec;+  int i;+  int c;++  length = read16();+  prec = str->getChar();+  height = read16();+  width = read16();+  numComps = str->getChar();+  if (numComps <= 0 || numComps > 4) {+    error(getPos(), "Bad number of components in DCT stream");+    numComps = 0;+    return gFalse;+  }+  if (prec != 8) {+    error(getPos(), "Bad DCT precision %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();+  }+  progressive = gFalse;+  return gTrue;+}++GBool DCTStream::readProgressiveSOF() {+  int length;+  int prec;+  int i;+  int c;++  length = read16();+  prec = str->getChar();+  height = read16();+  width = read16();+  numComps = str->getChar();+  if (numComps <= 0 || numComps > 4) {+    error(getPos(), "Bad number of components in DCT stream");+    numComps = 0;+    return gFalse;+  }+  if (prec != 8) {+    error(getPos(), "Bad DCT precision %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();+  }+  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(getPos(), "Bad number of components in DCT stream");+    scanInfo.numComps = 0;+    return gFalse;+  }+  --length;+  if (length != 2 * scanInfo.numComps + 3) {+    error(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(getPos(), "Bad 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(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(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]] = read16();+      } else {+	quantTables[index][dctZigZag[i]] = 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(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] = c;+      sym += c;+      code = (code + c) << 1;+    }+    length -= 16;+    for (i = 0; i < sym; ++i)+      tbl->sym[i] = str->getChar();+    length -= sym;+  }+  return gTrue;+}++GBool DCTStream::readRestartInterval() {+  int length;++  length = read16();+  if (length != 4) {+    error(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(getPos(), "Bad DCT APP0 marker");+	return gFalse;+      }+      buf[i] = c;+    }+    length -= 5;+    if (!memcmp(buf, "JFIF\0", 5)) {+      gotJFIFMarker = gTrue;+    }+  }+  while (length > 0) {+    if (str->getChar() == EOF) {+      error(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] = c;+  }+  if (strncmp(buf, "Adobe", 5)) {+    goto err;+  }+  colorXform = buf[11];+  gotAdobeMarker = gTrue;+  for (i = 14; i < length; ++i) {+    if (str->getChar() == EOF) {+      goto err;+    }+  }+  return gTrue;++ err:+  error(getPos(), "Bad DCT Adobe APP14 marker");+  return gFalse;+}++GBool DCTStream::readTrailer() {+  int c;++  c = readMarker();+  if (c != 0xd9) {		// EOI+    error(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;+}++GString *DCTStream::getPSFilter(int psLevel, char *indent) {+  GString *s;++  if (psLevel < 2) {+    return NULL;+  }+  if (!(s = str->getPSFilter(psLevel, indent))) {+    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;+}++void FlateStream::reset() {+  int cmf, flg;++  index = 0;+  remain = 0;+  codeBuf = 0;+  codeSize = 0;+  compressedBlock = gFalse;+  endOfBlock = gTrue;+  eof = gTrue;++  str->reset();++  // read header+  //~ need to look at window size?+  endOfBlock = eof = gTrue;+  cmf = str->getChar();+  flg = str->getChar();+  if (cmf == EOF || flg == EOF)+    return;+  if ((cmf & 0x0f) != 0x08) {+    error(getPos(), "Unknown compression method in flate stream");+    return;+  }+  if ((((cmf << 8) + flg) % 31) != 0) {+    error(getPos(), "Bad FCHECK in flate stream");+    return;+  }+  if (flg & 0x20) {+    error(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;+}++GString *FlateStream::getPSFilter(int psLevel, char *indent) {+  GString *s;++  if (psLevel < 3 || pred) {+    return NULL;+  }+  if (!(s = str->getPSFilter(psLevel, indent))) {+    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 i, j, k;+  int c;++  if (endOfBlock) {+    if (!startBlock())+      return;+  }++  if (compressedBlock) {+    if ((code1 = getHuffmanCodeWord(&litCodeTab)) == EOF)+      goto err;+    if (code1 < 256) {+      buf[index] = 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;+      i = index;+      j = (index - dist) & flateMask;+      for (k = 0; k < len; ++k) {+	buf[i] = buf[j];+	i = (i + 1) & flateMask;+	j = (j + 1) & flateMask;+      }+      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] = c & 0xff;+    }+    remain = i;+    blockLen -= len;+    if (blockLen == 0)+      endOfBlock = gTrue;+  }++  return;++err:+  error(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(getPos(), "Bad uncompressed block length in flate stream");+    codeBuf = 0;+    codeSize = 0;++  // 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(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(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;+  }+  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;+  }+  c = codeBuf & ((1 << bits) - 1);+  codeBuf >>= bits;+  codeSize -= bits;+  return c;+}++//------------------------------------------------------------------------+// EOFStream+//------------------------------------------------------------------------++EOFStream::EOFStream(Stream *strA):+    FilterStream(strA) {+}++EOFStream::~EOFStream() {+  delete str;+}++//------------------------------------------------------------------------+// FixedLengthEncoder+//------------------------------------------------------------------------++FixedLengthEncoder::FixedLengthEncoder(Stream *strA, int lengthA):+    FilterStream(strA) {+  length = lengthA;+  count = 0;+}++FixedLengthEncoder::~FixedLengthEncoder() {+  if (str->isEncoder())+    delete str;+}++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;+  }+}++void ASCIIHexEncoder::reset() {+  str->reset();+  bufPtr = bufEnd = buf;+  lineLen = 0;+  eof = gFalse;+}++GBool ASCIIHexEncoder::fillBuf() {+  static 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;+}++void ASCII85Encoder::reset() {+  str->reset();+  bufPtr = bufEnd = buf;+  lineLen = 0;+  eof = gFalse;+}++GBool ASCII85Encoder::fillBuf() {+  Gulong 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;+}++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] = 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] = c1;+    bufEnd = &buf[2];+    if (c == EOF) {+      eof = gTrue;+    } else if (n < 128) {+      buf[2] = c;+      nextEnd = &buf[3];+    } else {+      nextEnd = bufEnd;+    }++  // get up to 128 chars+  } else {+    buf[1] = c1;+    buf[2] = c2;+    n = 2;+    while (n < 128) {+      if ((c = str->getChar()) == EOF) {+	eof = gTrue;+	break;+      }+      ++n;+      buf[n] = 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;+}
+ xpdf/Stream.h view
@@ -0,0 +1,860 @@+//========================================================================+//+// 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>+#include "gtypes.h"+#include "Object.h"++class BaseStream;++//------------------------------------------------------------------------++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+};++//------------------------------------------------------------------------+// Stream (base class)+//------------------------------------------------------------------------++class Stream {+public:++  // Constructor.+  Stream();++  // Destructor.+  virtual ~Stream();++  // Reference counting.+  int incRef() { return ++ref; }+  int decRef() { return --ref; }++  // Get kind of stream.+  virtual StreamKind getKind() = 0;++  // 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 next line from stream.+  virtual char *getLine(char *buf, int size);++  // Get current position in file.+  virtual int 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(Guint pos, int dir = 0) = 0;++  // Get PostScript command for the filter(s).+  virtual GString *getPSFilter(int psLevel, char *indent);++  // 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);++private:++  Stream *makeFilter(char *name, Stream *str, Object *params);++  int ref;			// reference count+};++//------------------------------------------------------------------------+// 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(Guint start, GBool limited,+				Guint length, Object *dict) = 0;+  virtual void setPos(Guint 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 Guint getStart() = 0;+  virtual void moveStart(int delta) = 0;++private:++  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 int getPos() { return str->getPos(); }+  virtual void setPos(Guint 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();++  // 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+  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; }++  int lookChar();+  int getChar();++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, Guint startA, GBool limitedA,+	     Guint lengthA, Object *dictA);+  virtual ~FileStream();+  virtual Stream *makeSubStream(Guint startA, GBool limitedA,+				Guint lengthA, Object *dictA);+  virtual StreamKind getKind() { return strFile; }+  virtual void reset();+  virtual void close();+  virtual int getChar()+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr++ & 0xff); }+  virtual int lookChar()+    { return (bufPtr >= bufEnd && !fillBuf()) ? EOF : (*bufPtr & 0xff); }+  virtual int getPos() { return bufPos + (bufPtr - buf); }+  virtual void setPos(Guint pos, int dir = 0);+  virtual Guint getStart() { return start; }+  virtual void moveStart(int delta);++private:++  GBool fillBuf();++  FILE *f;+  Guint start;+  GBool limited;+  Guint length;+  char buf[fileStreamBufSize];+  char *bufPtr;+  char *bufEnd;+  Guint bufPos;+  int savePos;+  GBool saved;+};++//------------------------------------------------------------------------+// MemStream+//------------------------------------------------------------------------++class MemStream: public BaseStream {+public:++  MemStream(char *bufA, Guint startA, Guint lengthA, Object *dictA);+  virtual ~MemStream();+  virtual Stream *makeSubStream(Guint start, GBool limited,+				Guint 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 getPos() { return (int)(bufPtr - buf); }+  virtual void setPos(Guint pos, int dir = 0);+  virtual Guint 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, Guint lengthA);+  virtual ~EmbedStream();+  virtual Stream *makeSubStream(Guint start, GBool limitedA,+				Guint lengthA, Object *dictA);+  virtual StreamKind getKind() { return str->getKind(); }+  virtual void reset() {}+  virtual int getChar();+  virtual int lookChar();+  virtual int getPos() { return str->getPos(); }+  virtual void setPos(Guint pos, int dir = 0);+  virtual Guint getStart();+  virtual void moveStart(int delta);++private:++  Stream *str;+  GBool limited;+  Guint length;+};++//------------------------------------------------------------------------+// ASCIIHexStream+//------------------------------------------------------------------------++class ASCIIHexStream: public FilterStream {+public:++  ASCIIHexStream(Stream *strA);+  virtual ~ASCIIHexStream();+  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, char *indent);+  virtual GBool isBinary(GBool last = gTrue);++private:++  int buf;+  GBool eof;+};++//------------------------------------------------------------------------+// ASCII85Stream+//------------------------------------------------------------------------++class ASCII85Stream: public FilterStream {+public:++  ASCII85Stream(Stream *strA);+  virtual ~ASCII85Stream();+  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, char *indent);+  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 StreamKind getKind() { return strLZW; }+  virtual void reset();+  virtual int getChar();+  virtual int lookChar();+  virtual int getRawChar();+  virtual GString *getPSFilter(int psLevel, char *indent);+  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++  GBool processNextCode();+  void clearTable();+  int getCode();+};++//------------------------------------------------------------------------+// RunLengthStream+//------------------------------------------------------------------------++class RunLengthStream: public FilterStream {+public:++  RunLengthStream(Stream *strA);+  virtual ~RunLengthStream();+  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 GString *getPSFilter(int psLevel, char *indent);+  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 StreamKind getKind() { return strCCITTFax; }+  virtual void reset();+  virtual int getChar()+    { int c = lookChar(); buf = EOF; return c; }+  virtual int lookChar();+  virtual GString *getPSFilter(int psLevel, char *indent);+  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+  GBool eof;			// true if at eof+  GBool nextLine2D;		// true if next line uses 2D encoding+  int row;			// current row+  int inputBuf;			// input buffer+  int inputBits;		// number of bits in input buffer+  int *codingLine;		// coding line changing elements+  int *refLine;			// reference line changing elements+  int a0i;			// index into codingLine+  GBool err;			// error on current line+  int outputBits;		// remaining ouput bits+  int buf;			// character buffer++  void addPixels(int a1, int black);+  void addPixelsNeg(int a1, int black);+  short getTwoDimCode();+  short getWhiteCode();+  short getBlackCode();+  short lookBits(int n);+  void eatBits(int n) { if ((inputBits -= n) < 0) inputBits = 0; }+};++//------------------------------------------------------------------------+// DCTStream+//------------------------------------------------------------------------++// 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+};++class DCTStream: public FilterStream {+public:++  DCTStream(Stream *strA, int colorXformA);+  virtual ~DCTStream();+  virtual StreamKind getKind() { return strDCT; }+  virtual void reset();+  virtual void close();+  virtual int getChar();+  virtual int lookChar();+  virtual GString *getPSFilter(int psLevel, char *indent);+  virtual GBool isBinary(GBool last = gTrue);+  Stream *getRawStream() { return str; }++private:++  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[4][32];	// buffer for one MCU (non-progressive mode)+  int *frameBuf[4];		// buffer for frame (progressive mode)+  int comp, x, y, dy;		// 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 readBaselineSOF();+  GBool readProgressiveSOF();+  GBool readScanInfo();+  GBool readQuantTables();+  GBool readHuffmanTables();+  GBool readRestartInterval();+  GBool readJFIFMarker();+  GBool readAdobeMarker();+  GBool readTrailer();+  int readMarker();+  int read16();+};++//------------------------------------------------------------------------+// 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 StreamKind getKind() { return strFlate; }+  virtual void reset();+  virtual int getChar();+  virtual int lookChar();+  virtual int getRawChar();+  virtual GString *getPSFilter(int psLevel, char *indent);+  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++  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 StreamKind getKind() { return strWeird; }+  virtual void reset() {}+  virtual int getChar() { return EOF; }+  virtual int lookChar() { return EOF; }+  virtual GString *getPSFilter(int psLevel, char *indent)  { return NULL; }+  virtual GBool isBinary(GBool last = gTrue) { return gFalse; }+};++//------------------------------------------------------------------------+// FixedLengthEncoder+//------------------------------------------------------------------------++class FixedLengthEncoder: public FilterStream {+public:++  FixedLengthEncoder(Stream *strA, int lengthA);+  ~FixedLengthEncoder();+  virtual StreamKind getKind() { return strWeird; }+  virtual void reset();+  virtual int getChar();+  virtual int lookChar();+  virtual GString *getPSFilter(int psLevel, char *indent) { 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 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, char *indent) { 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 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, char *indent) { 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 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, char *indent) { 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();+};++#endif
+ xpdf/TextOutputDev.cc view
@@ -0,0 +1,4103 @@+//========================================================================+//+// TextOutputDev.cc+//+// Copyright 1997-2003 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 <ctype.h>+#ifdef WIN32+#include <fcntl.h> // for O_BINARY+#include <io.h>    // for setmode+#endif+#include "gmem.h"+#include "GString.h"+#include "GList.h"+#include "config.h"+#include "Error.h"+#include "GlobalParams.h"+#include "UnicodeMap.h"+#include "UnicodeTypeTable.h"+#include "GfxState.h"+#include "Link.h"+#include "TextOutputDev.h"++#ifdef MACOS+// needed for setting type/creator of MacOS files+#include "ICSupport.h"+#endif++//------------------------------------------------------------------------+// parameters+//------------------------------------------------------------------------++// Each bucket in a text pool includes baselines within a range of+// this many points.+#define textPoolStep 4++// Inter-character space width which will cause addChar to start a new+// word.+#define minWordBreakSpace 0.1++// Negative inter-character space width, i.e., overlap, which will+// cause addChar to start a new word.+#define minDupBreakOverlap 0.2++// Max distance between baselines of two lines within a block, as a+// fraction of the font size.+#define maxLineSpacingDelta 1.5++// Max difference in primary font sizes on two lines in the same+// block.  Delta1 is used when examining new lines above and below the+// current block; delta2 is used when examining text that overlaps the+// current block; delta3 is used when examining text to the left and+// right of the current block.+#define maxBlockFontSizeDelta1 0.05+#define maxBlockFontSizeDelta2 0.6+#define maxBlockFontSizeDelta3 0.2++// Max difference in font sizes inside a word.+#define maxWordFontSizeDelta 0.05++// Maximum distance between baselines of two words on the same line,+// e.g., distance between subscript or superscript and the primary+// baseline, as a fraction of the font size.+#define maxIntraLineDelta 0.5++// Minimum inter-word spacing, as a fraction of the font size.  (Only+// used for raw ordering.)+#define minWordSpacing 0.15++// Maximum inter-word spacing, as a fraction of the font size.+#define maxWordSpacing 1.5++// Maximum horizontal spacing which will allow a word to be pulled+// into a block.+#define minColSpacing1 0.3++// Minimum spacing between columns, as a fraction of the font size.+#define minColSpacing2 1.0++// Maximum vertical spacing between blocks within a flow, as a+// multiple of the font size.+#define maxBlockSpacing 2.5++// Minimum spacing between characters within a word, as a fraction of+// the font size.+#define minCharSpacing -0.2++// Maximum spacing between characters within a word, as a fraction of+// the font size, when there is no obvious extra-wide character+// spacing.+#define maxCharSpacing 0.03++// When extra-wide character spacing is detected, the inter-character+// space threshold is set to the minimum inter-character space+// multiplied by this constant.+#define maxWideCharSpacingMul 1.3++// Upper limit on spacing between characters in a word.+#define maxWideCharSpacing 0.4++// 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++// Max width of underlines (in points).+#define maxUnderlineWidth 3++// Min distance between baseline and underline (in points).+//~ this should be font-size-dependent+#define minUnderlineGap -2++// Max distance between baseline and underline (in points).+//~ this should be font-size-dependent+#define maxUnderlineGap 4++// Max horizontal distance between edge of word and start of underline+// (in points).+//~ this should be font-size-dependent+#define underlineSlack 1++// Max distance between edge of text and edge of link border+#define hyperlinkSlack 2++//------------------------------------------------------------------------+// 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(int xMinA, int yMinA, int xMaxA, int yMaxA, Link *linkA)+    { xMin = xMinA; yMin = yMinA; xMax = xMaxA; yMax = yMaxA; link = linkA; }+  ~TextLink() {}++  int xMin, yMin, xMax, yMax;+  Link *link;+};++//------------------------------------------------------------------------+// TextFontInfo+//------------------------------------------------------------------------++TextFontInfo::TextFontInfo(GfxState *state) {+  gfxFont = state->getFont();+#if TEXTOUT_WORD_LIST+  fontName = (gfxFont && gfxFont->getOrigName())+                 ? gfxFont->getOrigName()->copy()+                 : (GString *)NULL;+  flags = gfxFont ? gfxFont->getFlags() : 0;+#endif+}++TextFontInfo::~TextFontInfo() {+#if TEXTOUT_WORD_LIST+  if (fontName) {+    delete fontName;+  }+#endif+}++GBool TextFontInfo::matches(GfxState *state) {+  return state->getFont() == gfxFont;+}++//------------------------------------------------------------------------+// TextWord+//------------------------------------------------------------------------++TextWord::TextWord(GfxState *state, int rotA, double x0, double y0,+		   int charPosA, TextFontInfo *fontA, double fontSizeA) {+  GfxFont *gfxFont;+  double x, y, ascent, descent;++  rot = rotA;+  charPos = charPosA;+  charLen = 0;+  font = fontA;+  fontSize = fontSizeA;+  state->transform(x0, y0, &x, &y);+  if ((gfxFont = font->gfxFont)) {+    ascent = gfxFont->getAscent() * fontSize;+    descent = gfxFont->getDescent() * fontSize;+  } else {+    // this means that the PDF file draws text without a current font,+    // which should never happen+    ascent = 0.95 * fontSize;+    descent = -0.35 * fontSize;+  }+  switch (rot) {+  case 0:+    yMin = y - ascent;+    yMax = y - descent;+    if (yMin == yMax) {+      // this is a sanity check for a case that shouldn't happen -- but+      // if it does happen, we want to avoid dividing by zero later+      yMin = y;+      yMax = y + 1;+    }+    base = y;+    break;+  case 1:+    xMin = x + descent;+    xMax = x + ascent;+    if (xMin == xMax) {+      // this is a sanity check for a case that shouldn't happen -- but+      // if it does happen, we want to avoid dividing by zero later+      xMin = x;+      xMax = x + 1;+    }+    base = x;+    break;+  case 2:+    yMin = y + descent;+    yMax = y + ascent;+    if (yMin == yMax) {+      // this is a sanity check for a case that shouldn't happen -- but+      // if it does happen, we want to avoid dividing by zero later+      yMin = y;+      yMax = y + 1;+    }+    base = y;+    break;+  case 3:+    xMin = x - ascent;+    xMax = x - descent;+    if (xMin == xMax) {+      // this is a sanity check for a case that shouldn't happen -- but+      // if it does happen, we want to avoid dividing by zero later+      xMin = x;+      xMax = x + 1;+    }+    base = x;+    break;+  }+  text = NULL;+  edge = NULL;+  len = size = 0;+  spaceAfter = gFalse;+  next = NULL;++#if TEXTOUT_WORD_LIST+  GfxRGB rgb;++  if ((state->getRender() & 3) == 1) {+    state->getStrokeRGB(&rgb);+  } else {+    state->getFillRGB(&rgb);+  }+  colorR = colToDbl(rgb.r);+  colorG = colToDbl(rgb.g);+  colorB = colToDbl(rgb.b);+#endif++  underlined = gFalse;+  link = NULL;+}++TextWord::~TextWord() {+  gfree(text);+  gfree(edge);+}++void TextWord::addChar(GfxState *state, double x, double y,+		       double dx, double dy, Unicode u) {+  if (len == size) {+    size += 16;+    text = (Unicode *)greallocn(text, size, sizeof(Unicode));+    edge = (double *)greallocn(edge, size + 1, sizeof(double));+  }+  text[len] = u;+  switch (rot) {+  case 0:+    if (len == 0) {+      xMin = x;+    }+    edge[len] = x;+    xMax = edge[len+1] = x + dx;+    break;+  case 1:+    if (len == 0) {+      yMin = y;+    }+    edge[len] = y;+    yMax = edge[len+1] = y + dy;+    break;+  case 2:+    if (len == 0) {+      xMax = x;+    }+    edge[len] = x;+    xMin = edge[len+1] = x + dx;+    break;+  case 3:+    if (len == 0) {+      yMax = y;+    }+    edge[len] = y;+    yMin = edge[len+1] = y + dy;+    break;+  }+  ++len;+}++void TextWord::merge(TextWord *word) {+  int i;++  if (word->xMin < xMin) {+    xMin = word->xMin;+  }+  if (word->yMin < yMin) {+    yMin = word->yMin;+  }+  if (word->xMax > xMax) {+    xMax = word->xMax;+  }+  if (word->yMax > yMax) {+    yMax = word->yMax;+  }+  if (len + word->len > size) {+    size = len + word->len;+    text = (Unicode *)greallocn(text, size, sizeof(Unicode));+    edge = (double *)greallocn(edge, size + 1, sizeof(double));+  }+  for (i = 0; i < word->len; ++i) {+    text[len + i] = word->text[i];+    edge[len + i] = word->edge[i];+  }+  edge[len + word->len] = word->edge[word->len];+  len += word->len;+  charLen += word->charLen;+}++inline int TextWord::primaryCmp(TextWord *word) {+  double cmp;++  cmp = 0; // make gcc happy+  switch (rot) {+  case 0:+    cmp = xMin - word->xMin;+    break;+  case 1:+    cmp = yMin - word->yMin;+    break;+  case 2:+    cmp = word->xMax - xMax;+    break;+  case 3:+    cmp = word->yMax - yMax;+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++double TextWord::primaryDelta(TextWord *word) {+  double delta;++  delta = 0; // make gcc happy+  switch (rot) {+  case 0:+    delta = word->xMin - xMax;+    break;+  case 1:+    delta = word->yMin - yMax;+    break;+  case 2:+    delta = xMin - word->xMax;+    break;+  case 3:+    delta = yMin - word->yMax;+    break;+  }+  return delta;+}++int TextWord::cmpYX(const void *p1, const void *p2) {+  TextWord *word1 = *(TextWord **)p1;+  TextWord *word2 = *(TextWord **)p2;+  double cmp;++  cmp = word1->yMin - word2->yMin;+  if (cmp == 0) {+    cmp = word1->xMin - word2->xMin;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++#if TEXTOUT_WORD_LIST++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;+  }+}++#endif // TEXTOUT_WORD_LIST++//------------------------------------------------------------------------+// TextPool+//------------------------------------------------------------------------++TextPool::TextPool() {+  minBaseIdx = 0;+  maxBaseIdx = -1;+  pool = NULL;+  cursor = NULL;+  cursorBaseIdx = -1;+}++TextPool::~TextPool() {+  int baseIdx;+  TextWord *word, *word2;++  for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {+    for (word = pool[baseIdx - minBaseIdx]; word; word = word2) {+      word2 = word->next;+      delete word;+    }+  }+  gfree(pool);+}++int TextPool::getBaseIdx(double base) {+  int baseIdx;++  baseIdx = (int)(base / textPoolStep);+  if (baseIdx < minBaseIdx) {+    return minBaseIdx;+  }+  if (baseIdx > maxBaseIdx) {+    return maxBaseIdx;+  }+  return baseIdx;+}++void TextPool::addWord(TextWord *word) {+  TextWord **newPool;+  int wordBaseIdx, newMinBaseIdx, newMaxBaseIdx, baseIdx;+  TextWord *w0, *w1;++  // expand the array if needed+  wordBaseIdx = (int)(word->base / textPoolStep);+  if (minBaseIdx > maxBaseIdx) {+    minBaseIdx = wordBaseIdx - 128;+    maxBaseIdx = wordBaseIdx + 128;+    pool = (TextWord **)gmallocn(maxBaseIdx - minBaseIdx + 1,+				 sizeof(TextWord *));+    for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {+      pool[baseIdx - minBaseIdx] = NULL;+    }+  } else if (wordBaseIdx < minBaseIdx) {+    newMinBaseIdx = wordBaseIdx - 128;+    newPool = (TextWord **)gmallocn(maxBaseIdx - newMinBaseIdx + 1,+				    sizeof(TextWord *));+    for (baseIdx = newMinBaseIdx; baseIdx < minBaseIdx; ++baseIdx) {+      newPool[baseIdx - newMinBaseIdx] = NULL;+    }+    memcpy(&newPool[minBaseIdx - newMinBaseIdx], pool,+	   (maxBaseIdx - minBaseIdx + 1) * sizeof(TextWord *));+    gfree(pool);+    pool = newPool;+    minBaseIdx = newMinBaseIdx;+  } else if (wordBaseIdx > maxBaseIdx) {+    newMaxBaseIdx = wordBaseIdx + 128;+    pool = (TextWord **)greallocn(pool, newMaxBaseIdx - minBaseIdx + 1,+				  sizeof(TextWord *));+    for (baseIdx = maxBaseIdx + 1; baseIdx <= newMaxBaseIdx; ++baseIdx) {+      pool[baseIdx - minBaseIdx] = NULL;+    }+    maxBaseIdx = newMaxBaseIdx;+  }++  // insert the new word+  if (cursor && wordBaseIdx == cursorBaseIdx &&+      word->primaryCmp(cursor) > 0) {+    w0 = cursor;+    w1 = cursor->next;+  } else {+    w0 = NULL;+    w1 = pool[wordBaseIdx - minBaseIdx];+  }+  for (; w1 && word->primaryCmp(w1) > 0; w0 = w1, w1 = w1->next) ;+  word->next = w1;+  if (w0) {+    w0->next = word;+  } else {+    pool[wordBaseIdx - minBaseIdx] = word;+  }+  cursor = word;+  cursorBaseIdx = wordBaseIdx;+}++//------------------------------------------------------------------------+// TextLine+//------------------------------------------------------------------------++TextLine::TextLine(TextBlock *blkA, int rotA, double baseA) {+  blk = blkA;+  rot = rotA;+  xMin = yMin = 0;+  xMax = yMax = -1;+  base = baseA;+  words = lastWord = NULL;+  text = NULL;+  edge = NULL;+  col = NULL;+  len = 0;+  convertedLen = 0;+  hyphenated = gFalse;+  next = NULL;+}++TextLine::~TextLine() {+  TextWord *word;++  while (words) {+    word = words;+    words = words->next;+    delete word;+  }+  gfree(text);+  gfree(edge);+  gfree(col);+}++void TextLine::addWord(TextWord *word) {+  if (lastWord) {+    lastWord->next = word;+  } else {+    words = word;+  }+  lastWord = word;++  if (xMin > xMax) {+    xMin = word->xMin;+    xMax = word->xMax;+    yMin = word->yMin;+    yMax = word->yMax;+  } else {+    if (word->xMin < xMin) {+      xMin = word->xMin;+    }+    if (word->xMax > xMax) {+      xMax = word->xMax;+    }+    if (word->yMin < yMin) {+      yMin = word->yMin;+    }+    if (word->yMax > yMax) {+      yMax = word->yMax;+    }+  }+}++double TextLine::primaryDelta(TextLine *line) {+  double delta;++  delta = 0; // make gcc happy+  switch (rot) {+  case 0:+    delta = line->xMin - xMax;+    break;+  case 1:+    delta = line->yMin - yMax;+    break;+  case 2:+    delta = xMin - line->xMax;+    break;+  case 3:+    delta = yMin - line->yMax;+    break;+  }+  return delta;+}++int TextLine::primaryCmp(TextLine *line) {+  double cmp;++  cmp = 0; // make gcc happy+  switch (rot) {+  case 0:+    cmp = xMin - line->xMin;+    break;+  case 1:+    cmp = yMin - line->yMin;+    break;+  case 2:+    cmp = line->xMax - xMax;+    break;+  case 3:+    cmp = line->yMax - yMax;+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++int TextLine::secondaryCmp(TextLine *line) {+  double cmp;++  cmp = (rot == 0 || rot == 3) ? base - line->base : line->base - base;+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++int TextLine::cmpYX(TextLine *line) {+  int cmp;++  if ((cmp = secondaryCmp(line))) {+    return cmp;+  }+  return primaryCmp(line);+}++int TextLine::cmpXY(const void *p1, const void *p2) {+  TextLine *line1 = *(TextLine **)p1;+  TextLine *line2 = *(TextLine **)p2;+  int cmp;++  if ((cmp = line1->primaryCmp(line2))) {+    return cmp;+  }+  return line1->secondaryCmp(line2);+}++void TextLine::coalesce(UnicodeMap *uMap) {+  TextWord *word0, *word1;+  double space, delta, minSpace;+  GBool isUnicode;+  char buf[8];+  int i, j;++  if (words->next) {++    // compute the inter-word space threshold+    if (words->len > 1 || words->next->len > 1) {+      minSpace = 0;+    } else {+      minSpace = words->primaryDelta(words->next);+      for (word0 = words->next, word1 = word0->next;+	   word1 && minSpace > 0;+	   word0 = word1, word1 = word0->next) {+	if (word1->len > 1) {+	  minSpace = 0;+	}+	delta = word0->primaryDelta(word1);+	if (delta < minSpace) {+	  minSpace = delta;+	}+      }+    }+    if (minSpace <= 0) {+      space = maxCharSpacing * words->fontSize;+    } else {+      space = maxWideCharSpacingMul * minSpace;+      if (space > maxWideCharSpacing * words->fontSize) {+	space = maxWideCharSpacing * words->fontSize;+      }+    }++    // merge words+    word0 = words;+    word1 = words->next;+    while (word1) {+      if (word0->primaryDelta(word1) >= space) {+	word0->spaceAfter = gTrue;+	word0 = word1;+	word1 = word1->next;+      } else if (word0->font == word1->font &&+		 word0->underlined == word1->underlined &&+		 fabs(word0->fontSize - word1->fontSize) <+		   maxWordFontSizeDelta * words->fontSize &&+		 word1->charPos == word0->charPos + word0->charLen) {+	word0->merge(word1);+	word0->next = word1->next;+	delete word1;+	word1 = word0->next;+      } else {+	word0 = word1;+	word1 = word1->next;+      }+    }+  }++  // build the line text+  isUnicode = uMap ? uMap->isUnicode() : gFalse;+  len = 0;+  for (word1 = words; word1; word1 = word1->next) {+    len += word1->len;+    if (word1->spaceAfter) {+      ++len;+    }+  }+  text = (Unicode *)gmallocn(len, sizeof(Unicode));+  edge = (double *)gmallocn(len + 1, sizeof(double));+  i = 0;+  for (word1 = words; word1; word1 = word1->next) {+    for (j = 0; j < word1->len; ++j) {+      text[i] = word1->text[j];+      edge[i] = word1->edge[j];+      ++i;+    }+    edge[i] = word1->edge[word1->len];+    if (word1->spaceAfter) {+      text[i] = (Unicode)0x0020;+      ++i;+    }+  }++  // compute convertedLen and set up the col array+  col = (int *)gmallocn(len + 1, sizeof(int));+  convertedLen = 0;+  for (i = 0; i < len; ++i) {+    col[i] = convertedLen;+    if (isUnicode) {+      ++convertedLen;+    } else if (uMap) {+      convertedLen += uMap->mapUnicode(text[i], buf, sizeof(buf));+    }+  }+  col[len] = convertedLen;++  // check for hyphen at end of line+  //~ need to check for other chars used as hyphens+  hyphenated = text[len - 1] == (Unicode)'-';+}++//------------------------------------------------------------------------+// TextLineFrag+//------------------------------------------------------------------------++class TextLineFrag {+public:++  TextLine *line;		// the line object+  int start, len;		// offset and length of this fragment+				//   (in Unicode chars)+  double xMin, xMax;		// bounding box coordinates+  double yMin, yMax;+  double base;			// baseline virtual coordinate+  int col;			// first column++  void init(TextLine *lineA, int startA, int lenA);+  void computeCoords(GBool oneRot);++  static int cmpYXPrimaryRot(const void *p1, const void *p2);+  static int cmpYXLineRot(const void *p1, const void *p2);+  static int cmpXYLineRot(const void *p1, const void *p2);+  static int cmpXYColumnPrimaryRot(const void *p1, const void *p2);+  static int cmpXYColumnLineRot(const void *p1, const void *p2);+};++void TextLineFrag::init(TextLine *lineA, int startA, int lenA) {+  line = lineA;+  start = startA;+  len = lenA;+  col = line->col[start];+}++void TextLineFrag::computeCoords(GBool oneRot) {+  TextBlock *blk;+  double d0, d1, d2, d3, d4;++  if (oneRot) {++    switch (line->rot) {+    case 0:+      xMin = line->edge[start];+      xMax = line->edge[start + len];+      yMin = line->yMin;+      yMax = line->yMax;+      break;+    case 1:+      xMin = line->xMin;+      xMax = line->xMax;+      yMin = line->edge[start];+      yMax = line->edge[start + len];+      break;+    case 2:+      xMin = line->edge[start + len];+      xMax = line->edge[start];+      yMin = line->yMin;+      yMax = line->yMax;+      break;+    case 3:+      xMin = line->xMin;+      xMax = line->xMax;+      yMin = line->edge[start + len];+      yMax = line->edge[start];+      break;+    }+    base = line->base;++  } else {++    if (line->rot == 0 && line->blk->page->primaryRot == 0) {++      xMin = line->edge[start];+      xMax = line->edge[start + len];+      yMin = line->yMin;+      yMax = line->yMax;+      base = line->base;++    } else {++      blk = line->blk;+      d0 = line->edge[start];+      d1 = line->edge[start + len];+      d2 = d3 = d4 = 0; // make gcc happy++      switch (line->rot) {+      case 0:+	d2 = line->yMin;+	d3 = line->yMax;+	d4 = line->base;+	d0 = (d0 - blk->xMin) / (blk->xMax - blk->xMin);+	d1 = (d1 - blk->xMin) / (blk->xMax - blk->xMin);+	d2 = (d2 - blk->yMin) / (blk->yMax - blk->yMin);+	d3 = (d3 - blk->yMin) / (blk->yMax - blk->yMin);+	d4 = (d4 - blk->yMin) / (blk->yMax - blk->yMin);+	break;+      case 1:+	d2 = line->xMax;+	d3 = line->xMin;+	d4 = line->base;+	d0 = (d0 - blk->yMin) / (blk->yMax - blk->yMin);+	d1 = (d1 - blk->yMin) / (blk->yMax - blk->yMin);+	d2 = (blk->xMax - d2) / (blk->xMax - blk->xMin);+	d3 = (blk->xMax - d3) / (blk->xMax - blk->xMin);+	d4 = (blk->xMax - d4) / (blk->xMax - blk->xMin);+	break;+      case 2:+	d2 = line->yMax;+	d3 = line->yMin;+	d4 = line->base;+	d0 = (blk->xMax - d0) / (blk->xMax - blk->xMin);+	d1 = (blk->xMax - d1) / (blk->xMax - blk->xMin);+	d2 = (blk->yMax - d2) / (blk->yMax - blk->yMin);+	d3 = (blk->yMax - d3) / (blk->yMax - blk->yMin);+	d4 = (blk->yMax - d4) / (blk->yMax - blk->yMin);+	break;+      case 3:+	d2 = line->xMin;+	d3 = line->xMax;+	d4 = line->base;+	d0 = (blk->yMax - d0) / (blk->yMax - blk->yMin);+	d1 = (blk->yMax - d1) / (blk->yMax - blk->yMin);+	d2 = (d2 - blk->xMin) / (blk->xMax - blk->xMin);+	d3 = (d3 - blk->xMin) / (blk->xMax - blk->xMin);+	d4 = (d4 - blk->xMin) / (blk->xMax - blk->xMin);+	break;+      }++      switch (line->blk->page->primaryRot) {+      case 0:+	xMin = blk->xMin + d0 * (blk->xMax - blk->xMin);+	xMax = blk->xMin + d1 * (blk->xMax - blk->xMin);+	yMin = blk->yMin + d2 * (blk->yMax - blk->yMin);+	yMax = blk->yMin + d3 * (blk->yMax - blk->yMin);+	base = blk->yMin + base * (blk->yMax - blk->yMin);+	break;+      case 1:+	xMin = blk->xMax - d3 * (blk->xMax - blk->xMin);+	xMax = blk->xMax - d2 * (blk->xMax - blk->xMin);+	yMin = blk->yMin + d0 * (blk->yMax - blk->yMin);+	yMax = blk->yMin + d1 * (blk->yMax - blk->yMin);+	base = blk->xMax - d4 * (blk->xMax - blk->xMin);+	break;+      case 2:+	xMin = blk->xMax - d1 * (blk->xMax - blk->xMin);+	xMax = blk->xMax - d0 * (blk->xMax - blk->xMin);+	yMin = blk->yMax - d3 * (blk->yMax - blk->yMin);+	yMax = blk->yMax - d2 * (blk->yMax - blk->yMin);+	base = blk->yMax - d4 * (blk->yMax - blk->yMin);+	break;+      case 3:+	xMin = blk->xMin + d2 * (blk->xMax - blk->xMin);+	xMax = blk->xMin + d3 * (blk->xMax - blk->xMin);+	yMin = blk->yMax - d1 * (blk->yMax - blk->yMin);+	yMax = blk->yMax - d0 * (blk->yMax - blk->yMin);+	base = blk->xMin + d4 * (blk->xMax - blk->xMin);+	break;+      }++    }+  }+}++int TextLineFrag::cmpYXPrimaryRot(const void *p1, const void *p2) {+  TextLineFrag *frag1 = (TextLineFrag *)p1;+  TextLineFrag *frag2 = (TextLineFrag *)p2;+  double cmp;++  cmp = 0; // make gcc happy+  switch (frag1->line->blk->page->primaryRot) {+  case 0:+    if (fabs(cmp = frag1->yMin - frag2->yMin) < 0.01) {+      cmp = frag1->xMin - frag2->xMin;+    }+    break;+  case 1:+    if (fabs(cmp = frag2->xMax - frag1->xMax) < 0.01) {+      cmp = frag1->yMin - frag2->yMin;+    }+    break;+  case 2:+    if (fabs(cmp = frag2->yMin - frag1->yMin) < 0.01) {+      cmp = frag2->xMax - frag1->xMax;+    }+    break;+  case 3:+    if (fabs(cmp = frag1->xMax - frag2->xMax) < 0.01) {+      cmp = frag2->yMax - frag1->yMax;+    }+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++int TextLineFrag::cmpYXLineRot(const void *p1, const void *p2) {+  TextLineFrag *frag1 = (TextLineFrag *)p1;+  TextLineFrag *frag2 = (TextLineFrag *)p2;+  double cmp;++  cmp = 0; // make gcc happy+  switch (frag1->line->rot) {+  case 0:+    if ((cmp = frag1->yMin - frag2->yMin) == 0) {+      cmp = frag1->xMin - frag2->xMin;+    }+    break;+  case 1:+    if ((cmp = frag2->xMax - frag1->xMax) == 0) {+      cmp = frag1->yMin - frag2->yMin;+    }+    break;+  case 2:+    if ((cmp = frag2->yMin - frag1->yMin) == 0) {+      cmp = frag2->xMax - frag1->xMax;+    }+    break;+  case 3:+    if ((cmp = frag1->xMax - frag2->xMax) == 0) {+      cmp = frag2->yMax - frag1->yMax;+    }+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++int TextLineFrag::cmpXYLineRot(const void *p1, const void *p2) {+  TextLineFrag *frag1 = (TextLineFrag *)p1;+  TextLineFrag *frag2 = (TextLineFrag *)p2;+  double cmp;++  cmp = 0; // make gcc happy+  switch (frag1->line->rot) {+  case 0:+    if ((cmp = frag1->xMin - frag2->xMin) == 0) {+      cmp = frag1->yMin - frag2->yMin;+    }+    break;+  case 1:+    if ((cmp = frag1->yMin - frag2->yMin) == 0) {+      cmp = frag2->xMax - frag1->xMax;+    }+    break;+  case 2:+    if ((cmp = frag2->xMax - frag1->xMax) == 0) {+      cmp = frag2->yMin - frag1->yMin;+    }+    break;+  case 3:+    if ((cmp = frag2->yMax - frag1->yMax) == 0) {+      cmp = frag1->xMax - frag2->xMax;+    }+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++int TextLineFrag::cmpXYColumnPrimaryRot(const void *p1, const void *p2) {+  TextLineFrag *frag1 = (TextLineFrag *)p1;+  TextLineFrag *frag2 = (TextLineFrag *)p2;+  double cmp;++  // if columns overlap, compare y values+  if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] -+				 frag2->line->col[frag2->start]) &&+      frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] -+				 frag1->line->col[frag1->start])) {+    cmp = 0; // make gcc happy+    switch (frag1->line->blk->page->primaryRot) {+    case 0: cmp = frag1->yMin - frag2->yMin; break;+    case 1: cmp = frag2->xMax - frag1->xMax; break;+    case 2: cmp = frag2->yMin - frag1->yMin; break;+    case 3: cmp = frag1->xMax - frag2->xMax; break;+    }+    return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+  }++  // otherwise, compare starting column+  return frag1->col - frag2->col;+}++int TextLineFrag::cmpXYColumnLineRot(const void *p1, const void *p2) {+  TextLineFrag *frag1 = (TextLineFrag *)p1;+  TextLineFrag *frag2 = (TextLineFrag *)p2;+  double cmp;++  // if columns overlap, compare y values+  if (frag1->col < frag2->col + (frag2->line->col[frag2->start + frag2->len] -+				 frag2->line->col[frag2->start]) &&+      frag2->col < frag1->col + (frag1->line->col[frag1->start + frag1->len] -+				 frag1->line->col[frag1->start])) {+    cmp = 0; // make gcc happy+    switch (frag1->line->rot) {+    case 0: cmp = frag1->yMin - frag2->yMin; break;+    case 1: cmp = frag2->xMax - frag1->xMax; break;+    case 2: cmp = frag2->yMin - frag1->yMin; break;+    case 3: cmp = frag1->xMax - frag2->xMax; break;+    }+    return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+  }++  // otherwise, compare starting column+  return frag1->col - frag2->col;+}++//------------------------------------------------------------------------+// TextBlock+//------------------------------------------------------------------------++TextBlock::TextBlock(TextPage *pageA, int rotA) {+  page = pageA;+  rot = rotA;+  xMin = yMin = 0;+  xMax = yMax = -1;+  priMin = 0;+  priMax = page->pageWidth;+  pool = new TextPool();+  lines = NULL;+  curLine = NULL;+  next = NULL;+  stackNext = NULL;+}++TextBlock::~TextBlock() {+  TextLine *line;++  delete pool;+  while (lines) {+    line = lines;+    lines = lines->next;+    delete line;+  }+}++void TextBlock::addWord(TextWord *word) {+  pool->addWord(word);+  if (xMin > xMax) {+    xMin = word->xMin;+    xMax = word->xMax;+    yMin = word->yMin;+    yMax = word->yMax;+  } else {+    if (word->xMin < xMin) {+      xMin = word->xMin;+    }+    if (word->xMax > xMax) {+      xMax = word->xMax;+    }+    if (word->yMin < yMin) {+      yMin = word->yMin;+    }+    if (word->yMax > yMax) {+      yMax = word->yMax;+    }+  }+}++void TextBlock::coalesce(UnicodeMap *uMap) {+  TextWord *word0, *word1, *word2, *bestWord0, *bestWord1, *lastWord;+  TextLine *line, *line0, *line1;+  int poolMinBaseIdx, startBaseIdx, minBaseIdx, maxBaseIdx;+  int baseIdx, bestWordBaseIdx, idx0, idx1;+  double minBase, maxBase;+  double fontSize, delta, priDelta, secDelta;+  TextLine **lineArray;+  GBool found;+  int col1, col2;+  int i, j, k;++  // discard duplicated text (fake boldface, drop shadows)+  for (idx0 = pool->minBaseIdx; idx0 <= pool->maxBaseIdx; ++idx0) {+    word0 = pool->getPool(idx0);+    while (word0) {+      priDelta = dupMaxPriDelta * word0->fontSize;+      secDelta = dupMaxSecDelta * word0->fontSize;+      if (rot == 0 || rot == 3) {+	maxBaseIdx = pool->getBaseIdx(word0->base + secDelta);+      } else {+	maxBaseIdx = pool->getBaseIdx(word0->base - secDelta);+      }+      found = gFalse;+      word1 = word2 = NULL; // make gcc happy+      for (idx1 = idx0; idx1 <= maxBaseIdx; ++idx1) {+	if (idx1 == idx0) {+	  word1 = word0;+	  word2 = word0->next;+	} else {+	  word1 = NULL;+	  word2 = pool->getPool(idx1);+	}+	for (; word2; word1 = word2, word2 = word2->next) {+	  if (word2->len == word0->len &&+	      !memcmp(word2->text, word0->text,+		      word0->len * sizeof(Unicode))) {+	    switch (rot) {+	    case 0:+	    case 2:+	      found = fabs(word0->xMin - word2->xMin) < priDelta &&+		      fabs(word0->xMax - word2->xMax) < priDelta &&+		      fabs(word0->yMin - word2->yMin) < secDelta &&+		      fabs(word0->yMax - word2->yMax) < secDelta;+	      break;+	    case 1:+	    case 3:+	      found = fabs(word0->xMin - word2->xMin) < secDelta &&+		      fabs(word0->xMax - word2->xMax) < secDelta &&+		      fabs(word0->yMin - word2->yMin) < priDelta &&+		      fabs(word0->yMax - word2->yMax) < priDelta;+	      break;+	    }+	  }+	  if (found) {+	    break;+	  }+	}+	if (found) {+	  break;+	}+      }+      if (found) {+	if (word1) {+	  word1->next = word2->next;+	} else {+	  pool->setPool(idx1, word2->next);+	}+	delete word2;+      } else {+	word0 = word0->next;+      }+    }+  }++  // build the lines+  curLine = NULL;+  poolMinBaseIdx = pool->minBaseIdx;+  charCount = 0;+  nLines = 0;+  while (1) {++    // find the first non-empty line in the pool+    for (;+	 poolMinBaseIdx <= pool->maxBaseIdx && !pool->getPool(poolMinBaseIdx);+	 ++poolMinBaseIdx) ;+    if (poolMinBaseIdx > pool->maxBaseIdx) {+      break;+    }++    // look for the left-most word in the first four lines of the+    // pool -- this avoids starting with a superscript word+    startBaseIdx = poolMinBaseIdx;+    for (baseIdx = poolMinBaseIdx + 1;+	 baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;+	 ++baseIdx) {+      if (!pool->getPool(baseIdx)) {+	continue;+      }+      if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))+	  < 0) {+	startBaseIdx = baseIdx;+      }+    }++    // create a new line+    word0 = pool->getPool(startBaseIdx);+    pool->setPool(startBaseIdx, word0->next);+    word0->next = NULL;+    line = new TextLine(this, word0->rot, word0->base);+    line->addWord(word0);+    lastWord = word0;++    // compute the search range+    fontSize = word0->fontSize;+    minBase = word0->base - maxIntraLineDelta * fontSize;+    maxBase = word0->base + maxIntraLineDelta * fontSize;+    minBaseIdx = pool->getBaseIdx(minBase);+    maxBaseIdx = pool->getBaseIdx(maxBase);++    // find the rest of the words in this line+    while (1) {++      // find the left-most word whose baseline is in the range for+      // this line+      bestWordBaseIdx = 0;+      bestWord0 = bestWord1 = NULL;+      for (baseIdx = minBaseIdx; baseIdx <= maxBaseIdx; ++baseIdx) {+	for (word0 = NULL, word1 = pool->getPool(baseIdx);+	     word1;+	     word0 = word1, word1 = word1->next) {+	  if (word1->base >= minBase &&+	      word1->base <= maxBase &&+	      (delta = lastWord->primaryDelta(word1)) >=+	        minCharSpacing * fontSize) {+	    if (delta < maxWordSpacing * fontSize &&+		(!bestWord1 || word1->primaryCmp(bestWord1) < 0)) {+	      bestWordBaseIdx = baseIdx;+	      bestWord0 = word0;+	      bestWord1 = word1;+	    }+	    break;+	  }+	}+      }+      if (!bestWord1) {+	break;+      }++      // remove it from the pool, and add it to the line+      if (bestWord0) {+	bestWord0->next = bestWord1->next;+      } else {+	pool->setPool(bestWordBaseIdx, bestWord1->next);+      }+      bestWord1->next = NULL;+      line->addWord(bestWord1);+      lastWord = bestWord1;+    }++    // add the line+    if (curLine && line->cmpYX(curLine) > 0) {+      line0 = curLine;+      line1 = curLine->next;+    } else {+      line0 = NULL;+      line1 = lines;+    }+    for (;+	 line1 && line->cmpYX(line1) > 0;+	 line0 = line1, line1 = line1->next) ;+    if (line0) {+      line0->next = line;+    } else {+      lines = line;+    }+    line->next = line1;+    curLine = line;+    line->coalesce(uMap);+    charCount += line->len;+    ++nLines;+  }++  // sort lines into xy order for column assignment+  lineArray = (TextLine **)gmallocn(nLines, sizeof(TextLine *));+  for (line = lines, i = 0; line; line = line->next, ++i) {+    lineArray[i] = line;+  }+  qsort(lineArray, nLines, sizeof(TextLine *), &TextLine::cmpXY);++  // column assignment+  nColumns = 0;+  for (i = 0; i < nLines; ++i) {+    line0 = lineArray[i];+    col1 = 0;+    for (j = 0; j < i; ++j) {+      line1 = lineArray[j];+      if (line1->primaryDelta(line0) >= 0) {+	col2 = line1->col[line1->len] + 1;+      } else {+	k = 0; // make gcc happy+	switch (rot) {+	case 0:+	  for (k = 0;+	       k < line1->len &&+		 line0->xMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);+	       ++k) ;+	  break;+	case 1:+	  for (k = 0;+	       k < line1->len &&+		 line0->yMin >= 0.5 * (line1->edge[k] + line1->edge[k+1]);+	       ++k) ;+	  break;+	case 2:+	  for (k = 0;+	       k < line1->len &&+		 line0->xMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);+	       ++k) ;+	  break;+	case 3:+	  for (k = 0;+	       k < line1->len &&+		 line0->yMax <= 0.5 * (line1->edge[k] + line1->edge[k+1]);+	       ++k) ;+	  break;+	}+	col2 = line1->col[k];+      }+      if (col2 > col1) {+	col1 = col2;+      }+    }+    for (k = 0; k <= line0->len; ++k) {+      line0->col[k] += col1;+    }+    if (line0->col[line0->len] > nColumns) {+      nColumns = line0->col[line0->len];+    }+  }+  gfree(lineArray);+}++void TextBlock::updatePriMinMax(TextBlock *blk) {+  double newPriMin, newPriMax;+  GBool gotPriMin, gotPriMax;++  gotPriMin = gotPriMax = gFalse;+  newPriMin = newPriMax = 0; // make gcc happy+  switch (page->primaryRot) {+  case 0:+  case 2:+    if (blk->yMin < yMax && blk->yMax > yMin) {+      if (blk->xMin < xMin) {+	newPriMin = blk->xMax;+	gotPriMin = gTrue;+      }+      if (blk->xMax > xMax) {+	newPriMax = blk->xMin;+	gotPriMax = gTrue;+      }+    }+    break;+  case 1:+  case 3:+    if (blk->xMin < xMax && blk->xMax > xMin) {+      if (blk->yMin < yMin) {+	newPriMin = blk->yMax;+	gotPriMin = gTrue;+      }+      if (blk->yMax > yMax) {+	newPriMax = blk->yMin;+	gotPriMax = gTrue;+      }+    }+    break;+  }+  if (gotPriMin) {+    if (newPriMin > xMin) {+      newPriMin = xMin;+    }+    if (newPriMin > priMin) {+      priMin = newPriMin;+    }+  }+  if (gotPriMax) {+    if (newPriMax < xMax) {+      newPriMax = xMax;+    }+    if (newPriMax < priMax) {+      priMax = newPriMax;+    }+  }+}++int TextBlock::cmpXYPrimaryRot(const void *p1, const void *p2) {+  TextBlock *blk1 = *(TextBlock **)p1;+  TextBlock *blk2 = *(TextBlock **)p2;+  double cmp;++  cmp = 0; // make gcc happy+  switch (blk1->page->primaryRot) {+  case 0:+    if ((cmp = blk1->xMin - blk2->xMin) == 0) {+      cmp = blk1->yMin - blk2->yMin;+    }+    break;+  case 1:+    if ((cmp = blk1->yMin - blk2->yMin) == 0) {+      cmp = blk2->xMax - blk1->xMax;+    }+    break;+  case 2:+    if ((cmp = blk2->xMax - blk1->xMax) == 0) {+      cmp = blk2->yMin - blk1->yMin;+    }+    break;+  case 3:+    if ((cmp = blk2->yMax - blk1->yMax) == 0) {+      cmp = blk1->xMax - blk2->xMax;+    }+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++int TextBlock::cmpYXPrimaryRot(const void *p1, const void *p2) {+  TextBlock *blk1 = *(TextBlock **)p1;+  TextBlock *blk2 = *(TextBlock **)p2;+  double cmp;++  cmp = 0; // make gcc happy+  switch (blk1->page->primaryRot) {+  case 0:+    if ((cmp = blk1->yMin - blk2->yMin) == 0) {+      cmp = blk1->xMin - blk2->xMin;+    }+    break;+  case 1:+    if ((cmp = blk2->xMax - blk1->xMax) == 0) {+      cmp = blk1->yMin - blk2->yMin;+    }+    break;+  case 2:+    if ((cmp = blk2->yMin - blk1->yMin) == 0) {+      cmp = blk2->xMax - blk1->xMax;+    }+    break;+  case 3:+    if ((cmp = blk1->xMax - blk2->xMax) == 0) {+      cmp = blk2->yMax - blk1->yMax;+    }+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++int TextBlock::primaryCmp(TextBlock *blk) {+  double cmp;++  cmp = 0; // make gcc happy+  switch (rot) {+  case 0:+    cmp = xMin - blk->xMin;+    break;+  case 1:+    cmp = yMin - blk->yMin;+    break;+  case 2:+    cmp = blk->xMax - xMax;+    break;+  case 3:+    cmp = blk->yMax - yMax;+    break;+  }+  return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;+}++double TextBlock::secondaryDelta(TextBlock *blk) {+  double delta;++  delta = 0; // make gcc happy+  switch (rot) {+  case 0:+    delta = blk->yMin - yMax;+    break;+  case 1:+    delta = xMin - blk->xMax;+    break;+  case 2:+    delta = yMin - blk->yMax;+    break;+  case 3:+    delta = blk->xMin - xMax;+    break;+  }+  return delta;+}++GBool TextBlock::isBelow(TextBlock *blk) {+  GBool below;++  below = gFalse; // make gcc happy+  switch (page->primaryRot) {+  case 0:+    below = xMin >= blk->priMin && xMax <= blk->priMax &&+            yMin > blk->yMin;+    break;+  case 1:+    below = yMin >= blk->priMin && yMax <= blk->priMax &&+            xMax < blk->xMax;+    break;+  case 2:+    below = xMin >= blk->priMin && xMax <= blk->priMax &&+            yMax < blk->yMax;+    break;+  case 3:+    below = yMin >= blk->priMin && yMax <= blk->priMax &&+            xMin > blk->xMin;+    break;+  }++  return below;+}++//------------------------------------------------------------------------+// TextFlow+//------------------------------------------------------------------------++TextFlow::TextFlow(TextPage *pageA, TextBlock *blk) {+  page = pageA;+  xMin = blk->xMin;+  xMax = blk->xMax;+  yMin = blk->yMin;+  yMax = blk->yMax;+  priMin = blk->priMin;+  priMax = blk->priMax;+  blocks = lastBlk = blk;+  next = NULL;+}++TextFlow::~TextFlow() {+  TextBlock *blk;++  while (blocks) {+    blk = blocks;+    blocks = blocks->next;+    delete blk;+  }+}++void TextFlow::addBlock(TextBlock *blk) {+  if (lastBlk) {+    lastBlk->next = blk;+  } else {+    blocks = blk;+  }+  lastBlk = blk;+  if (blk->xMin < xMin) {+    xMin = blk->xMin;+  }+  if (blk->xMax > xMax) {+    xMax = blk->xMax;+  }+  if (blk->yMin < yMin) {+    yMin = blk->yMin;+  }+  if (blk->yMax > yMax) {+    yMax = blk->yMax;+  }+}++GBool TextFlow::blockFits(TextBlock *blk, TextBlock *prevBlk) {+  GBool fits;++  // lower blocks must use smaller fonts+  if (blk->lines->words->fontSize > lastBlk->lines->words->fontSize) {+    return gFalse;+  }++  fits = gFalse; // make gcc happy+  switch (page->primaryRot) {+  case 0:+    fits = blk->xMin >= priMin && blk->xMax <= priMax;+    break;+  case 1:+    fits = blk->yMin >= priMin && blk->yMax <= priMax;+    break;+  case 2:+    fits = blk->xMin >= priMin && blk->xMax <= priMax;+    break;+  case 3:+    fits = blk->yMin >= priMin && blk->yMax <= priMax;+    break;+  }+  return fits;+}++#if TEXTOUT_WORD_LIST++//------------------------------------------------------------------------+// TextWordList+//------------------------------------------------------------------------++TextWordList::TextWordList(TextPage *text, GBool physLayout) {+  TextFlow *flow;+  TextBlock *blk;+  TextLine *line;+  TextWord *word;+  TextWord **wordArray;+  int nWords, i;++  words = new GList();++  if (text->rawOrder) {+    for (word = text->rawWords; word; word = word->next) {+      words->append(word);+    }++  } else if (physLayout) {+    // this is inefficient, but it's also the least useful of these+    // three cases+    nWords = 0;+    for (flow = text->flows; flow; flow = flow->next) {+      for (blk = flow->blocks; blk; blk = blk->next) {+	for (line = blk->lines; line; line = line->next) {+	  for (word = line->words; word; word = word->next) {+	    ++nWords;+	  }+	}+      }+    }+    wordArray = (TextWord **)gmallocn(nWords, sizeof(TextWord *));+    i = 0;+    for (flow = text->flows; flow; flow = flow->next) {+      for (blk = flow->blocks; blk; blk = blk->next) {+	for (line = blk->lines; line; line = line->next) {+	  for (word = line->words; word; word = word->next) {+	    wordArray[i++] = word;+	  }+	}+      }+    }+    qsort(wordArray, nWords, sizeof(TextWord *), &TextWord::cmpYX);+    for (i = 0; i < nWords; ++i) {+      words->append(wordArray[i]);+    }+    gfree(wordArray);++  } else {+    for (flow = text->flows; flow; flow = flow->next) {+      for (blk = flow->blocks; blk; blk = blk->next) {+	for (line = blk->lines; line; line = line->next) {+	  for (word = line->words; word; word = word->next) {+	    words->append(word);+	  }+	}+      }+    }+  }+}++TextWordList::~TextWordList() {+  delete words;+}++int TextWordList::getLength() {+  return words->getLength();+}++TextWord *TextWordList::get(int idx) {+  if (idx < 0 || idx >= words->getLength()) {+    return NULL;+  }+  return (TextWord *)words->get(idx);+}++#endif // TEXTOUT_WORD_LIST++//------------------------------------------------------------------------+// TextPage+//------------------------------------------------------------------------++TextPage::TextPage(GBool rawOrderA) {+  int rot;++  rawOrder = rawOrderA;+  curWord = NULL;+  charPos = 0;+  curFont = NULL;+  curFontSize = 0;+  nest = 0;+  nTinyChars = 0;+  lastCharOverlap = gFalse;+  if (!rawOrder) {+    for (rot = 0; rot < 4; ++rot) {+      pools[rot] = new TextPool();+    }+  }+  flows = NULL;+  blocks = NULL;+  rawWords = NULL;+  rawLastWord = NULL;+  fonts = new GList();+  lastFindXMin = lastFindYMin = 0;+  haveLastFind = gFalse;+  underlines = new GList();+  links = new GList();+}++TextPage::~TextPage() {+  int rot;++  clear();+  if (!rawOrder) {+    for (rot = 0; rot < 4; ++rot) {+      delete pools[rot];+    }+  }+  delete fonts;+  deleteGList(underlines, TextUnderline);+  deleteGList(links, TextLink);+}++void TextPage::startPage(GfxState *state) {+  clear();+  if (state) {+    pageWidth = state->getPageWidth();+    pageHeight = state->getPageHeight();+  } else {+    pageWidth = pageHeight = 0;+  }+}++void TextPage::endPage() {+  if (curWord) {+    endWord();+  }+}++void TextPage::clear() {+  int rot;+  TextFlow *flow;+  TextWord *word;++  if (curWord) {+    delete curWord;+    curWord = NULL;+  }+  if (rawOrder) {+    while (rawWords) {+      word = rawWords;+      rawWords = rawWords->next;+      delete word;+    }+  } else {+    for (rot = 0; rot < 4; ++rot) {+      delete pools[rot];+    }+    while (flows) {+      flow = flows;+      flows = flows->next;+      delete flow;+    }+    gfree(blocks);+  }+  deleteGList(fonts, TextFontInfo);++  curWord = NULL;+  charPos = 0;+  curFont = NULL;+  curFontSize = 0;+  nest = 0;+  nTinyChars = 0;+  if (!rawOrder) {+    for (rot = 0; rot < 4; ++rot) {+      pools[rot] = new TextPool();+    }+  }+  flows = NULL;+  blocks = NULL;+  rawWords = NULL;+  rawLastWord = NULL;+  fonts = new GList();+}++void TextPage::updateFont(GfxState *state) {+  GfxFont *gfxFont;+  double *fm;+  char *name;+  int code, mCode, letterCode, anyCode;+  double w;+  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);+  }++  // 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[1] == '\0' &&+	  ((name[0] >= 'A' && name[0] <= 'Z') ||+	   (name[0] >= 'a' && name[0] <= 'z'))) {+	letterCode = code;+      }+      if (anyCode < 0 && name &&+	  ((Gfx8BitFont *)gfxFont)->getWidth(code) > 0) {+	anyCode = code;+      }+    }+    if (mCode >= 0 &&+	(w = ((Gfx8BitFont *)gfxFont)->getWidth(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(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(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]);+    }+  }+}++void TextPage::beginWord(GfxState *state, double x0, double y0) {+  double *fontm;+  double m[4], m2[4];+  int rot;++  // This check is needed because Type 3 characters can contain+  // text-drawing operations (when TextPage is being used via+  // {X,Win}SplashOutputDev rather than TextOutputDev).+  if (curWord) {+    ++nest;+    return;+  }++  // compute the rotation+  state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);+  if (state->getFont()->getType() == fontType3) {+    fontm = state->getFont()->getFontMatrix();+    m2[0] = fontm[0] * m[0] + fontm[1] * m[2];+    m2[1] = fontm[0] * m[1] + fontm[1] * m[3];+    m2[2] = fontm[2] * m[0] + fontm[3] * m[2];+    m2[3] = fontm[2] * m[1] + fontm[3] * m[3];+    m[0] = m2[0];+    m[1] = m2[1];+    m[2] = m2[2];+    m[3] = m2[3];+  }+  if (fabs(m[0] * m[3]) > fabs(m[1] * m[2])) {+    rot = (m[3] < 0) ? 0 : 2;+  } else {+    rot = (m[2] > 0) ? 1 : 3;+  }++  curWord = new TextWord(state, rot, x0, y0, charPos, curFont, curFontSize);+}++void TextPage::addChar(GfxState *state, double x, double y,+		       double dx, double dy,+		       CharCode c, int nBytes, Unicode *u, int uLen) {+  double x1, y1, w1, h1, dx2, dy2, base, sp, delta;+  GBool overlap;+  int i;++  // 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 < 0 || x1 > pageWidth ||+      y1 + h1 < 0 || y1 > pageHeight ||+      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;+    }+  }++  // break words at space character+  if (uLen == 1 && u[0] == (Unicode)0x20) {+    if (curWord) {+      ++curWord->charLen;+    }+    charPos += nBytes;+    endWord();+    return;+  }++  // start a new word if:+  // (1) this character doesn't fall in the right place relative to+  //     the end of the previous word (this places upper and lower+  //     constraints on the position deltas along both the primary+  //     and secondary axes), or+  // (2) this character overlaps the previous one (duplicated text), or+  // (3) the previous character was an overlap (we want each duplicated+  //     character to be in a word by itself at this stage),+  // (4) the font size has changed+  if (curWord && curWord->len > 0) {+    base = sp = delta = 0; // make gcc happy+    switch (curWord->rot) {+    case 0:+      base = y1;+      sp = x1 - curWord->xMax;+      delta = x1 - curWord->edge[curWord->len - 1];+      break;+    case 1:+      base = x1;+      sp = y1 - curWord->yMax;+      delta = y1 - curWord->edge[curWord->len - 1];+      break;+    case 2:+      base = y1;+      sp = curWord->xMin - x1;+      delta = curWord->edge[curWord->len - 1] - x1;+      break;+    case 3:+      base = x1;+      sp = curWord->yMin - y1;+      delta = curWord->edge[curWord->len - 1] - y1;+      break;+    }+    overlap = fabs(delta) < dupMaxPriDelta * curWord->fontSize &&+              fabs(base - curWord->base) < dupMaxSecDelta * curWord->fontSize;+    if (overlap || lastCharOverlap ||+	sp < -minDupBreakOverlap * curWord->fontSize ||+	sp > minWordBreakSpace * curWord->fontSize ||+	fabs(base - curWord->base) > 0.5 ||+	curFontSize != curWord->fontSize) {+      endWord();+    }+    lastCharOverlap = overlap;+  } else {+    lastCharOverlap = gFalse;+  }++  if (uLen != 0) {+    // start a new word if needed+    if (!curWord) {+      beginWord(state, x, y);+    }++    // page rotation and/or transform matrices can cause text to be+    // drawn in reverse order -- in this case, swap the begin/end+    // coordinates and break text into individual chars+    if ((curWord->rot == 0 && w1 < 0) ||+	(curWord->rot == 1 && h1 < 0) ||+	(curWord->rot == 2 && w1 > 0) ||+	(curWord->rot == 3 && h1 > 0)) {+      endWord();+      beginWord(state, x + dx, y + dy);+      x1 += w1;+      y1 += h1;+      w1 = -w1;+      h1 = -h1;+    }++    // add the characters to the current word+    w1 /= uLen;+    h1 /= uLen;+    for (i = 0; i < uLen; ++i) {+      curWord->addChar(state, x1 + i*w1, y1 + i*h1, w1, h1, u[i]);+    }+  }+  if (curWord) {+    curWord->charLen += nBytes;+  }+  charPos += nBytes;+}++void TextPage::endWord() {+  // This check is needed because Type 3 characters can contain+  // text-drawing operations (when TextPage is being used via+  // {X,Win}SplashOutputDev rather than TextOutputDev).+  if (nest > 0) {+    --nest;+    return;+  }++  if (curWord) {+    addWord(curWord);+    curWord = NULL;+  }+}++void TextPage::addWord(TextWord *word) {+  // throw away zero-length words -- they don't have valid xMin/xMax+  // values, and they're useless anyway+  if (word->len == 0) {+    delete word;+    return;+  }++  if (rawOrder) {+    if (rawLastWord) {+      rawLastWord->next = word;+    } else {+      rawWords = word;+    }+    rawLastWord = word;+  } else {+    pools[word->rot]->addWord(word);+  }+}++void TextPage::addUnderline(double x0, double y0, double x1, double y1) {+  underlines->append(new TextUnderline(x0, y0, x1, y1));+}++void TextPage::addLink(int xMin, int yMin, int xMax, int yMax, Link *link) {+  links->append(new TextLink(xMin, yMin, xMax, yMax, link));+}++void TextPage::coalesce(GBool physLayout, GBool doHTML) {+  UnicodeMap *uMap;+  TextPool *pool;+  TextWord *word0, *word1, *word2;+  TextLine *line;+  TextBlock *blkList, *blkStack, *blk, *lastBlk, *blk0, *blk1;+  TextBlock **blkArray;+  TextFlow *flow, *lastFlow;+  TextUnderline *underline;+  TextLink *link;+  int rot, poolMinBaseIdx, baseIdx, startBaseIdx, endBaseIdx;+  double minBase, maxBase, newMinBase, newMaxBase;+  double fontSize, colSpace1, colSpace2, lineSpace, intraLineSpace, blkSpace;+  GBool found;+  int count[4];+  int lrCount;+  int firstBlkIdx, nBlocksLeft;+  int col1, col2;+  int i, j, n;++  if (rawOrder) {+    primaryRot = 0;+    primaryLR = gTrue;+    return;+  }++  uMap = globalParams->getTextEncoding();+  blkList = NULL;+  lastBlk = NULL;+  nBlocks = 0;+  primaryRot = -1;++  for (rot = 0; rot < 4; ++rot) {+      /* XXX */+    char buf[8];+    int n;+    pool = pools[rot];+    for (baseIdx = pool->minBaseIdx; baseIdx <= pool->maxBaseIdx; ++baseIdx) {+      for (word0 = pool->getPool(baseIdx); word0; word0 = word0->next) {+        GString *s = new GString();+	for (i = 0; i < word0->len; ++i) {+	  n = uMap->mapUnicode(word0->text[i], buf, sizeof(buf));+	  s->append(buf, n);+	}++	printf("l=%06d r=%06d u=%06d d=%06d b=%06d f=%06d r=%01d s=%04d ",+	       lrint(word0->xMin*100),+               lrint(word0->xMax*100),+               lrint(word0->yMin*100),+               lrint(word0->yMax*100),+	       lrint(word0->base*100),+               lrint(word0->fontSize*100),+               rot*90,+               s->getLength());+        fwrite(s->getCString(), 1, s->getLength(), stdout);+	printf("\n");+      }+    }+  }+  printf("\n");+  return;++#if 0 //~ for debugging+  for (i = 0; i < underlines->getLength(); ++i) {+    underline = (TextUnderline *)underlines->get(i);+    printf("underline: x=%g..%g y=%g..%g horiz=%d\n",+	   underline->x0, underline->x1, underline->y0, underline->y1,+	   underline->horiz);+  }+#endif++  if (doHTML) {++    //----- handle underlining+    for (i = 0; i < underlines->getLength(); ++i) {+      underline = (TextUnderline *)underlines->get(i);+      if (underline->horiz) {+	// rot = 0+	if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) {+	  startBaseIdx = pools[0]->getBaseIdx(underline->y0 + minUnderlineGap);+	  endBaseIdx = pools[0]->getBaseIdx(underline->y0 + maxUnderlineGap);+	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	    for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) {+	      //~ need to check the y value against the word baseline+	      if (underline->x0 < word0->xMin + underlineSlack &&+		  word0->xMax - underlineSlack < underline->x1) {+		word0->underlined = gTrue;+	      }+	    }+	  }+	}++	// rot = 2+	if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) {+	  startBaseIdx = pools[2]->getBaseIdx(underline->y0 - maxUnderlineGap);+	  endBaseIdx = pools[2]->getBaseIdx(underline->y0 - minUnderlineGap);+	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	    for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) {+	      if (underline->x0 < word0->xMin + underlineSlack &&+		  word0->xMax - underlineSlack < underline->x1) {+		word0->underlined = gTrue;+	      }+	    }+	  }+	}+      } else {+	// rot = 1+	if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) {+	  startBaseIdx = pools[1]->getBaseIdx(underline->x0 - maxUnderlineGap);+	  endBaseIdx = pools[1]->getBaseIdx(underline->x0 - minUnderlineGap);+	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	    for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) {+	      if (underline->y0 < word0->yMin + underlineSlack &&+		  word0->yMax - underlineSlack < underline->y1) {+		word0->underlined = gTrue;+	      }+	    }+	  }+	}++	// rot = 3+	if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) {+	  startBaseIdx = pools[3]->getBaseIdx(underline->x0 + minUnderlineGap);+	  endBaseIdx = pools[3]->getBaseIdx(underline->x0 + maxUnderlineGap);+	  for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	    for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) {+	      if (underline->y0 < word0->yMin + underlineSlack &&+		  word0->yMax - underlineSlack < underline->y1) {+		word0->underlined = gTrue;+	      }+	    }+	  }+	}+      }+    }++    //----- handle links+    for (i = 0; i < links->getLength(); ++i) {+      link = (TextLink *)links->get(i);++      // rot = 0+      if (pools[0]->minBaseIdx <= pools[0]->maxBaseIdx) {+	startBaseIdx = pools[0]->getBaseIdx(link->yMin);+	endBaseIdx = pools[0]->getBaseIdx(link->yMax);+	for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	  for (word0 = pools[0]->getPool(j); word0; word0 = word0->next) {+	    if (link->xMin < word0->xMin + hyperlinkSlack &&+		word0->xMax - hyperlinkSlack < link->xMax &&+		link->yMin < word0->yMin + hyperlinkSlack &&+		word0->yMax - hyperlinkSlack < link->yMax) {+	      word0->link = link->link;+	    }+	  }+	}+      }++      // rot = 2+      if (pools[2]->minBaseIdx <= pools[2]->maxBaseIdx) {+	startBaseIdx = pools[2]->getBaseIdx(link->yMin);+	endBaseIdx = pools[2]->getBaseIdx(link->yMax);+	for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	  for (word0 = pools[2]->getPool(j); word0; word0 = word0->next) {+	    if (link->xMin < word0->xMin + hyperlinkSlack &&+		word0->xMax - hyperlinkSlack < link->xMax &&+		link->yMin < word0->yMin + hyperlinkSlack &&+		word0->yMax - hyperlinkSlack < link->yMax) {+	      word0->link = link->link;+	    }+	  }+	}+      }++      // rot = 1+      if (pools[1]->minBaseIdx <= pools[1]->maxBaseIdx) {+	startBaseIdx = pools[1]->getBaseIdx(link->xMin);+	endBaseIdx = pools[1]->getBaseIdx(link->xMax);+	for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	  for (word0 = pools[1]->getPool(j); word0; word0 = word0->next) {+	    if (link->yMin < word0->yMin + hyperlinkSlack &&+		word0->yMax - hyperlinkSlack < link->yMax &&+		link->xMin < word0->xMin + hyperlinkSlack &&+		word0->xMax - hyperlinkSlack < link->xMax) {+	      word0->link = link->link;+	    }+	  }+	}+      }++      // rot = 3+      if (pools[3]->minBaseIdx <= pools[3]->maxBaseIdx) {+	startBaseIdx = pools[3]->getBaseIdx(link->xMin);+	endBaseIdx = pools[3]->getBaseIdx(link->xMax);+	for (j = startBaseIdx; j <= endBaseIdx; ++j) {+	  for (word0 = pools[3]->getPool(j); word0; word0 = word0->next) {+	    if (link->yMin < word0->yMin + hyperlinkSlack &&+		word0->yMax - hyperlinkSlack < link->yMax &&+		link->xMin < word0->xMin + hyperlinkSlack &&+		word0->xMax - hyperlinkSlack < link->xMax) {+	      word0->link = link->link;+	    }+	  }+	}+      }+    }+  }++  //----- assemble the blocks++  //~ add an outer loop for writing mode (vertical text)++  // build blocks for each rotation value+  for (rot = 0; rot < 4; ++rot) {+    pool = pools[rot];+    poolMinBaseIdx = pool->minBaseIdx;+    count[rot] = 0;++    // add blocks until no more words are left+    while (1) {++      // find the first non-empty line in the pool+      for (;+	   poolMinBaseIdx <= pool->maxBaseIdx &&+	     !pool->getPool(poolMinBaseIdx);+	   ++poolMinBaseIdx) ;+      if (poolMinBaseIdx > pool->maxBaseIdx) {+	break;+      }++      // look for the left-most word in the first four lines of the+      // pool -- this avoids starting with a superscript word+      startBaseIdx = poolMinBaseIdx;+      for (baseIdx = poolMinBaseIdx + 1;+	   baseIdx < poolMinBaseIdx + 4 && baseIdx <= pool->maxBaseIdx;+	   ++baseIdx) {+	if (!pool->getPool(baseIdx)) {+	  continue;+	}+	if (pool->getPool(baseIdx)->primaryCmp(pool->getPool(startBaseIdx))+	    < 0) {+	  startBaseIdx = baseIdx;+	}+      }++      // create a new block+      word0 = pool->getPool(startBaseIdx);+      pool->setPool(startBaseIdx, word0->next);+      word0->next = NULL;+      blk = new TextBlock(this, rot);+      blk->addWord(word0);++      fontSize = word0->fontSize;+      minBase = maxBase = word0->base;+      colSpace1 = minColSpacing1 * fontSize;+      colSpace2 = minColSpacing2 * fontSize;+      lineSpace = maxLineSpacingDelta * fontSize;+      intraLineSpace = maxIntraLineDelta * fontSize;++      // add words to the block+      do {+	found = gFalse;++	// look for words on the line above the current top edge of+	// the block+	newMinBase = minBase;+	for (baseIdx = pool->getBaseIdx(minBase);+	     baseIdx >= pool->getBaseIdx(minBase - lineSpace);+	     --baseIdx) {+	  word0 = NULL;+	  word1 = pool->getPool(baseIdx);+	  while (word1) {+	    if (word1->base < minBase &&+		word1->base >= minBase - lineSpace &&+		((rot == 0 || rot == 2)+		 ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)+		 : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&+		fabs(word1->fontSize - fontSize) <+		  maxBlockFontSizeDelta1 * fontSize) {+	      word2 = word1;+	      if (word0) {+		word0->next = word1->next;+	      } else {+		pool->setPool(baseIdx, word1->next);+	      }+	      word1 = word1->next;+	      word2->next = NULL;+	      blk->addWord(word2);+	      found = gTrue;+	      newMinBase = word2->base;+	    } else {+	      word0 = word1;+	      word1 = word1->next;+	    }+	  }+	}+	minBase = newMinBase;++	// look for words on the line below the current bottom edge of+	// the block+	newMaxBase = maxBase;+	for (baseIdx = pool->getBaseIdx(maxBase);+	     baseIdx <= pool->getBaseIdx(maxBase + lineSpace);+	     ++baseIdx) {+	  word0 = NULL;+	  word1 = pool->getPool(baseIdx);+	  while (word1) {+	    if (word1->base > maxBase &&+		word1->base <= maxBase + lineSpace &&+		((rot == 0 || rot == 2)+		 ? (word1->xMin < blk->xMax && word1->xMax > blk->xMin)+		 : (word1->yMin < blk->yMax && word1->yMax > blk->yMin)) &&+		fabs(word1->fontSize - fontSize) <+		  maxBlockFontSizeDelta1 * fontSize) {+	      word2 = word1;+	      if (word0) {+		word0->next = word1->next;+	      } else {+		pool->setPool(baseIdx, word1->next);+	      }+	      word1 = word1->next;+	      word2->next = NULL;+	      blk->addWord(word2);+	      found = gTrue;+	      newMaxBase = word2->base;+	    } else {+	      word0 = word1;+	      word1 = word1->next;+	    }+	  }+	}+	maxBase = newMaxBase;++	// look for words that are on lines already in the block, and+	// that overlap the block horizontally+	for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);+	     baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);+	     ++baseIdx) {+	  word0 = NULL;+	  word1 = pool->getPool(baseIdx);+	  while (word1) {+	    if (word1->base >= minBase - intraLineSpace &&+		word1->base <= maxBase + intraLineSpace &&+		((rot == 0 || rot == 2)+		 ? (word1->xMin < blk->xMax + colSpace1 &&+		    word1->xMax > blk->xMin - colSpace1)+		 : (word1->yMin < blk->yMax + colSpace1 &&+		    word1->yMax > blk->yMin - colSpace1)) &&+		fabs(word1->fontSize - fontSize) <+		  maxBlockFontSizeDelta2 * fontSize) {+	      word2 = word1;+	      if (word0) {+		word0->next = word1->next;+	      } else {+		pool->setPool(baseIdx, word1->next);+	      }+	      word1 = word1->next;+	      word2->next = NULL;+	      blk->addWord(word2);+	      found = gTrue;+	    } else {+	      word0 = word1;+	      word1 = word1->next;+	    }+	  }+	}++	// only check for outlying words (the next two chunks of code)+	// if we didn't find anything else+	if (found) {+	  continue;+	}++	// scan down the left side of the block, looking for words+	// that are near (but not overlapping) the block; if there are+	// three or fewer, add them to the block+	n = 0;+	for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);+	     baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);+	     ++baseIdx) {+	  word1 = pool->getPool(baseIdx);+	  while (word1) {+	    if (word1->base >= minBase - intraLineSpace &&+		word1->base <= maxBase + intraLineSpace &&+		((rot == 0 || rot == 2)+		 ? (word1->xMax <= blk->xMin &&+		    word1->xMax > blk->xMin - colSpace2)+		 : (word1->yMax <= blk->yMin &&+		    word1->yMax > blk->yMin - colSpace2)) &&+		fabs(word1->fontSize - fontSize) <+		  maxBlockFontSizeDelta3 * fontSize) {+	      ++n;+	      break;+	    }+	    word1 = word1->next;+	  }+	}+	if (n > 0 && n <= 3) {+	  for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);+	       baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);+	       ++baseIdx) {+	    word0 = NULL;+	    word1 = pool->getPool(baseIdx);+	    while (word1) {+	      if (word1->base >= minBase - intraLineSpace &&+		  word1->base <= maxBase + intraLineSpace &&+		  ((rot == 0 || rot == 2)+		   ? (word1->xMax <= blk->xMin &&+		      word1->xMax > blk->xMin - colSpace2)+		   : (word1->yMax <= blk->yMin &&+		      word1->yMax > blk->yMin - colSpace2)) &&+		  fabs(word1->fontSize - fontSize) <+		    maxBlockFontSizeDelta3 * fontSize) {+		word2 = word1;+		if (word0) {+		  word0->next = word1->next;+		} else {+		  pool->setPool(baseIdx, word1->next);+		}+		word1 = word1->next;+		word2->next = NULL;+		blk->addWord(word2);+		if (word2->base < minBase) {+		  minBase = word2->base;+		} else if (word2->base > maxBase) {+		  maxBase = word2->base;+		}+		found = gTrue;+		break;+	      } else {+		word0 = word1;+		word1 = word1->next;+	      }+	    }+	  }+	}++	// scan down the right side of the block, looking for words+	// that are near (but not overlapping) the block; if there are+	// three or fewer, add them to the block+	n = 0;+	for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);+	     baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);+	     ++baseIdx) {+	  word1 = pool->getPool(baseIdx);+	  while (word1) {+	    if (word1->base >= minBase - intraLineSpace &&+		word1->base <= maxBase + intraLineSpace &&+		((rot == 0 || rot == 2)+		 ? (word1->xMin >= blk->xMax &&+		    word1->xMin < blk->xMax + colSpace2)+		 : (word1->yMin >= blk->yMax &&+		    word1->yMin < blk->yMax + colSpace2)) &&+		fabs(word1->fontSize - fontSize) <+		  maxBlockFontSizeDelta3 * fontSize) {+	      ++n;+	      break;+	    }+	    word1 = word1->next;+	  }+	}+	if (n > 0 && n <= 3) {+	  for (baseIdx = pool->getBaseIdx(minBase - intraLineSpace);+	       baseIdx <= pool->getBaseIdx(maxBase + intraLineSpace);+	       ++baseIdx) {+	    word0 = NULL;+	    word1 = pool->getPool(baseIdx);+	    while (word1) {+	      if (word1->base >= minBase - intraLineSpace &&+		  word1->base <= maxBase + intraLineSpace &&+		  ((rot == 0 || rot == 2)+		   ? (word1->xMin >= blk->xMax &&+		      word1->xMin < blk->xMax + colSpace2)+		   : (word1->yMin >= blk->yMax &&+		      word1->yMin < blk->yMax + colSpace2)) &&+		  fabs(word1->fontSize - fontSize) <+		    maxBlockFontSizeDelta3 * fontSize) {+		word2 = word1;+		if (word0) {+		  word0->next = word1->next;+		} else {+		  pool->setPool(baseIdx, word1->next);+		}+		word1 = word1->next;+		word2->next = NULL;+		blk->addWord(word2);+		if (word2->base < minBase) {+		  minBase = word2->base;+		} else if (word2->base > maxBase) {+		  maxBase = word2->base;+		}+		found = gTrue;+		break;+	      } else {+		word0 = word1;+		word1 = word1->next;+	      }+	    }+	  }+	}++      } while (found);++      //~ need to compute the primary writing mode (horiz/vert) in+      //~ addition to primary rotation++      // coalesce the block, and add it to the list+      blk->coalesce(uMap);+      if (lastBlk) {+	lastBlk->next = blk;+      } else {+	blkList = blk;+      }+      lastBlk = blk;+      count[rot] += blk->charCount;+      if (primaryRot < 0 || count[rot] > count[primaryRot]) {+	primaryRot = rot;+      }+      ++nBlocks;+    }+  }++#if 0 // for debugging +  printf("*** rotation ***\n");+  for (rot = 0; rot < 4; ++rot) {+    printf("  %d: %6d\n", rot, count[rot]);+  }+  printf("  primary rot = %d\n", primaryRot);+  printf("\n");+#endif++#if 0 // for debugging+  printf("*** blocks ***\n");+  for (blk = blkList; blk; blk = blk->next) {+    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f\n",+	   blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax);+    for (line = blk->lines; line; line = line->next) {+      printf("  line: x=%.2f..%.2f y=%.2f..%.2f base=%.2f\n",+	     line->xMin, line->xMax, line->yMin, line->yMax, line->base);+      for (word0 = line->words; word0; word0 = word0->next) {+	printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",+	       word0->xMin, word0->xMax, word0->yMin, word0->yMax,+	       word0->base, word0->fontSize, word0->spaceAfter);+	for (i = 0; i < word0->len; ++i) {+	  fputc(word0->text[i] & 0xff, stdout);+	}+	printf("'\n");+      }+    }+  }+  printf("\n");+#endif++  // determine the primary direction+  lrCount = 0;+  for (blk = blkList; blk; blk = blk->next) {+    for (line = blk->lines; line; line = line->next) {+      for (word0 = line->words; word0; word0 = word0->next) {+	for (i = 0; i < word0->len; ++i) {+	  if (unicodeTypeL(word0->text[i])) {+	    ++lrCount;+	  } else if (unicodeTypeR(word0->text[i])) {+	    --lrCount;+	  }+	}+      }+    }+  }+  primaryLR = lrCount >= 0;++#if 0 // for debugging+  printf("*** direction ***\n");+  printf("lrCount = %d\n", lrCount);+  printf("primaryLR = %d\n", primaryLR);+#endif++  //----- column assignment++  // sort blocks into xy order for column assignment+  blocks = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));+  for (blk = blkList, i = 0; blk; blk = blk->next, ++i) {+    blocks[i] = blk;+  }+  qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpXYPrimaryRot);++  // column assignment+  for (i = 0; i < nBlocks; ++i) {+    blk0 = blocks[i];+    col1 = 0;+    for (j = 0; j < i; ++j) {+      blk1 = blocks[j];+      col2 = 0; // make gcc happy+      switch (primaryRot) {+      case 0:+	if (blk0->xMin > blk1->xMax) {+	  col2 = blk1->col + blk1->nColumns + 3;+	} else if (blk1->xMax == blk1->xMin) {+	  col2 = blk1->col;+	} else {+	  col2 = blk1->col + (int)(((blk0->xMin - blk1->xMin) /+				    (blk1->xMax - blk1->xMin)) *+				   blk1->nColumns);+	}+	break;+      case 1:+	if (blk0->yMin > blk1->yMax) {+	  col2 = blk1->col + blk1->nColumns + 3;+	} else if (blk1->yMax == blk1->yMin) {+	  col2 = blk1->col;+	} else {+	  col2 = blk1->col + (int)(((blk0->yMin - blk1->yMin) /+				    (blk1->yMax - blk1->yMin)) *+				   blk1->nColumns);+	}+	break;+      case 2:+	if (blk0->xMax < blk1->xMin) {+	  col2 = blk1->col + blk1->nColumns + 3;+	} else if (blk1->xMin == blk1->xMax) {+	  col2 = blk1->col;+	} else {+	  col2 = blk1->col + (int)(((blk0->xMax - blk1->xMax) /+				    (blk1->xMin - blk1->xMax)) *+				   blk1->nColumns);+	}+	break;+      case 3:+	if (blk0->yMax < blk1->yMin) {+	  col2 = blk1->col + blk1->nColumns + 3;+	} else if (blk1->yMin == blk1->yMax) {+	  col2 = blk1->col;+	} else {+	  col2 = blk1->col + (int)(((blk0->yMax - blk1->yMax) /+				    (blk1->yMin - blk1->yMax)) *+				   blk1->nColumns);+	}+	break;+      }+      if (col2 > col1) {+	col1 = col2;+      }+    }+    blk0->col = col1;+    for (line = blk0->lines; line; line = line->next) {+      for (j = 0; j <= line->len; ++j) {+	line->col[j] += col1;+      }+    }+  }++#if 0 // for debugging+  printf("*** blocks, after column assignment ***\n");+  for (blk = blkList; blk; blk = blk->next) {+    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f col=%d nCols=%d\n",+	   blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax, blk->col,+	   blk->nColumns);+    for (line = blk->lines; line; line = line->next) {+      printf("  line:\n");+      for (word0 = line->words; word0; word0 = word0->next) {+	printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",+	       word0->xMin, word0->xMax, word0->yMin, word0->yMax,+	       word0->base, word0->fontSize, word0->spaceAfter);+	for (i = 0; i < word0->len; ++i) {+	  fputc(word0->text[i] & 0xff, stdout);+	}+	printf("'\n");+      }+    }+  }+  printf("\n");+#endif++  //----- reading order sort++  // sort blocks into yx order (in preparation for reading order sort)+  qsort(blocks, nBlocks, sizeof(TextBlock *), &TextBlock::cmpYXPrimaryRot);++  // compute space on left and right sides of each block+  for (i = 0; i < nBlocks; ++i) {+    blk0 = blocks[i];+    for (j = 0; j < nBlocks; ++j) {+      blk1 = blocks[j];+      if (blk1 != blk0) {+	blk0->updatePriMinMax(blk1);+      }+    }+  }++#if 0 // for debugging+  printf("*** blocks, after yx sort ***\n");+  for (i = 0; i < nBlocks; ++i) {+    blk = blocks[i];+    printf("block: rot=%d x=%.2f..%.2f y=%.2f..%.2f space=%.2f..%.2f\n",+	   blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,+	   blk->priMin, blk->priMax);+    for (line = blk->lines; line; line = line->next) {+      printf("  line:\n");+      for (word0 = line->words; word0; word0 = word0->next) {+	printf("    word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",+	       word0->xMin, word0->xMax, word0->yMin, word0->yMax,+	       word0->base, word0->fontSize, word0->spaceAfter);+	for (j = 0; j < word0->len; ++j) {+	  fputc(word0->text[j] & 0xff, stdout);+	}+	printf("'\n");+      }+    }+  }+  printf("\n");+#endif++  // build the flows+  //~ this needs to be adjusted for writing mode (vertical text)+  //~ this also needs to account for right-to-left column ordering+  blkArray = (TextBlock **)gmallocn(nBlocks, sizeof(TextBlock *));+  memcpy(blkArray, blocks, nBlocks * sizeof(TextBlock *));+  flows = lastFlow = NULL;+  firstBlkIdx = 0;+  nBlocksLeft = nBlocks;+  while (nBlocksLeft > 0) {++    // find the upper-left-most block+    for (; !blkArray[firstBlkIdx]; ++firstBlkIdx) ;+    i = firstBlkIdx;+    blk = blkArray[i];+    for (j = firstBlkIdx + 1; j < nBlocks; ++j) {+      blk1 = blkArray[j];+      if (blk1) {+	if (blk && blk->secondaryDelta(blk1) > 0) {+	  break;+	}+	if (blk1->primaryCmp(blk) < 0) {+	  i = j;+	  blk = blk1;+	}+      }+    }+    blkArray[i] = NULL;+    --nBlocksLeft;+    blk->next = NULL;++    // create a new flow, starting with the upper-left-most block+    flow = new TextFlow(this, blk);+    if (lastFlow) {+      lastFlow->next = flow;+    } else {+      flows = flow;+    }+    lastFlow = flow;+    fontSize = blk->lines->words->fontSize;++    // push the upper-left-most block on the stack+    blk->stackNext = NULL;+    blkStack = blk;++    // find the other blocks in this flow+    while (blkStack) {++      // find the upper-left-most block under (but within+      // maxBlockSpacing of) the top block on the stack+      blkSpace = maxBlockSpacing * blkStack->lines->words->fontSize;+      blk = NULL;+      i = -1;+      for (j = firstBlkIdx; j < nBlocks; ++j) {+	blk1 = blkArray[j];+	if (blk1) {+	  if (blkStack->secondaryDelta(blk1) > blkSpace) {+	    break;+	  }+	  if (blk && blk->secondaryDelta(blk1) > 0) {+	    break;+	  }+	  if (blk1->isBelow(blkStack) &&+	      (!blk || blk1->primaryCmp(blk) < 0)) {+	    i = j;+	    blk = blk1;+	  }+	}+      }++      // if a suitable block was found, add it to the flow and push it+      // onto the stack+      if (blk && flow->blockFits(blk, blkStack)) {+	blkArray[i] = NULL;+	--nBlocksLeft;+	blk->next = NULL;+	flow->addBlock(blk);+	fontSize = blk->lines->words->fontSize;+	blk->stackNext = blkStack;+	blkStack = blk;++      // otherwise (if there is no block under the top block or the+      // block is not suitable), pop the stack+      } else {+	blkStack = blkStack->stackNext;+      }+    }+  }+  gfree(blkArray);++#if 0 // for debugging+  printf("*** flows ***\n");+  for (flow = flows; flow; flow = flow->next) {+    printf("flow: x=%.2f..%.2f y=%.2f..%.2f pri:%.2f..%.2f\n",+	   flow->xMin, flow->xMax, flow->yMin, flow->yMax,+	   flow->priMin, flow->priMax);+    for (blk = flow->blocks; blk; blk = blk->next) {+      printf("  block: rot=%d x=%.2f..%.2f y=%.2f..%.2f pri=%.2f..%.2f\n",+	     blk->rot, blk->xMin, blk->xMax, blk->yMin, blk->yMax,+	     blk->priMin, blk->priMax);+      for (line = blk->lines; line; line = line->next) {+	printf("    line:\n");+	for (word0 = line->words; word0; word0 = word0->next) {+	  printf("      word: x=%.2f..%.2f y=%.2f..%.2f base=%.2f fontSize=%.2f space=%d: '",+		 word0->xMin, word0->xMax, word0->yMin, word0->yMax,+		 word0->base, word0->fontSize, word0->spaceAfter);+	  for (i = 0; i < word0->len; ++i) {+	    fputc(word0->text[i] & 0xff, stdout);+	  }+	  printf("'\n");+	}+      }+    }+  }+  printf("\n");+#endif++  if (uMap) {+    uMap->decRefCnt();+  }+}++GBool TextPage::findText(Unicode *s, int len,+			 GBool startAtTop, GBool stopAtBottom,+			 GBool startAtLast, GBool stopAtLast,+			 GBool caseSensitive, GBool backward,+			 double *xMin, double *yMin,+			 double *xMax, double *yMax) {+  TextBlock *blk;+  TextLine *line;+  Unicode *s2, *txt;+  Unicode *p;+  int txtSize, m, i, j, k;+  double xStart, yStart, xStop, yStop;+  double xMin0, yMin0, xMax0, yMax0;+  double xMin1, yMin1, xMax1, yMax1;+  GBool found;++  //~ needs to handle right-to-left text++  if (rawOrder) {+    return gFalse;+  }++  // 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 (i = backward ? nBlocks - 1 : 0;+       backward ? i >= 0 : i < nBlocks;+       i += backward ? -1 : 1) {+    blk = blocks[i];++    // check: is the block above the top limit?+    if (!startAtTop && (backward ? blk->yMin > yStart : blk->yMax < yStart)) {+      continue;+    }++    // check: is the block below the bottom limit?+    if (!stopAtBottom && (backward ? blk->yMax < yStop : blk->yMin > yStop)) {+      break;+    }++    for (line = blk->lines; line; line = line->next) {++      // check: is the line above the top limit?+      if (!startAtTop &&+	  (backward ? line->yMin > yStart : line->yMin < yStart)) {+	continue;+      }++      // check: is the line below the bottom limit?+      if (!stopAtBottom &&+	  (backward ? line->yMin < 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) {++	// 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) {+  GString *s;+  UnicodeMap *uMap;+  GBool isUnicode;+  TextBlock *blk;+  TextLine *line;+  TextLineFrag *frags;+  int nFrags, fragsSize;+  TextLineFrag *frag;+  char space[8], eol[16];+  int spaceLen, eolLen;+  int lastRot;+  double x, y, delta;+  int col, idx0, idx1, i, j;+  GBool multiLine, oneRot;++  s = new GString();++  if (rawOrder) {+    return s;+  }++  // get the output encoding+  if (!(uMap = globalParams->getTextEncoding())) {+    return s;+  }+  isUnicode = uMap->isUnicode();+  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, sizeof(eol) - eolLen);+    break;+  case eolMac:+    eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));+    break;+  }++  //~ writing mode (horiz/vert)++  // collect the line fragments that are in the rectangle+  fragsSize = 256;+  frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag));+  nFrags = 0;+  lastRot = -1;+  oneRot = gTrue;+  for (i = 0; i < nBlocks; ++i) {+    blk = blocks[i];+    if (xMin < blk->xMax && blk->xMin < xMax &&+	yMin < blk->yMax && blk->yMin < yMax) {+      for (line = blk->lines; line; line = line->next) {+	if (xMin < line->xMax && line->xMin < xMax &&+	    yMin < line->yMax && line->yMin < yMax) {+	  idx0 = idx1 = -1;+	  switch (line->rot) {+	  case 0:+	    y = 0.5 * (line->yMin + line->yMax);+	    if (yMin < y && y < yMax) {+	      j = 0;+	      while (j < line->len) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {+		  idx0 = j;+		  break;+		}+		++j;+	      }+	      j = line->len - 1;+	      while (j >= 0) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {+		  idx1 = j;+		  break;+		}+		--j;+	      }+	    }+	    break;+	  case 1:+	    x = 0.5 * (line->xMin + line->xMax);+	    if (xMin < x && x < xMax) {+	      j = 0;+	      while (j < line->len) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {+		  idx0 = j;+		  break;+		}+		++j;+	      }+	      j = line->len - 1;+	      while (j >= 0) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {+		  idx1 = j;+		  break;+		}+		--j;+	      }+	    }+	    break;+	  case 2:+	    y = 0.5 * (line->yMin + line->yMax);+	    if (yMin < y && y < yMax) {+	      j = 0;+	      while (j < line->len) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) < xMax) {+		  idx0 = j;+		  break;+		}+		++j;+	      }+	      j = line->len - 1;+	      while (j >= 0) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) > xMin) {+		  idx1 = j;+		  break;+		}+		--j;+	      }+	    }+	    break;+	  case 3:+	    x = 0.5 * (line->xMin + line->xMax);+	    if (xMin < x && x < xMax) {+	      j = 0;+	      while (j < line->len) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) < yMax) {+		  idx0 = j;+		  break;+		}+		++j;+	      }+	      j = line->len - 1;+	      while (j >= 0) {+		if (0.5 * (line->edge[j] + line->edge[j+1]) > yMin) {+		  idx1 = j;+		  break;+		}+		--j;+	      }+	    }+	    break;+	  }+	  if (idx0 >= 0 && idx1 >= 0) {+	    if (nFrags == fragsSize) {+	      fragsSize *= 2;+	      frags = (TextLineFrag *)+		          greallocn(frags, fragsSize, sizeof(TextLineFrag));+	    }+	    frags[nFrags].init(line, idx0, idx1 - idx0 + 1);+	    ++nFrags;+	    if (lastRot >= 0 && line->rot != lastRot) {+	      oneRot = gFalse;+	    }+	    lastRot = line->rot;+	  }+	}+      }+    }+  }++  // sort the fragments and generate the string+  if (nFrags > 0) {++    for (i = 0; i < nFrags; ++i) {+      frags[i].computeCoords(oneRot);+    }+    assignColumns(frags, nFrags, oneRot);++    // if all lines in the region have the same rotation, use it;+    // otherwise, use the page's primary rotation+    if (oneRot) {+      qsort(frags, nFrags, sizeof(TextLineFrag),+	    &TextLineFrag::cmpYXLineRot);+    } else {+      qsort(frags, nFrags, sizeof(TextLineFrag),+	    &TextLineFrag::cmpYXPrimaryRot);+    }+    i = 0;+    while (i < nFrags) {+      delta = maxIntraLineDelta * frags[i].line->words->fontSize;+      for (j = i+1;+	   j < nFrags && fabs(frags[j].base - frags[i].base) < delta;+	   ++j) ;+      qsort(frags + i, j - i, sizeof(TextLineFrag),+	    oneRot ? &TextLineFrag::cmpXYColumnLineRot+	           : &TextLineFrag::cmpXYColumnPrimaryRot);+      i = j;+    }++    col = 0;+    multiLine = gFalse;+    for (i = 0; i < nFrags; ++i) {+      frag = &frags[i];++      // insert a return+      if (frag->col < col ||+	  (i > 0 && fabs(frag->base - frags[i-1].base) >+	              maxIntraLineDelta * frags[i-1].line->words->fontSize)) {+	s->append(eol, eolLen);+	col = 0;+	multiLine = gTrue;+      }++      // column alignment+      for (; col < frag->col; ++col) {+	s->append(space, spaceLen);+      }++      // get the fragment text+      col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);+    }++    if (multiLine) {+      s->append(eol, eolLen);+    }+  }++  gfree(frags);+  uMap->decRefCnt();++  return s;+}++GBool TextPage::findCharRange(int pos, int length,+			      double *xMin, double *yMin,+			      double *xMax, double *yMax) {+  TextBlock *blk;+  TextLine *line;+  TextWord *word;+  double xMin0, xMax0, yMin0, yMax0;+  double xMin1, xMax1, yMin1, yMax1;+  GBool first;+  int i, j0, j1;++  if (rawOrder) {+    return gFalse;+  }++  //~ this doesn't correctly handle:+  //~ - ranges split across multiple lines (the highlighted region+  //~   is the bounding box of all the parts of the range)+  //~ - cases where characters don't convert one-to-one into Unicode+  first = gTrue;+  xMin0 = xMax0 = yMin0 = yMax0 = 0; // make gcc happy+  xMin1 = xMax1 = yMin1 = yMax1 = 0; // make gcc happy+  for (i = 0; i < nBlocks; ++i) {+    blk = blocks[i];+    for (line = blk->lines; line; line = line->next) {+      for (word = line->words; word; word = word->next) {+	if (pos < word->charPos + word->charLen &&+	    word->charPos < pos + length) {+	  j0 = pos - word->charPos;+	  if (j0 < 0) {+	    j0 = 0;+	  }+	  j1 = pos + length - 1 - word->charPos;+	  if (j1 >= word->len) {+	    j1 = word->len - 1;+	  }+	  switch (line->rot) {+	  case 0:+	    xMin1 = word->edge[j0];+	    xMax1 = word->edge[j1 + 1];+	    yMin1 = word->yMin;+	    yMax1 = word->yMax;+	    break;+	  case 1:+	    xMin1 = word->xMin;+	    xMax1 = word->xMax;+	    yMin1 = word->edge[j0];+	    yMax1 = word->edge[j1 + 1];+	    break;+	  case 2:+	    xMin1 = word->edge[j1 + 1];+	    xMax1 = word->edge[j0];+	    yMin1 = word->yMin;+	    yMax1 = word->yMax;+	    break;+	  case 3:+	    xMin1 = word->xMin;+	    xMax1 = word->xMax;+	    yMin1 = word->edge[j1 + 1];+	    yMax1 = word->edge[j0];+	    break;+	  }+	  if (first || xMin1 < xMin0) {+	    xMin0 = xMin1;+	  }+	  if (first || xMax1 > xMax0) {+	    xMax0 = xMax1;+	  }+	  if (first || yMin1 < yMin0) {+	    yMin0 = yMin1;+	  }+	  if (first || yMax1 > yMax0) {+	    yMax0 = yMax1;+	  }+	  first = gFalse;+	}+      }+    }+  }+  if (!first) {+    *xMin = xMin0;+    *xMax = xMax0;+    *yMin = yMin0;+    *yMax = yMax0;+    return gTrue;+  }+  return gFalse;+}++void TextPage::dump(void *outputStream, TextOutputFunc outputFunc,+		    GBool physLayout) {+  UnicodeMap *uMap;+  TextFlow *flow;+  TextBlock *blk;+  TextLine *line;+  TextLineFrag *frags;+  TextWord *word;+  int nFrags, fragsSize;+  TextLineFrag *frag;+  char space[8], eol[16], eop[8];+  int spaceLen, eolLen, eopLen;+  GBool pageBreaks;+  GString *s;+  double delta;+  int col, i, j, d, n;++  // 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, sizeof(eol) - eolLen);+    break;+  case eolMac:+    eolLen = uMap->mapUnicode(0x0d, eol, sizeof(eol));+    break;+  }+  eopLen = uMap->mapUnicode(0x0c, eop, sizeof(eop));+  pageBreaks = globalParams->getTextPageBreaks();++  //~ writing mode (horiz/vert)++  // output the page in raw (content stream) order+  if (rawOrder) {++    for (word = rawWords; word; word = word->next) {+      s = new GString();+      dumpFragment(word->text, word->len, uMap, s);+      (*outputFunc)(outputStream, s->getCString(), s->getLength());+      delete s;+      if (word->next &&+	  fabs(word->next->base - word->base) <+	    maxIntraLineDelta * word->fontSize) {+	if (word->next->xMin > word->xMax + minWordSpacing * word->fontSize) {+	  (*outputFunc)(outputStream, space, spaceLen);+	}+      } else {+	(*outputFunc)(outputStream, eol, eolLen);+      }+    }++  // output the page, maintaining the original physical layout+  } else if (physLayout) {++    // collect the line fragments for the page and sort them+    fragsSize = 256;+    frags = (TextLineFrag *)gmallocn(fragsSize, sizeof(TextLineFrag));+    nFrags = 0;+    for (i = 0; i < nBlocks; ++i) {+      blk = blocks[i];+      for (line = blk->lines; line; line = line->next) {+	if (nFrags == fragsSize) {+	  fragsSize *= 2;+	  frags = (TextLineFrag *)greallocn(frags,+					    fragsSize, sizeof(TextLineFrag));+	}+	frags[nFrags].init(line, 0, line->len);+	frags[nFrags].computeCoords(gTrue);+	++nFrags;+      }+    }+    qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpYXPrimaryRot);+    i = 0;+    while (i < nFrags) {+      delta = maxIntraLineDelta * frags[i].line->words->fontSize;+      for (j = i+1;+	   j < nFrags && fabs(frags[j].base - frags[i].base) < delta;+	   ++j) ;+      qsort(frags + i, j - i, sizeof(TextLineFrag),+	    &TextLineFrag::cmpXYColumnPrimaryRot);+      i = j;+    }++#if 0 // for debugging+    printf("*** line fragments ***\n");+    for (i = 0; i < nFrags; ++i) {+      frag = &frags[i];+      printf("frag: x=%.2f..%.2f y=%.2f..%.2f base=%.2f '",+	     frag->xMin, frag->xMax, frag->yMin, frag->yMax, frag->base);+      for (n = 0; n < frag->len; ++n) {+	fputc(frag->line->text[frag->start + n] & 0xff, stdout);+      }+      printf("'\n");+    }+    printf("\n");+#endif++    // generate output+    col = 0;+    for (i = 0; i < nFrags; ++i) {+      frag = &frags[i];++      // column alignment+      for (; col < frag->col; ++col) {+	(*outputFunc)(outputStream, space, spaceLen);+      }++      // print the line+      s = new GString();+      col += dumpFragment(frag->line->text + frag->start, frag->len, uMap, s);+      (*outputFunc)(outputStream, s->getCString(), s->getLength());+      delete s;++      // print one or more returns if necessary+      if (i == nFrags - 1 ||+	  frags[i+1].col < col ||+	  fabs(frags[i+1].base - frag->base) >+	    maxIntraLineDelta * frag->line->words->fontSize) {+	if (i < nFrags - 1) {+	  d = (int)((frags[i+1].base - frag->base) /+		    frag->line->words->fontSize);+	  if (d < 1) {+	    d = 1;+	  } else if (d > 5) {+	    d = 5;+	  }+	} else {+	  d = 1;+	}+	for (; d > 0; --d) {+	  (*outputFunc)(outputStream, eol, eolLen);+	}+	col = 0;+      }+    }++    gfree(frags);++  // output the page, "undoing" the layout+  } else {+    for (flow = flows; flow; flow = flow->next) {+      for (blk = flow->blocks; blk; blk = blk->next) {+	for (line = blk->lines; line; line = line->next) {+	  n = line->len;+	  if (line->hyphenated && (line->next || blk->next)) {+	    --n;+	  }+	  s = new GString();+	  dumpFragment(line->text, n, uMap, s);+	  (*outputFunc)(outputStream, s->getCString(), s->getLength());+	  delete s;+	  if (!line->hyphenated) {+	    if (line->next) {+	      (*outputFunc)(outputStream, space, spaceLen);+	    } else if (blk->next) {+	      //~ this is a bit of a kludge - we should really do a more+	      //~ intelligent determination of paragraphs+	      if (blk->next->lines->words->fontSize ==+		  blk->lines->words->fontSize) {+		(*outputFunc)(outputStream, space, spaceLen);+	      } else {+		(*outputFunc)(outputStream, eol, eolLen);+	      }+	    }+	  }+	}+      }+      (*outputFunc)(outputStream, eol, eolLen);+      (*outputFunc)(outputStream, eol, eolLen);+    }+  }++  // end of page+  if (pageBreaks) {+    (*outputFunc)(outputStream, eop, eopLen);+  }++  uMap->decRefCnt();+}++void TextPage::assignColumns(TextLineFrag *frags, int nFrags, GBool oneRot) {+  TextLineFrag *frag0, *frag1;+  int rot, col1, col2, i, j, k;++  // all text in the region has the same rotation -- recompute the+  // column numbers based only on the text in the region+  if (oneRot) {+    qsort(frags, nFrags, sizeof(TextLineFrag), &TextLineFrag::cmpXYLineRot);+    rot = frags[0].line->rot;+    for (i = 0; i < nFrags; ++i) {+      frag0 = &frags[i];+      col1 = 0;+      for (j = 0; j < i; ++j) {+	frag1 = &frags[j];+	col2 = 0; // make gcc happy+	switch (rot) {+	case 0:+	  if (frag0->xMin >= frag1->xMax) {+	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -+				 frag1->line->col[frag1->start]) + 1;+	  } else {+	    for (k = frag1->start;+		 k < frag1->start + frag1->len &&+		   frag0->xMin >= 0.5 * (frag1->line->edge[k] ++					 frag1->line->edge[k+1]);+		 ++k) ;+	    col2 = frag1->col ++	           frag1->line->col[k] - frag1->line->col[frag1->start];+	  }+	  break;+	case 1:+	  if (frag0->yMin >= frag1->yMax) {+	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -+				 frag1->line->col[frag1->start]) + 1;+	  } else {+	    for (k = frag1->start;+		 k < frag1->start + frag1->len &&+		   frag0->yMin >= 0.5 * (frag1->line->edge[k] ++					 frag1->line->edge[k+1]);+		 ++k) ;+	    col2 = frag1->col ++	           frag1->line->col[k] - frag1->line->col[frag1->start];+	  }+	  break;+	case 2:+	  if (frag0->xMax <= frag1->xMin) {+	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -+				 frag1->line->col[frag1->start]) + 1;+	  } else {+	    for (k = frag1->start;+		 k < frag1->start + frag1->len &&+		   frag0->xMax <= 0.5 * (frag1->line->edge[k] ++					 frag1->line->edge[k+1]);+		 ++k) ;+	    col2 = frag1->col ++	           frag1->line->col[k] - frag1->line->col[frag1->start];+	  }+	  break;+	case 3:+	  if (frag0->yMax <= frag1->yMin) {+	    col2 = frag1->col + (frag1->line->col[frag1->start + frag1->len] -+				 frag1->line->col[frag1->start]) + 1;+	  } else {+	    for (k = frag1->start;+		 k < frag1->start + frag1->len &&+		   frag0->yMax <= 0.5 * (frag1->line->edge[k] ++					 frag1->line->edge[k+1]);+		 ++k) ;+	    col2 = frag1->col ++	           frag1->line->col[k] - frag1->line->col[frag1->start];+	  }+	  break;+	}+        printf("*** col: %i/%i ***\n", col1, col2);+	if (col2 > col1) {+	  col1 = col2;+	}+      }+      frag0->col = col1;+    }++  // the region includes text at different rotations -- use the+  // globally assigned column numbers, offset by the minimum column+  // number (i.e., shift everything over to column 0)+  } else {+    col1 = frags[0].col;+    for (i = 1; i < nFrags; ++i) {+      if (frags[i].col < col1) {+	col1 = frags[i].col;+      }+    }+    for (i = 0; i < nFrags; ++i) {+      frags[i].col -= col1;+    }+  }+}++int TextPage::dumpFragment(Unicode *text, int len, UnicodeMap *uMap,+			   GString *s) {+  char lre[8], rle[8], popdf[8], buf[8];+  int lreLen, rleLen, popdfLen, n;+  int nCols, i, j, k;++  nCols = 0;++  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);+	  ++nCols;+	}+	i = j;+	// output a right-to-left section+	for (j = i; j < len && !unicodeTypeL(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);+	    ++nCols;+	  }+	  s->append(popdf, popdfLen);+	  i = j;+	}+      }++    } else {++      s->append(rle, rleLen);+      i = len - 1;+      while (i >= 0) {+	// output a right-to-left section+	for (j = i; j >= 0 && !unicodeTypeL(text[j]); --j) ;+	for (k = i; k > j; --k) {+	  n = uMap->mapUnicode(text[k], buf, sizeof(buf));+	  s->append(buf, n);+	  ++nCols;+	}+	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);+	    ++nCols;+	  }+	  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);+      nCols += n;+    }+  }++  return nCols;+}++#if TEXTOUT_WORD_LIST+TextWordList *TextPage::makeWordList(GBool physLayout) {+  return new TextWordList(this, physLayout);+}+#endif++//------------------------------------------------------------------------+// TextOutputDev+//------------------------------------------------------------------------++static void outputToFile(void *stream, char *text, int len) {+  fwrite(text, 1, len, (FILE *)stream);+}++TextOutputDev::TextOutputDev(char *fileName, GBool physLayoutA,+			     GBool rawOrderA, GBool append) {+  text = NULL;+  physLayout = physLayoutA;+  rawOrder = rawOrderA;+  doHTML = gFalse;+  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 ((outputStream = fopen(fileName, append ? "ab" : "wb"))) {+      needClose = gTrue;+    } else {+      error(-1, "Couldn't open text file '%s'", fileName);+      ok = gFalse;+      return;+    }+    outputFunc = &outputToFile;+  } else {+    outputStream = NULL;+  }++  // set up text object+  text = new TextPage(rawOrderA);+}++TextOutputDev::TextOutputDev(TextOutputFunc func, void *stream,+			     GBool physLayoutA, GBool rawOrderA) {+  outputFunc = func;+  outputStream = stream;+  needClose = gFalse;+  physLayout = physLayoutA;+  rawOrder = rawOrderA;+  doHTML = gFalse;+  text = new TextPage(rawOrderA);+  ok = gTrue;+}++TextOutputDev::~TextOutputDev() {+  if (needClose) {+#ifdef MACOS+    ICS_MapRefNumAndAssign((short)((FILE *)outputStream)->handle);+#endif+    fclose((FILE *)outputStream);+  }+  if (text) {+    delete text;+  }+}++void TextOutputDev::startPage(int pageNum, GfxState *state) {+  text->startPage(state);+}++void TextOutputDev::endPage() {+  text->endPage();+  text->coalesce(physLayout, doHTML);+  return;+  if (outputStream) {+    text->dump(outputStream, outputFunc, physLayout);+  }+}++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::stroke(GfxState *state) {+  GfxPath *path;+  GfxSubpath *subpath;+  double x[2], y[2];++  if (!doHTML) {+    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]) {+    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 (!doHTML) {+    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 (!doHTML) {+    return;+  }+  fill(state);+}++void TextOutputDev::processLink(Link *link, Catalog *catalog) {+  double x1, y1, x2, y2;+  int xMin, yMin, xMax, yMax, x, y;++  if (!doHTML) {+    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,+			      double *xMin, double *yMin,+			      double *xMax, double *yMax) {+  return text->findText(s, len, startAtTop, stopAtBottom,+			startAtLast, stopAtLast, caseSensitive, backward,+			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);+}++#if TEXTOUT_WORD_LIST+TextWordList *TextOutputDev::makeWordList() {+  return text->makeWordList(physLayout);+}+#endif++TextPage *TextOutputDev::takeText() {+  TextPage *ret;++  ret = text;+  text = new TextPage(rawOrder);+  return ret;+}
+ xpdf/TextOutputDev.h view
@@ -0,0 +1,661 @@+//========================================================================+//+// TextOutputDev.h+//+// Copyright 1997-2003 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 GString;+class GList;+class GfxFont;+class GfxState;+class UnicodeMap;+class Link;++class TextWord;+class TextPool;+class TextLine;+class TextLineFrag;+class TextBlock;+class TextFlow;+class TextWordList;+class TextPage;++//------------------------------------------------------------------------++typedef void (*TextOutputFunc)(void *stream, char *text, int len);++//------------------------------------------------------------------------+// TextFontInfo+//------------------------------------------------------------------------++class TextFontInfo {+public:++  TextFontInfo(GfxState *state);+  ~TextFontInfo();++  GBool matches(GfxState *state);++#if TEXTOUT_WORD_LIST+  // 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; }+#endif++private:++  GfxFont *gfxFont;+#if TEXTOUT_WORD_LIST+  GString *fontName;+  int flags;+#endif++  friend class TextWord;+  friend class TextPage;+};++//------------------------------------------------------------------------+// TextWord+//------------------------------------------------------------------------++class TextWord {+public:++  // Constructor.+  TextWord(GfxState *state, int rotA, double x0, double y0,+	   int charPosA, TextFontInfo *fontA, double fontSize);++  // Destructor.+  ~TextWord();++  // Add a character to the word.+  void addChar(GfxState *state, double x, double y,+	       double dx, double dy, Unicode u);++  // Merge <word> onto the end of <this>.+  void merge(TextWord *word);++  // Compares <this> to <word>, returning -1 (<), 0 (=), or +1 (>),+  // based on a primary-axis comparison, e.g., x ordering if rot=0.+  int primaryCmp(TextWord *word);++  // Return the distance along the primary axis between <this> and+  // <word>.+  double primaryDelta(TextWord *word);++  static int cmpYX(const void *p1, const void *p2);++  // Get the TextFontInfo object associated with this word.+  TextFontInfo *getFontInfo() { return font; }++  // Get the next TextWord on the linked list.+  TextWord *getNext() { return next; }++#if TEXTOUT_WORD_LIST+  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; }+  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; }+  int getCharPos() { return charPos; }+  int getCharLen() { return charLen; }+  GBool getSpaceAfter() { return spaceAfter; }+#endif++  GBool isUnderlined() { return underlined; }+  Link *getLink() { return link; }++private:++  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 base;			// baseline x or y coordinate+  Unicode *text;		// the text+  double *edge;			// "near" edge x or y coord of each char+				//   (plus one extra entry for the last char)+  int len;			// length of text and edge arrays+  int size;			// size of text and edge arrays+  int charPos;                  // character position (within content stream)+  int charLen;                  // number of content stream characters in+                                //   this word+  TextFontInfo *font;		// font information+  double fontSize;		// font size+  GBool spaceAfter;		// set if there is a space between this+				//   word and the next word on the line+  TextWord *next;		// next word in line++#if TEXTOUT_WORD_LIST+  double colorR,		// word color+         colorG,+         colorB;+#endif++  GBool underlined;+  Link *link;++  friend class TextPool;+  friend class TextLine;+  friend class TextBlock;+  friend class TextFlow;+  friend class TextWordList;+  friend class TextPage;+};++//------------------------------------------------------------------------+// TextPool+//------------------------------------------------------------------------++class TextPool {+public:++  TextPool();+  ~TextPool();++  TextWord *getPool(int baseIdx) { return pool[baseIdx - minBaseIdx]; }+  void setPool(int baseIdx, TextWord *p) { pool[baseIdx - minBaseIdx] = p; }++  int getBaseIdx(double base);++  void addWord(TextWord *word);++private:++  int minBaseIdx;		// min baseline bucket index+  int maxBaseIdx;		// max baseline bucket index+  TextWord **pool;		// array of linked lists, one for each+				//   baseline value (multiple of 4 pts)+  TextWord *cursor;		// pointer to last-accessed word+  int cursorBaseIdx;		// baseline bucket index of last-accessed word++  friend class TextBlock;+  friend class TextPage;+};++//------------------------------------------------------------------------+// TextLine+//------------------------------------------------------------------------++class TextLine {+public:++  TextLine(TextBlock *blkA, int rotA, double baseA);+  ~TextLine();++  void addWord(TextWord *word);++  // Return the distance along the primary axis between <this> and+  // <line>.+  double primaryDelta(TextLine *line);++  // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),+  // based on a primary-axis comparison, e.g., x ordering if rot=0.+  int primaryCmp(TextLine *line);++  // Compares <this> to <line>, returning -1 (<), 0 (=), or +1 (>),+  // based on a secondary-axis comparison of the baselines, e.g., y+  // ordering if rot=0.+  int secondaryCmp(TextLine *line);++  int cmpYX(TextLine *line);++  static int cmpXY(const void *p1, const void *p2);++  void coalesce(UnicodeMap *uMap);++  // Get the head of the linked list of TextWords.+  TextWord *getWords() { return words; }++  // Get the next TextLine on the linked list.+  TextLine *getNext() { return next; }++  // Returns true if the last char of the line is a hyphen.+  GBool isHyphenated() { return hyphenated; }++private:++  TextBlock *blk;		// parent block+  int rot;			// text rotation+  double xMin, xMax;		// bounding box x coordinates+  double yMin, yMax;		// bounding box y coordinates+  double base;			// baseline x or y coordinate+  TextWord *words;		// words in this line+  TextWord *lastWord;		// last word in 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 *col;			// starting column number of each Unicode char+  int len;			// number of Unicode chars+  int convertedLen;		// total number of converted characters+  GBool hyphenated;		// set if last char is a hyphen+  TextLine *next;		// next line in block++  friend class TextLineFrag;+  friend class TextBlock;+  friend class TextFlow;+  friend class TextWordList;+  friend class TextPage;+};++//------------------------------------------------------------------------+// TextBlock+//------------------------------------------------------------------------++class TextBlock {+public:++  TextBlock(TextPage *pageA, int rotA);+  ~TextBlock();++  void addWord(TextWord *word);++  void coalesce(UnicodeMap *uMap);++  // Update this block's priMin and priMax values, looking at <blk>.+  void updatePriMinMax(TextBlock *blk);++  static int cmpXYPrimaryRot(const void *p1, const void *p2);++  static int cmpYXPrimaryRot(const void *p1, const void *p2);++  int primaryCmp(TextBlock *blk);++  double secondaryDelta(TextBlock *blk);++  // Returns true if <this> is below <blk>, relative to the page's+  // primary rotation.+  GBool isBelow(TextBlock *blk);++  // Get the head of the linked list of TextLines.+  TextLine *getLines() { return lines; }++  // Get the next TextBlock on the linked list.+  TextBlock *getNext() { return next; }++private:++  TextPage *page;		// the parent page+  int rot;			// text rotation+  double xMin, xMax;		// bounding box x coordinates+  double yMin, yMax;		// bounding box y coordinates+  double priMin, priMax;	// whitespace bounding box along primary axis++  TextPool *pool;		// pool of words (used only until lines+				//   are built)+  TextLine *lines;		// linked list of lines+  TextLine *curLine;		// most recently added line+  int nLines;			// number of lines+  int charCount;		// number of characters in the block+  int col;			// starting column+  int nColumns;			// number of columns in the block++  TextBlock *next;+  TextBlock *stackNext;++  friend class TextLine;+  friend class TextLineFrag;+  friend class TextFlow;+  friend class TextWordList;+  friend class TextPage;+};++//------------------------------------------------------------------------+// TextFlow+//------------------------------------------------------------------------++class TextFlow {+public:++  TextFlow(TextPage *pageA, TextBlock *blk);+  ~TextFlow();++  // Add a block to the end of this flow.+  void addBlock(TextBlock *blk);++  // Returns true if <blk> fits below <prevBlk> in the flow, i.e., (1)+  // it uses a font no larger than the last block added to the flow,+  // and (2) it fits within the flow's [priMin, priMax] along the+  // primary axis.+  GBool blockFits(TextBlock *blk, TextBlock *prevBlk);++  // Get the head of the linked list of TextBlocks.+  TextBlock *getBlocks() { return blocks; }++  // Get the next TextFlow on the linked list.+  TextFlow *getNext() { return next; }++private:++  TextPage *page;		// the parent page+  double xMin, xMax;		// bounding box x coordinates+  double yMin, yMax;		// bounding box y coordinates+  double priMin, priMax;	// whitespace bounding box along primary axis+  TextBlock *blocks;		// blocks in flow+  TextBlock *lastBlk;		// last block in this flow+  TextFlow *next;++  friend class TextWordList;+  friend class TextPage;+};++#if TEXTOUT_WORD_LIST++//------------------------------------------------------------------------+// TextWordList+//------------------------------------------------------------------------++class TextWordList {+public:++  // Build a flat word list, in content stream order (if+  // text->rawOrder is true), physical layout order (if <physLayout>+  // is true and text->rawOrder is false), or reading order (if both+  // flags are false).+  TextWordList(TextPage *text, GBool physLayout);++  ~TextWordList();++  // Return the number of words on the list.+  int getLength();++  // Return the <idx>th word from the list.+  TextWord *get(int idx);++private:++  GList *words;			// [TextWord]+};++#endif // TEXTOUT_WORD_LIST++//------------------------------------------------------------------------+// TextPage+//------------------------------------------------------------------------++class TextPage {+public:++  // Constructor.+  TextPage(GBool rawOrderA);++  // Destructor.+  ~TextPage();++  // Start a new page.+  void startPage(GfxState *state);++  // End the current page.+  void endPage();++  // Update the current font.+  void updateFont(GfxState *state);++  // Begin a new word.+  void beginWord(GfxState *state, double x0, double y0);++  // Add a character to the current word.+  void addChar(GfxState *state, double x, double y,+	       double dx, double dy,+	       CharCode c, int nBytes, Unicode *u, int uLen);++  // End the current word, sorting it into the list of words.+  void endWord();++  // Add a word, sorting it into the list of words.+  void addWord(TextWord *word);++  // Add a (potential) underline.+  void addUnderline(double x0, double y0, double x1, double y1);++  // Add a hyperlink.+  void addLink(int xMin, int yMin, int xMax, int yMax, Link *link);++  // Coalesce strings that look like parts of the same line.+  void coalesce(GBool physLayout, GBool doHTML);++  // 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,+		 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);++  // Dump contents of page to a file.+  void dump(void *outputStream, TextOutputFunc outputFunc,+	    GBool physLayout);++  // Get the head of the linked list of TextFlows.+  TextFlow *getFlows() { return flows; }++#if TEXTOUT_WORD_LIST+  // Build a flat word list, in content stream order (if+  // this->rawOrder is true), physical layout order (if <physLayout>+  // is true and this->rawOrder is false), or reading order (if both+  // flags are false).+  TextWordList *makeWordList(GBool physLayout);+#endif++private:++  void clear();+  void assignColumns(TextLineFrag *frags, int nFrags, int rot);+  int dumpFragment(Unicode *text, int len, UnicodeMap *uMap, GString *s);++  GBool rawOrder;		// keep text in content stream order++  double pageWidth, pageHeight;	// width and height of current page+  TextWord *curWord;		// currently active string+  int charPos;			// next character position (within content+				//   stream)+  TextFontInfo *curFont;	// current font+  double curFontSize;		// current font size+  int nest;			// current nesting level (for Type 3 fonts)+  int nTinyChars;		// number of "tiny" chars seen so far+  GBool lastCharOverlap;	// set if the last added char overlapped the+				//   previous char++  TextPool *pools[4];		// a "pool" of TextWords for each rotation+  TextFlow *flows;		// linked list of flows+  TextBlock **blocks;		// array of blocks, in yx order+  int nBlocks;			// number of blocks+  int primaryRot;		// primary rotation+  GBool primaryLR;		// primary direction (true means L-to-R,+				//   false means R-to-L)+  TextWord *rawWords;		// list of words, in raw order (only if+				//   rawOrder is set)+  TextWord *rawLastWord;	// last word on rawWords list++  GList *fonts;			// all font info objects used on this+				//   page [TextFontInfo]++  double lastFindXMin,		// coordinates of the last "find" result+         lastFindYMin;+  GBool haveLastFind;++  GList *underlines;		// [TextUnderline]+  GList *links;			// [TextLink]++  friend class TextLine;+  friend class TextLineFrag;+  friend class TextBlock;+  friend class TextFlow;+  friend class TextWordList;+};++//------------------------------------------------------------------------+// 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, GBool physLayoutA,+		GBool rawOrderA, GBool append);++  // 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,+		GBool physLayoutA, GBool rawOrderA);++  // 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; }++  //----- initialization and control++  // Start a page.+  virtual void startPage(int pageNum, GfxState *state);++  // End a page.+  virtual void endPage();++  //----- 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);++  //----- path painting+  virtual void stroke(GfxState *state);+  virtual void fill(GfxState *state);+  virtual void eoFill(GfxState *state);++  //----- link borders+  virtual void processLink(Link *link, Catalog *catalog);++  //----- 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,+		 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);++#if TEXTOUT_WORD_LIST+  // 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();+#endif++  // 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 doHTMLA) { doHTML = doHTMLA; }++private:++  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+  GBool physLayout;		// maintain original physical layout when+				//   dumping text+  GBool rawOrder;		// keep text in content stream order+  GBool doHTML;			// extra processing for HTML conversion+  GBool ok;			// set up ok?+};++#endif
+ xpdf/UTF8.h view
@@ -0,0 +1,56 @@+//========================================================================+//+// UTF8.h+//+// Copyright 2001-2003 Glyph & Cog, LLC+//+//========================================================================++static 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;+  }+}++static 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;+  }+}
+ xpdf/UnicodeMap.cc view
@@ -0,0 +1,293 @@+//========================================================================+//+// 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 "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(-1, "Couldn't find unicodeMap file for the '%s' encoding",+	  encodingNameA->getCString());+    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 = 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(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",+	      line, encodingNameA->getCString());+      }+    } else {+      error(-1, "Bad line (%d) in unicodeMap file for the '%s' encoding",+	    line, encodingNameA->getCString());+    }+    ++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;+#if MULTITHREADED+  gInitMutex(&mutex);+#endif+}++UnicodeMap::UnicodeMap(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;+#if MULTITHREADED+  gInitMutex(&mutex);+#endif+}++UnicodeMap::UnicodeMap(char *encodingNameA, GBool unicodeOutA,+		       UnicodeMapFunc funcA) {+  encodingName = new GString(encodingNameA);+  unicodeOut = unicodeOutA;+  kind = unicodeMapFunc;+  func = funcA;+  eMaps = NULL;+  eMapsLen = 0;+  refCnt = 1;+#if MULTITHREADED+  gInitMutex(&mutex);+#endif+}++UnicodeMap::~UnicodeMap() {+  delete encodingName;+  if (kind == unicodeMapUser && ranges) {+    gfree(ranges);+  }+  if (eMaps) {+    gfree(eMaps);+  }+#if MULTITHREADED+  gDestroyMutex(&mutex);+#endif+}++void UnicodeMap::incRefCnt() {+#if MULTITHREADED+  gLockMutex(&mutex);+#endif+  ++refCnt;+#if MULTITHREADED+  gUnlockMutex(&mutex);+#endif+}++void UnicodeMap::decRefCnt() {+  GBool done;++#if MULTITHREADED+  gLockMutex(&mutex);+#endif+  done = --refCnt == 0;+#if MULTITHREADED+  gUnlockMutex(&mutex);+#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;+}
+ xpdf/UnicodeMap.h view
@@ -0,0 +1,123 @@+//========================================================================+//+// 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(char *encodingNameA, GBool unicodeOutA,+	     UnicodeMapRange *rangesA, int lenA);++  // Create a resident UnicodeMap that uses a function instead of a+  // list of ranges.+  UnicodeMap(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)+  int refCnt;+#if MULTITHREADED+  GMutex mutex;+#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
+ xpdf/UnicodeMapTables.h view
@@ -0,0 +1,361 @@+//========================================================================+//+// UnicodeMapTables.h+//+// Copyright 2001-2003 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 },+  { 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 }+};+#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 }+};+#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))
+ xpdf/UnicodeTypeTable.cc view
@@ -0,0 +1,949 @@+//========================================================================+//+// UnicodeTypeTable.cc+//+// Copyright 2004 Glyph & Cog, LLC+//+//========================================================================++#include <stdlib.h>+#include "CharTypes.h"+#include "UnicodeTypeTable.h"++struct UnicodeMapTableEntry {+  char *vector;+  char type;+};++struct UnicodeCaseTableVector {+  Unicode codes[256];+};++static UnicodeMapTableEntry typeTable[256] = {+  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNLNNNNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL", 'X' },+  { NULL, 'L' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLNNNNNNNNNNNNNNLLNNNNNNNNNNNNNNLLLLLNNNNNNNNNLNNNNNNNNNNNNNNNNN", 'X' },+  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLNNNNNNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRNRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },+  { "RRRRNNNNNNNNNRNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNRNNNNNNNRRNNNNNNNRRNNNNNNNNNNRRRRRR", 'X' },+  { "RRRRRRRRRRRRRRNNRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNNNNNNNNNNRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },+  { NULL, 'N' },+  { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLNNNNLLLLLLLLLLLLLNNLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNLLLLLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLNNLLLLLLLNNNNN", 'X' },+  { "NNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLNNNNNNNNLLLLNLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLNNNNNNNNNNNNNNNN", 'X' },+  { "NNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLNLNNNLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLLLLLLLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNNNNLLLLLLLNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLNNNNNNNNNLLLLLLLLLLNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNLNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNLNNNNNLNNLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNLNNNNNNLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { NULL, 'L' },+  { NULL, 'L' },+  { NULL, 'L' },+  { NULL, 'L' },+  { NULL, 'L' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNLLLLLLLLNLLNNNNNNNNNNNLLLLLLLNLNLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", 'X' },+  { "NNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL", 'X' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLNNNNNLLLLLLNLLLLLLNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },+  { NULL, 'L' },+  { NULL, 'L' },+  { NULL, 'L' },+  { NULL, 'L' },+  { NULL, 'L' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLNNNLLLLLLLLLLLNNNLLLLLLLLLLLLNNNNLLLLLLLLLLLLLNNNLLLLLLLLLLLLLNNN", 'X' },+  { "NNNNNNNNNNNNNNLRNNNNNNNNNNNNNNNNNNNNNNNNNNLRNLRNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },+  { "NNLNNNNLNNLLLLLLLLLLNLNNNLLLLLNNNNNNLNLNLNLLLLNLLLNLLLLLLLNNLLLLNNNNNLLLLLNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },+  { NULL, 'N' },+  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN", 'X' },+  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNN", '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' },+  { "LLLLLLLLLLLLLLLLLLLLLLLLRRRRRRNRRRRRRRRRRNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR", 'X' },+  { NULL, 'R' },+  { "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },+  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRNNN", 'X' },+  { "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNLL", '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';+}++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;+}+
+ xpdf/UnicodeTypeTable.h view
@@ -0,0 +1,20 @@+//========================================================================+//+// UnicodeTypeTable.h+//+// Copyright 2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef UNICODETYPETABLE_H+#define UNICODETYPETABLE_H++#include "gtypes.h"++extern GBool unicodeTypeL(Unicode c);++extern GBool unicodeTypeR(Unicode c);++extern Unicode unicodeToUpper(Unicode c);++#endif
+ xpdf/XRef.cc view
@@ -0,0 +1,896 @@+//========================================================================+//+// 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 "gmem.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++//------------------------------------------------------------------------+// ObjectStream+//------------------------------------------------------------------------++class ObjectStream {+public:++  // Create an object stream, using object number <objStrNum>,+  // generation 0.+  ObjectStream(XRef *xref, int objStrNumA);++  ~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)+};++ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {+  Stream *str;+  Parser *parser;+  int *offsets;+  Object objStr, obj1, obj2;+  int first, i;++  objStrNum = objStrNumA;+  nObjects = 0;+  objs = NULL;+  objNums = NULL;++  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;+  }++  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);+  parser = new Parser(xref, new Lexer(xref, str), gFalse);+  for (i = 0; i < nObjects; ++i) {+    parser->getObj(&obj1);+    parser->getObj(&obj2);+    if (!obj1.isInt() || !obj2.isInt()) {+      obj1.free();+      obj2.free();+      delete parser;+      gfree(offsets);+      goto err1;+    }+    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 err1;+    }+  }+  while (str->getChar() != EOF) ;+  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...+  for (i = first; i < offsets[0]; ++i) {+    objStr.getStream()->getChar();+  }++  // 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]);+    }+    parser = new Parser(xref, new Lexer(xref, str), gFalse);+    parser->getObj(&objs[i]);+    while (str->getChar() != EOF) ;+    delete parser;+  }++  gfree(offsets);++ err1:+  objStr.free();+  return;+}++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]) {+    return obj->initNull();+  }+  return objs[objIdx].copy(obj);+}++//------------------------------------------------------------------------+// XRef+//------------------------------------------------------------------------++XRef::XRef(BaseStream *strA) {+  Guint pos;+  Object obj;++  ok = gTrue;+  errCode = errNone;+  size = 0;+  entries = NULL;+  streamEnds = NULL;+  streamEndsLen = 0;+  objStr = NULL;++  encrypted = gFalse;+  permFlags = defPermFlags;+  ownerPasswordOk = gFalse;++  // read the trailer+  str = strA;+  start = str->getStart();+  pos = getStartXref();++  // if there was a problem with the 'startxref' position, try to+  // reconstruct the xref table+  if (pos == 0) {+    if (!(ok = constructXRef())) {+      errCode = errDamaged;+      return;+    }++  // read the xref table+  } else {+    while (readXRef(&pos)) ;++    // if there was a problem with the xref table,+    // try to reconstruct it+    if (!ok) {+      if (!(ok = constructXRef())) {+	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() {+  gfree(entries);+  trailerDict.free();+  if (streamEnds) {+    gfree(streamEnds);+  }+  if (objStr) {+    delete objStr;+  }+}++// Read the 'startxref' position.+Guint XRef::getStartXref() {+  char buf[xrefSearchSize+1];+  char *p;+  int c, n, i;++  // read last xrefSearchSize bytes+  str->setPos(xrefSearchSize, -1);+  for (n = 0; n < xrefSearchSize; ++n) {+    if ((c = str->getChar()) == EOF) {+      break;+    }+    buf[n] = c;+  }+  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); ++p) ;+  lastXRefPos = strToUnsigned(p);++  return lastXRefPos;+}++// Read one xref table section.  Also reads the associated trailer+// dictionary, and returns the prev pointer (if any).+GBool XRef::readXRef(Guint *pos) {+  Parser *parser;+  Object obj;+  GBool more;++  // start up a parser, parse one token+  obj.initNull();+  parser = new Parser(NULL,+	     new Lexer(NULL,+	       str->makeSubStream(start + *pos, gFalse, 0, &obj)),+	     gTrue);+  parser->getObj(&obj);++  // parse an old-style xref table+  if (obj.isCmd("xref")) {+    obj.free();+    more = readXRefTable(parser, pos);++  // parse an xref stream+  } else if (obj.isInt()) {+    obj.free();+    if (!parser->getObj(&obj)->isInt()) {+      goto err1;+    }+    obj.free();+    if (!parser->getObj(&obj)->isCmd("obj")) {+      goto err1;+    }+    obj.free();+    if (!parser->getObj(&obj)->isStream()) {+      goto err1;+    }+    more = readXRefStream(obj.getStream(), pos);+    obj.free();++  } else {+    goto err1;+  }++  delete parser;+  return more;++ err1:+  obj.free();+  delete parser;+  ok = gFalse;+  return gFalse;+}++GBool XRef::readXRefTable(Parser *parser, Guint *pos) {+  XRefEntry entry;+  GBool more;+  Object obj, obj2;+  Guint pos2;+  int first, n, newSize, i;++  while (1) {+    parser->getObj(&obj);+    if (obj.isCmd("trailer")) {+      obj.free();+      break;+    }+    if (!obj.isInt()) {+      goto err1;+    }+    first = obj.getInt();+    obj.free();+    if (!parser->getObj(&obj)->isInt()) {+      goto err1;+    }+    n = obj.getInt();+    obj.free();+    if (first < 0 || n < 0 || first + n < 0) {+      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 = 0xffffffff;+	entries[i].type = xrefEntryFree;+      }+      size = newSize;+    }+    for (i = first; i < first + n; ++i) {+      if (!parser->getObj(&obj)->isInt()) {+	goto err1;+      }+      entry.offset = (Guint)obj.getInt();+      obj.free();+      if (!parser->getObj(&obj)->isInt()) {+	goto err1;+      }+      entry.gen = obj.getInt();+      obj.free();+      parser->getObj(&obj);+      if (obj.isCmd("n")) {+	entry.type = xrefEntryUncompressed;+      } else if (obj.isCmd("f")) {+	entry.type = xrefEntryFree;+      } else {+	goto err1;+      }+      obj.free();+      if (entries[i].offset == 0xffffffff) {+	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 = 0xffffffff;+	}+      }+    }+  }++  // read the trailer dictionary+  if (!parser->getObj(&obj)->isDict()) {+    goto err1;+  }++  // get the 'Prev' pointer+  obj.getDict()->lookupNF("Prev", &obj2);+  if (obj2.isInt()) {+    *pos = (Guint)obj2.getInt();+    more = gTrue;+  } else if (obj2.isRef()) {+    // certain buggy PDF generators generate "/Prev NNN 0 R" instead+    // of "/Prev NNN"+    *pos = (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+  if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {+    pos2 = (Guint)obj2.getInt();+    readXRef(&pos2);+    if (!ok) {+      obj2.free();+      goto err1;+    }+  }+  obj2.free();++  obj.free();+  return more;++ err1:+  obj.free();+  ok = gFalse;+  return gFalse;+}++GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {+  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 = 0xffffffff;+      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();+    if (w[i] < 0 || w[i] > 4) {+      goto err1;+    }+  }+  obj.free();++  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();++  dict->lookupNF("Prev", &obj);+  if (obj.isInt()) {+    *pos = (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) {+  Guint offset;+  int type, gen, 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 = 0xffffffff;+      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;+    }+    for (gen = 0, j = 0; j < w[2]; ++j) {+      if ((c = xrefStr->getChar()) == EOF) {+	return gFalse;+      }+      gen = (gen << 8) + c;+    }+    if (entries[i].offset == 0xffffffff) {+      switch (type) {+      case 0:+	entries[i].offset = offset;+	entries[i].gen = gen;+	entries[i].type = xrefEntryFree;+	break;+      case 1:+	entries[i].offset = offset;+	entries[i].gen = gen;+	entries[i].type = xrefEntryUncompressed;+	break;+      case 2:+	entries[i].offset = offset;+	entries[i].gen = gen;+	entries[i].type = xrefEntryCompressed;+	break;+      default:+	return gFalse;+      }+    }+  }++  return gTrue;+}++// Attempt to construct an xref table for a damaged file.+GBool XRef::constructXRef() {+  Parser *parser;+  Object newTrailerDict, obj;+  char buf[256];+  Guint pos;+  int num, gen;+  int newSize;+  int streamEndsSize;+  char *p;+  int i;+  GBool gotRoot;++  gfree(entries);+  size = 0;+  entries = NULL;++  error(-1, "PDF file is damaged - attempting to reconstruct xref table...");+  gotRoot = gFalse;+  streamEndsLen = streamEndsSize = 0;++  str->reset();+  while (1) {+    pos = str->getPos();+    if (!str->getLine(buf, 256)) {+      break;+    }+    p = buf;++    // skip whitespace+    while (*p && Lexer::isSpace(*p & 0xff)) ++p;++    // got trailer dictionary+    if (!strncmp(p, "trailer", 7)) {+      obj.initNull();+      parser = new Parser(NULL,+		 new Lexer(NULL,+		   str->makeSubStream(pos + 7, gFalse, 0, &obj)),+		 gFalse);+      parser->getObj(&newTrailerDict);+      if (newTrailerDict.isDict()) {+	newTrailerDict.dictLookupNF("Root", &obj);+	if (obj.isRef()) {+	  rootNum = obj.getRefNum();+	  rootGen = obj.getRefGen();+	  if (!trailerDict.isNone()) {+	    trailerDict.free();+	  }+	  newTrailerDict.copy(&trailerDict);+	  gotRoot = gTrue;+	}+	obj.free();+      }+      newTrailerDict.free();+      delete parser;++    // look for object+    } else if (isdigit(*p)) {+      num = atoi(p);+      if (num > 0) {+	do {+	  ++p;+	} while (*p && isdigit(*p));+	if (isspace(*p)) {+	  do {+	    ++p;+	  } while (*p && isspace(*p));+	  if (isdigit(*p)) {+	    gen = atoi(p);+	    do {+	      ++p;+	    } while (*p && isdigit(*p));+	    if (isspace(*p)) {+	      do {+		++p;+	      } while (*p && isspace(*p));+	      if (!strncmp(p, "obj", 3)) {+		if (num >= size) {+		  newSize = (num + 1 + 255) & ~255;+		  if (newSize < 0) {+		    error(-1, "Bad object number");+		    return gFalse;+		  }+		  entries = (XRefEntry *)+		      greallocn(entries, newSize, sizeof(XRefEntry));+		  for (i = size; i < newSize; ++i) {+		    entries[i].offset = 0xffffffff;+		    entries[i].type = xrefEntryFree;+		  }+		  size = newSize;+		}+		if (entries[num].type == xrefEntryFree ||+		    gen >= entries[num].gen) {+		  entries[num].offset = pos - start;+		  entries[num].gen = gen;+		  entries[num].type = xrefEntryUncompressed;+		}+	      }+	    }+	  }+	}+      }++    } else if (!strncmp(p, "endstream", 9)) {+      if (streamEndsLen == streamEndsSize) {+	streamEndsSize += 64;+	streamEnds = (Guint *)greallocn(streamEnds,+					streamEndsSize, sizeof(int));+      }+      streamEnds[streamEndsLen++] = pos;+    }+  }++  if (gotRoot)+    return gTrue;++  error(-1, "Couldn't find trailer dictionary");+  return gFalse;+}++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 <= 16) {+    keyLength = keyLengthA;+  } else {+    keyLength = 16;+  }+  for (i = 0; i < keyLength; ++i) {+    fileKey[i] = fileKeyA[i];+  }+  encVersion = encVersionA;+  encAlgorithm = encAlgorithmA;+}++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) {+  XRefEntry *e;+  Parser *parser;+  Object obj1, obj2, obj3;++  // check for bogus ref - this can happen in corrupted PDF files+  if (num < 0 || num >= size) {+    goto err;+  }++  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);+    parser->getObj(&obj2);+    parser->getObj(&obj3);+    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, encrypted ? fileKey : (Guchar *)NULL,+		   encAlgorithm, keyLength, num, gen);+    obj1.free();+    obj2.free();+    obj3.free();+    delete parser;+    break;++  case xrefEntryCompressed:+    if (gen != 0) {+      goto err;+    }+    if (!objStr || objStr->getObjStrNum() != (int)e->offset) {+      if (objStr) {+	delete objStr;+      }+      objStr = new ObjectStream(this, e->offset);+    }+    objStr->getObject(e->gen, num, obj);+    break;++  default:+    goto err;+  }++  return obj;++ err:+  return obj->initNull();+}++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(Guint streamStart, Guint *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;+}++Guint XRef::strToUnsigned(char *s) {+  Guint x;+  char *p;+  int i;++  x = 0;+  for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {+    x = 10 * x + (*p - '0');+  }+  return x;+}
+ xpdf/XRef.h view
@@ -0,0 +1,133 @@+//========================================================================+//+// 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 "Object.h"++class Dict;+class Stream;+class Parser;+class ObjectStream;++//------------------------------------------------------------------------+// XRef+//------------------------------------------------------------------------++enum XRefEntryType {+  xrefEntryFree,+  xrefEntryUncompressed,+  xrefEntryCompressed+};++struct XRefEntry {+  Guint offset;+  int gen;+  XRefEntryType type;+};++class XRef {+public:++  // Constructor.  Read xref table from stream.+  XRef(BaseStream *strA);++  // 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; }++  // Check various permissions.+  GBool okToPrint(GBool ignoreOwnerPW = gFalse);+  GBool okToChange(GBool ignoreOwnerPW = gFalse);+  GBool okToCopy(GBool ignoreOwnerPW = gFalse);+  GBool okToAddNotes(GBool ignoreOwnerPW = gFalse);++  // Get catalog object.+  Object *getCatalog(Object *obj) { return fetch(rootNum, rootGen, obj); }++  // Fetch an indirect reference.+  Object *fetch(int num, int gen, Object *obj);++  // 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 size; }++  // Return the offset of the last xref table.+  Guint getLastXRefPos() { return lastXRefPos; }++  // Return the catalog object reference.+  int getRootNum() { return rootNum; }+  int getRootGen() { return rootGen; }++  // Get end position for a stream in a damaged file.+  // Returns false if unknown or file is not damaged.+  GBool getStreamEnd(Guint streamStart, Guint *streamEnd);++  // Direct access.+  int getSize() { return size; }+  XRefEntry *getEntry(int i) { return &entries[i]; }+  Object *getTrailerDict() { return &trailerDict; }++private:++  BaseStream *str;		// input stream+  Guint start;			// offset in file (to allow for garbage+				//   at beginning of file)+  XRefEntry *entries;		// xref entries+  int size;			// size of <entries> array+  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+  Guint lastXRefPos;		// offset of last xref table+  Guint *streamEnds;		// 'endstream' positions - only used in+				//   damaged files+  int streamEndsLen;		// number of valid entries in streamEnds+  ObjectStream *objStr;		// cached object stream+  GBool encrypted;		// true if file is encrypted+  int permFlags;		// permission bits+  GBool ownerPasswordOk;	// true if owner password is correct+  Guchar fileKey[16];		// file decryption key+  int keyLength;		// length of key, in bytes+  int encVersion;		// encryption version+  CryptAlgorithm encAlgorithm;	// encryption algorithm++  Guint getStartXref();+  GBool readXRef(Guint *pos);+  GBool readXRefTable(Parser *parser, Guint *pos);+  GBool readXRefStreamSection(Stream *xrefStr, int *w, int first, int n);+  GBool readXRefStream(Stream *xrefStr, Guint *pos);+  GBool constructXRef();+  Guint strToUnsigned(char *s);+};++#endif
+ xpdf/XpdfPluginAPI.cc view
@@ -0,0 +1,262 @@+//========================================================================+//+// XpdfPluginAPI.cc+//+// Copyright 2004 Glyph & Cog, LLC+//+//========================================================================++#include "aconf.h"++#ifdef ENABLE_PLUGINS++#include "gmem.h"+#include "GlobalParams.h"+#include "Object.h"+#include "PDFDoc.h"+#ifdef WIN32+#include "WinPDFCore.h"+#else+#include "XPDFCore.h"+#endif+#include "XpdfPluginAPI.h"++//------------------------------------------------------------------------++//~ This should use a pool of Objects; change xpdfFreeObj to match.+static Object *allocObj() {+  return (Object *)gmalloc(sizeof(Object));+}++//------------------------------------------------------------------------+// Document access functions+//------------------------------------------------------------------------++XpdfObject _xpdfGetInfoDict(XpdfDoc doc) {+  Object *obj;++  obj = allocObj();+  return (XpdfObject)((PDFDoc *)doc)->getDocInfo(obj);+}++XpdfObject _xpdfGetCatalog(XpdfDoc doc) {+  Object *obj;++  obj = allocObj();+  return (XpdfObject)((PDFDoc *)doc)->getXRef()->getCatalog(obj);+}++#ifdef _WIN32++HWND _xpdfWin32GetWindow(XpdfDoc doc) {+  WinPDFCore *core;++  if (!(core = (WinPDFCore *)((PDFDoc *)doc)->getGUIData())) {+    return NULL;+  }+  return core->getDrawFrame();+}++#else++Widget _xpdfXGetWindow(XpdfDoc doc) {+  XPDFCore *core;++  if (!(core = (XPDFCore *)((PDFDoc *)doc)->getGUIData())) {+    return NULL;+  }+  return core->getWidget();+}++#endif++//------------------------------------------------------------------------+// Object access functions.+//------------------------------------------------------------------------++XpdfBool _xpdfObjIsBool(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isBool();+}++XpdfBool _xpdfObjIsInt(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isInt();+}++XpdfBool _xpdfObjIsReal(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isReal();+}++XpdfBool _xpdfObjIsNumber(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isNum();+}++XpdfBool _xpdfObjIsString(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isString();+}++XpdfBool _xpdfObjIsName(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isName();+}++XpdfBool _xpdfObjIsNull(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isNull();+}++XpdfBool _xpdfObjIsArray(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isArray();+}++XpdfBool _xpdfObjIsDict(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isDict();+}++XpdfBool _xpdfObjIsStream(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isStream();+}++XpdfBool _xpdfObjIsRef(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->isRef();+}++XpdfBool _xpdfBoolValue(XpdfObject obj) {+  return (XpdfBool)((Object *)obj)->getBool();+}++int _xpdfIntValue(XpdfObject obj) {+  if (!((Object *)obj)->isInt()) {+    return 0;+  }+  return ((Object *)obj)->getInt();+}++double _xpdfRealValue(XpdfObject obj) {+  if (!((Object *)obj)->isReal()) {+    return 0;+  }+  return ((Object *)obj)->getReal();+}++double _xpdfNumberValue(XpdfObject obj) {+  if (!((Object *)obj)->isNum()) {+    return 0;+  }+  return ((Object *)obj)->getNum();+}++int _xpdfStringLength(XpdfObject obj) {+  if (!((Object *)obj)->isString()) {+    return 0;+  }+  return ((Object *)obj)->getString()->getLength();+}++char *_xpdfStringValue(XpdfObject obj) {+  if (!((Object *)obj)->isString()) {+    return 0;+  }+  return ((Object *)obj)->getString()->getCString();+}++char *_xpdfNameValue(XpdfObject obj) {+  if (!((Object *)obj)->isName()) {+    return NULL;+  }+  return ((Object *)obj)->getName();+}++int _xpdfArrayLength(XpdfObject obj) {+  if (!((Object *)obj)->isArray()) {+    return 0;+  }+  return ((Object *)obj)->arrayGetLength();+}++XpdfObject _xpdfArrayGet(XpdfObject obj, int idx) {+  Object *elem;++  elem = allocObj();+  if (!((Object *)obj)->isArray()) {+    return (XpdfObject)elem->initNull();+  }+  return (XpdfObject)((Object *)obj)->arrayGet(idx, elem);+}++XpdfObject _xpdfDictGet(XpdfObject obj, char *key) {+  Object *elem;++  elem = allocObj();+  if (!((Object *)obj)->isDict()) {+    return (XpdfObject)elem->initNull();+  }+  return (XpdfObject)((Object *)obj)->dictLookup(key, elem);+}++void _xpdfFreeObj(XpdfObject obj) {+  ((Object *)obj)->free();+  gfree(obj);+}++//------------------------------------------------------------------------+// Memory allocation functions+//------------------------------------------------------------------------++void *_xpdfMalloc(int size) {+  return gmalloc(size);+}++void *_xpdfRealloc(void *p, int size) {+  return grealloc(p, size);+}++void _xpdfFree(void *p) {+  gfree(p);+}++//------------------------------------------------------------------------+// Security handlers+//------------------------------------------------------------------------++void _xpdfRegisterSecurityHandler(XpdfSecurityHandler *handler) {+  if (handler->version <= xpdfPluginAPIVersion) {+    globalParams->addSecurityHandler(handler);+  }+}++//------------------------------------------------------------------------++XpdfPluginVecTable xpdfPluginVecTable = {+  xpdfPluginAPIVersion,+  &_xpdfGetInfoDict,+  &_xpdfGetCatalog,+#ifdef _WIN32+  &_xpdfWin32GetWindow,+#else+  &_xpdfXGetWindow,+#endif+  &_xpdfObjIsBool,+  &_xpdfObjIsInt,+  &_xpdfObjIsReal,+  &_xpdfObjIsString,+  &_xpdfObjIsName,+  &_xpdfObjIsNull,+  &_xpdfObjIsArray,+  &_xpdfObjIsDict,+  &_xpdfObjIsStream,+  &_xpdfObjIsRef,+  &_xpdfBoolValue,+  &_xpdfIntValue,+  &_xpdfRealValue,+  &_xpdfStringLength,+  &_xpdfStringValue,+  &_xpdfNameValue,+  &_xpdfArrayLength,+  &_xpdfArrayGet,+  &_xpdfDictGet,+  &_xpdfFreeObj,+  &_xpdfMalloc,+  &_xpdfRealloc,+  &_xpdfFree,+  &_xpdfRegisterSecurityHandler,+};++#endif // ENABLE_PLUGINS
+ xpdf/XpdfPluginAPI.h view
@@ -0,0 +1,341 @@+/*+ * XpdfPluginAPI.h+ *+ * Copyright 2004 Glyph & Cog, LLC+ */++#ifndef XPDFPLUGINAPI_H+#define XPDFPLUGINAPI_H++#ifdef _WIN32+#include <windows.h>+#else+#define Object XtObject+#include <X11/Intrinsic.h>+#undef Object+#endif++#ifdef __cplusplus+extern "C" {+#endif++/*------------------------------------------------------------------------+ * Macros+ *------------------------------------------------------------------------*/++/*+ * The current API version.+ */+#define xpdfPluginAPIVersion 1++#ifdef _WIN32+#  ifdef __cplusplus+#    define PLUGINFUNC(retType) extern "C" __declspec(dllexport) retType+#  else+#    define PLUGINFUNC(retType) extern __declspec(dllexport) retType+#  endif+#else+#  ifdef __cplusplus+#    define PLUGINFUNC(retType) extern "C" retType+#  else+#    define PLUGINFUNC(retType) extern retType+#  endif+#endif++/*------------------------------------------------------------------------+ * Plugin setup/cleanup+ *------------------------------------------------------------------------*/++/*+ * All plugins are required to implement two functions:+ *+ * -- Initialize the plugin.  Returns non-zero if successful.+ * PLUGINFUNC(XpdfBool) xpdfInitPlugin(void);+ *+ * -- Free the plugin.+ * PLUGINFUNC(void) xpdfFreePlugin(void);+ */++/*------------------------------------------------------------------------+ * Types+ *------------------------------------------------------------------------*/++/*+ * Standard C boolean -- zero = false, non-zero = true.+ */+typedef int XpdfBool;+#define xpdfTrue  1+#define xpdfFalse 0++/*+ * PDF document handle.+ */+typedef struct _XpdfDoc *XpdfDoc;++/*+ * PDF object handle.+ */+typedef struct _XpdfObject *XpdfObject;++/*+ * Document access permissions.  Any of these can be bitwise 'or'ed+ * together.  If xpdfPermissionOpen is not included, the document+ * cannot be opened at all, and the other bits are ignored.+ */+typedef unsigned int XpdfPermission;+#define xpdfPermissionOpen     (1 << 0)+#define xpdfPermissionPrint    (1 << 2)+#define xpdfPermissionChange   (1 << 3)+#define xpdfPermissionCopy     (1 << 4)+#define xpdfPermissionNotes    (1 << 5)++/*------------------------------------------------------------------------+ * Security handler+ *------------------------------------------------------------------------*/++/*+ * XpdfSecurityHandler - a security handler plugin should create one+ * of these and pass it to xpdfRegisterSecurityHandler.+ */+#ifdef __cplusplus+struct XpdfSecurityHandler {+#else+typedef struct {+#endif++  /*+   * Version of the security handler spec (this document) -- use+   * xpdfPluginAPIVersion.+   */+  int version;++  /*+   * Security handler name.+   */+  char *name;++  /*+   * Any global data the security handler needs.  XpdfViewer will pass+   * this pointer to all handler functions as the <handlerData>+   * argument.+   */+  void *handlerData;++  /*+   * Allocate and initialize data for a new document.  XpdfViewer will+   * pass the returned pointer to all other handler functions as the+   * <docData> argument.  Returns non-zero if successful.+   */+  XpdfBool (*newDoc)(void *handlerData, XpdfDoc doc,+		     XpdfObject encryptDict, void **docData);++  /*+   * Free the data allocated by newDoc.+   */+  void (*freeDoc)(void *handlerData, void *docData);++  /*+   * Construct authorization data based on the supplied owner and user+   * passwords (either or both of which may be NULL).  This function+   * is called in "batch" mode, i.e., if the password was supplied on+   * the command line or via an Xpdf library API.  It should not+   * generate any user interaction (e.g., a password dialog).  It is+   * not required to support this function: the makeAuthData function+   * pointer can be set to NULL.  Returns non-zero if successful.+   */+  XpdfBool (*makeAuthData)(void *handlerData, void *docData,+			   char *ownerPassword, char *userPassword,+			   void **authData);++  /*+   * Request any needed information (e.g., a password) from the user,+   * and construct an authorization data object.  Returns non-zero if+   * successful.+   */+  XpdfBool (*getAuthData)(void *handlerData, void *docData,+			  void **authData);++  /*+   * Free the data allocated by getAuthData.+   */+  void (*freeAuthData)(void *handlerData, void *docData,+		       void *authData);++  /*+   * Request permission to access the document.  This returns all+   * permissions granted by authData.+   */+  XpdfPermission (*authorize)(void *handlerData, void *docData,+			      void *authData);++  /*+   * Get the decryption key and algorithm version associated with the+   * document.  Returns non-zero if successful.+   */+  XpdfBool (*getKey)(void *handlerData, void *docData,+		     char **key, int *keyLen, int *cryptVersion);++  /*+   * Free the data allocated by getKey.+   */+  void (*freeKey)(void *handlerData, void *docData,+		  char *key, int keyLen);++#ifdef __cplusplus+};+#else+} XpdfSecurityHandler;+#endif++/*------------------------------------------------------------------------*/++typedef struct {+  int version;++/*------------------------------------------------------------------------+ * Document access functions+ *------------------------------------------------------------------------*/++/*+ * Get a document's info dictionary.  (The returned object must be+ * freed with xpdfFreeObj.)+ */+XpdfObject (*_xpdfGetInfoDict)(XpdfDoc doc);++/*+ * Get a document's catalog ("root") dictionary.  (The returned object+ * must be freed with xpdfFreeObj.)+ */+XpdfObject (*_xpdfGetCatalog)(XpdfDoc doc);++#ifdef _WIN32++/*+ * Get the handle for the viewer window associated with the specified+ * document.  [Win32 only]+ */+HWND (*_xpdfWin32GetWindow)(XpdfDoc doc);++#else++/*+ * Get the Motif widget for the viewer window associated with the+ * specified document.  [X only]+ */+Widget (*_xpdfXGetWindow)(XpdfDoc doc);++#endif++/*------------------------------------------------------------------------+ * Object access functions+ *------------------------------------------------------------------------*/++/*+ * Check an object's type.+ */+XpdfBool (*_xpdfObjIsBool)(XpdfObject obj);+XpdfBool (*_xpdfObjIsInt)(XpdfObject obj);+XpdfBool (*_xpdfObjIsReal)(XpdfObject obj);+XpdfBool (*_xpdfObjIsString)(XpdfObject obj);+XpdfBool (*_xpdfObjIsName)(XpdfObject obj);+XpdfBool (*_xpdfObjIsNull)(XpdfObject obj);+XpdfBool (*_xpdfObjIsArray)(XpdfObject obj);+XpdfBool (*_xpdfObjIsDict)(XpdfObject obj);+XpdfBool (*_xpdfObjIsStream)(XpdfObject obj);+XpdfBool (*_xpdfObjIsRef)(XpdfObject obj);++/*+ * Value access.+ * (Objects returned by xpdfArrayGet and xpdfDictGet must be freed+ * with xpdfFreeObj.)+ */+XpdfBool (*_xpdfBoolValue)(XpdfObject obj);+int (*_xpdfIntValue)(XpdfObject obj);+double (*_xpdfRealValue)(XpdfObject obj);+int (*_xpdfStringLength)(XpdfObject obj);+char *(*_xpdfStringValue)(XpdfObject obj);+char *(*_xpdfNameValue)(XpdfObject obj);+int (*_xpdfArrayLength)(XpdfObject obj);+XpdfObject (*_xpdfArrayGet)(XpdfObject obj, int idx);+XpdfObject (*_xpdfDictGet)(XpdfObject obj, char *key);++/*+ * Object destruction.  NB: *all* objects must be freed after use.+ */+void (*_xpdfFreeObj)(XpdfObject obj);++/*------------------------------------------------------------------------+ * Memory allocation functions+ *------------------------------------------------------------------------*/++void *(*_xpdfMalloc)(int size);+void *(*_xpdfRealloc)(void *p, int size);+void (*_xpdfFree)(void *p);++/*------------------------------------------------------------------------+ * Security handler functions+ *------------------------------------------------------------------------*/++/*+ * Register a new security handler.+ */+void (*_xpdfRegisterSecurityHandler)(XpdfSecurityHandler *handler);++/*------------------------------------------------------------------------*/++} XpdfPluginVecTable;++#ifdef _WIN32++extern __declspec(dllexport) XpdfPluginVecTable xpdfPluginVecTable;++#define xpdfPluginSetup \+  extern __declspec(dllexport) \+  XpdfPluginVecTable xpdfPluginVecTable = {xpdfPluginAPIVersion};++#else++extern XpdfPluginVecTable xpdfPluginVecTable;++#define xpdfPluginSetup \+  XpdfPluginVecTable xpdfPluginVecTable = {xpdfPluginAPIVersion};++#endif++#define xpdfGetInfoDict (*xpdfPluginVecTable._xpdfGetInfoDict)+#define xpdfGetCatalog (*xpdfPluginVecTable._xpdfGetCatalog)+#ifdef _WIN32+#define xpdfWin32GetWindow (*xpdfPluginVecTable._xpdfWin32GetWindow)+#else+#define xpdfXGetWindow (*xpdfPluginVecTable._xpdfXGetWindow)+#endif+#define xpdfObjIsBool (*xpdfPluginVecTable._xpdfObjIsBool)+#define xpdfObjIsInt (*xpdfPluginVecTable._xpdfObjIsInt)+#define xpdfObjIsReal (*xpdfPluginVecTable._xpdfObjIsReal)+#define xpdfObjIsString (*xpdfPluginVecTable._xpdfObjIsString)+#define xpdfObjIsName (*xpdfPluginVecTable._xpdfObjIsName)+#define xpdfObjIsNull (*xpdfPluginVecTable._xpdfObjIsNull)+#define xpdfObjIsArray (*xpdfPluginVecTable._xpdfObjIsArray)+#define xpdfObjIsDict (*xpdfPluginVecTable._xpdfObjIsDict)+#define xpdfObjIsStream (*xpdfPluginVecTable._xpdfObjIsStream)+#define xpdfObjIsRef (*xpdfPluginVecTable._xpdfObjIsRef)+#define xpdfBoolValue (*xpdfPluginVecTable._xpdfBoolValue)+#define xpdfIntValue (*xpdfPluginVecTable._xpdfIntValue)+#define xpdfRealValue (*xpdfPluginVecTable._xpdfRealValue)+#define xpdfStringLength (*xpdfPluginVecTable._xpdfStringLength)+#define xpdfStringValue (*xpdfPluginVecTable._xpdfStringValue)+#define xpdfNameValue (*xpdfPluginVecTable._xpdfNameValue)+#define xpdfArrayLength (*xpdfPluginVecTable._xpdfArrayLength)+#define xpdfArrayGet (*xpdfPluginVecTable._xpdfArrayGet)+#define xpdfDictGet (*xpdfPluginVecTable._xpdfDictGet)+#define xpdfFreeObj (*xpdfPluginVecTable._xpdfFreeObj)+#define xpdfMalloc (*xpdfPluginVecTable._xpdfMalloc)+#define xpdfRealloc (*xpdfPluginVecTable._xpdfRealloc)+#define xpdfFree (*xpdfPluginVecTable._xpdfFree)+#define xpdfRegisterSecurityHandler (*xpdfPluginVecTable._xpdfRegisterSecurityHandler)++#ifdef __cplusplus+}+#endif++#endif
+ xpdf/about-text.h view
@@ -0,0 +1,48 @@+//========================================================================+//+// about-text.h+//+// Copyright 2002-2007 Glyph & Cog, LLC+//+//========================================================================++static char *aboutWinText[] = {+  "http://www.foolabs.com/xpdf/",+  "derekn@foolabs.com",+  " ",+  "Licensed under the GNU General Public License (GPL).",+  "See the 'COPYING' file for details.",+  " ",+  "Supports PDF version " supportedPDFVersionStr ".",+  " ",+  "The PDF data structures, operators, and specification",+  "are copyright 1985-2006 Adobe Systems Inc.",+  " ",+  "Mouse bindings:",+  "  button 1: select text / follow link",+  "  button 2: pan window",+  "  button 3: menu",+  " ",+  "Key bindings:",+  "  o              = open file",+  "  r              = reload",+  "  f / ctrl-F     = find text",+  "  ctrl-G         = find next",+  "  ctrl-P         = print",+  "  n              = next page",+  "  p              = previous page",+  "  <PgDn> = <space>                = scroll down",+  "  <PgUp> = <backspace> = <delete> = scroll up",+  "  v              = forward (history path)",+  "  b              = backward (history path)",+  "  0 / + / -      = zoom zero / in / out",+  "  z / w          = zoom page / page width",+  "  alt-F          = toggle full-screen mode",+  "  ctrl-L         = redraw",+  "  q              = quit",+  "  <home> / <end> = top / bottom of page",+  "  <arrows>       = scroll",+  " ",+  "For more information, please read the xpdf(1) man page.",+  NULL+};
+ xpdf/aconf.h view
@@ -0,0 +1,116 @@+/* aconf.h.  Generated by configure.  */+/*+ * aconf.h+ *+ * Copyright 2002-2003 Glyph & Cog, LLC+ */++#ifndef ACONF_H+#define ACONF_H++#include <aconf2.h>++/*+ * Use A4 paper size instead of Letter for PostScript output.+ */+/* #undef A4_PAPER */++/*+ * Do not allow text selection.+ */+/* #undef NO_TEXT_SELECT */++/*+ * Include support for OPI comments.+ */+/* #undef OPI_SUPPORT */++/*+ * Enable multithreading support.+ */+/* #undef MULTITHREADED */++/*+ * Enable C++ exceptions.+ */+/* #undef USE_EXCEPTIONS */++/*+ * Enable word list support.+ */+/* #undef TEXTOUT_WORD_LIST */++/*+ * Use fixed point (instead of floating point) arithmetic.+ */+/* #undef USE_FIXEDPOINT */++/*+ * Directory with the Xpdf app-defaults file.+ */+/* #undef APPDEFDIR */++/*+ * Full path for the system-wide xpdfrc file.+ */+#define SYSTEM_XPDFRC "/usr/local/etc/xpdfrc"++/*+ * Various include files and functions.+ */+#define HAVE_DIRENT_H 1+/* #undef HAVE_SYS_NDIR_H */+/* #undef HAVE_SYS_DIR_H */+/* #undef HAVE_NDIR_H */+/* #undef HAVE_SYS_SELECT_H */+/* #undef HAVE_SYS_BSDTYPES_H */+/* #undef HAVE_STRINGS_H */+/* #undef HAVE_BSTRING_H */+#define HAVE_POPEN 1+#define HAVE_MKSTEMP 1+#define HAVE_MKSTEMPS 1+/* #undef SELECT_TAKES_INT */+#define HAVE_FSEEKO 1+/* #undef HAVE_FSEEK64 */+/* #undef _FILE_OFFSET_BITS */+/* #undef _LARGE_FILES */+/* #undef _LARGEFILE_SOURCE */+/* #undef HAVE_XTAPPSETEXITFLAG */++/*+ * This is defined if using libXpm.+ */+/* #undef HAVE_X11_XPM_H */++/*+ * This is defined if using t1lib.+ */+/* #undef HAVE_T1LIB_H */++/*+ * One of these is defined if using FreeType 2.+ */+/* #undef HAVE_FREETYPE_H */+/* #undef HAVE_FREETYPE_FREETYPE_H */++/*+ * This is defined if using libpaper.+ */+/* #undef HAVE_PAPER_H */++/*+ * Enable support for loading plugins.+ */+/* #undef ENABLE_PLUGINS */++/*+ * Defined if the Splash library is avaiable.+ */+/* #undef HAVE_SPLASH */++/*+ * Enable support for CMYK output.+ */+/* #undef SPLASH_CMYK */++#endif
+ xpdf/aconf2.h view
@@ -0,0 +1,38 @@+/*+ * 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.+ */+#ifdef __GNUC__+#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++/*+ * Make sure WIN32 is defined if appropriate.+ */+#if defined(_WIN32) && !defined(WIN32)+#  define WIN32+#endif++#endif
+ xpdf/config.h view
@@ -0,0 +1,112 @@+//========================================================================+//+// config.h+//+// Copyright 1996-2007 Glyph & Cog, LLC+//+//========================================================================++#ifndef CONFIG_H+#define CONFIG_H++//------------------------------------------------------------------------+// version+//------------------------------------------------------------------------++// xpdf version+#define xpdfVersion          "3.02"+#define xpdfVersionNum       3.02+#define xpdfMajorVersion     3+#define xpdfMinorVersion     2+#define xpdfUpdateVersion    0+#define xpdfMajorVersionStr  "3"+#define xpdfMinorVersionStr  "2"+#define xpdfUpdateVersionStr "0"++// supported PDF version+#define supportedPDFVersionStr "1.7"+#define supportedPDFVersionNum 1.7++// copyright notice+#define xpdfCopyright "Copyright 1996-2007 Glyph & Cog, LLC"++// Windows resource file stuff+#define winxpdfVersion "WinXpdf 3.02"+#define xpdfCopyrightAmp "Copyright 1996-2007 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) && !defined(__CYGWIN32__))+#define xpdfUserConfigFile "xpdfrc"+#else+#define xpdfUserConfigFile ".xpdfrc"+#endif++// system config file name (set via the configure script)+#ifdef SYSTEM_XPDFRC+#define xpdfSysConfigFile SYSTEM_XPDFRC+#else+// under Windows, we get the directory with the executable and then+// append this file name+#define xpdfSysConfigFile "xpdfrc"+#endif++//------------------------------------------------------------------------+// X-related constants+//------------------------------------------------------------------------++// default maximum size of color cube to allocate+#define defaultRGBCube 5++// number of fonts (combined t1lib, FreeType, X server) to cache+#define xOutFontCacheSize 64++// number of Type 3 fonts to cache+#define xOutT3FontCacheSize 8++//------------------------------------------------------------------------+// popen+//------------------------------------------------------------------------++#if defined(_MSC_VER) || defined(__BORLANDC__)+#define popen _popen+#define pclose _pclose+#endif++#if defined(VMS) || defined(VMCMS) || defined(DOS) || defined(OS2) || defined(__EMX__) || defined(WIN32) || defined(__DJGPP__) || defined(MACOS)+#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
+ xpdf/gfile.cc view
@@ -0,0 +1,731 @@+//========================================================================+//+// gfile.cc+//+// Miscellaneous file and directory name manipulation.+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>++#ifdef WIN32+#  include <time.h>+#else+#  if defined(MACOS)+#    include <sys/stat.h>+#  elif !defined(ACORN)+#    include <sys/types.h>+#    include <sys/stat.h>+#    include <fcntl.h>+#  endif+#  include <limits.h>+#  include <string.h>+#  if !defined(VMS) && !defined(ACORN) && !defined(MACOS)+#    include <pwd.h>+#  endif+#  if defined(VMS) && (__DECCXX_VER < 50200000)+#    include <unixlib.h>+#  endif+#endif // WIN32+#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;++  if ((s = getenv("HOME")))+    ret = new GString(s);+  else+    ret = new GString(".");+  return ret;++#elif defined(ACORN)+  //---------- RISCOS ----------+  return new GString("@");++#elif defined(MACOS)+  //---------- MacOS ----------+  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 (GetCurrentDirectory(sizeof(buf), buf))+#elif defined(ACORN)+  if (strcpy(buf, "@"))+#elif defined(MACOS)+  if (strcpy(buf, ":"))+#else+  if (getcwd(buf, sizeof(buf)))+#endif+    return new GString(buf);+  return new GString();+}++GString *appendToPath(GString *path, 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);+  GetFullPathName(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(MACOS)+  //---------- MacOS ----------+  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, p - fileName);+  if ((p = strrchr(fileName, '\\')))+    return new GString(fileName, p - fileName);+  if ((p = strrchr(fileName, ':')))+    return new GString(fileName, 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();++#elif defined(MACOS)+  //---------- MacOS ----------+  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, 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] == '$';++#elif defined(MACOS)+  //---------- MacOS ----------+  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 (!GetFullPathName(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;++#elif defined(MACOS)+  //---------- MacOS ----------+  path->del(0, 1);+  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 = p2 - p1) > PATH_MAX)+	n = PATH_MAX;+      strncpy(buf, p1, n);+      buf[n] = '\0';+      if ((pw = getpwnam(buf))) {+	path->del(0, 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+}++GBool openTempFile(GString **name, FILE **f, char *mode, char *ext) {+#if defined(WIN32)+  //---------- Win32 ----------+  char *tempDir;+  GString *s, *s2;+  char buf[32];+  FILE *f2;+  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+  if ((tempDir = getenv("TEMP"))) {+    s = new GString(tempDir);+    s->append('\\');+  } else {+    s = new GString();+  }+  s->append("x");+  t = (int)time(NULL);+  for (i = 0; i < 1000; ++i) {+    sprintf(buf, "%d", t + i);+    s2 = s->copy()->append(buf);+    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) || defined(MACOS)+  //---------- 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);+    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(), 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;+    return gFalse;+  }+  return gTrue;+#endif+}++GBool executeCommand(char *cmd) {+#ifdef VMS+  return system(cmd) ? gTrue : gFalse;+#else+  return system(cmd) ? gFalse : gTrue;+#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;+}++//------------------------------------------------------------------------+// GDir and GDirEntry+//------------------------------------------------------------------------++GDirEntry::GDirEntry(char *dirPath, char *nameA, GBool doStat) {+#ifdef VMS+  char *p;+#elif defined(WIN32)+  int fa;+  GString *s;+#elif defined(ACORN)+#else+  struct stat st;+  GString *s;+#endif++  name = new GString(nameA);+  dir = gFalse;+  if (doStat) {+#ifdef VMS+    if (!strcmp(nameA, "-") ||+	((p = strrchr(nameA, '.')) && !strncmp(p, ".DIR;", 5)))+      dir = gTrue;+#elif defined(ACORN)+#else+    s = new GString(dirPath);+    appendToPath(s, nameA);+#ifdef WIN32+    fa = GetFileAttributes(s->getCString());+    dir = (fa != 0xFFFFFFFF && (fa & FILE_ATTRIBUTE_DIRECTORY));+#else+    if (stat(s->getCString(), &st) == 0)+      dir = S_ISDIR(st.st_mode);+#endif+    delete s;+#endif+  }+}++GDirEntry::~GDirEntry() {+  delete name;+}++GDir::GDir(char *name, GBool doStatA) {+  path = new GString(name);+  doStat = doStatA;+#if defined(WIN32)+  GString *tmp;++  tmp = path->copy();+  tmp->append("/*.*");+  hnd = FindFirstFile(tmp->getCString(), &ffd);+  delete tmp;+#elif defined(ACORN)+#elif defined(MACOS)+#else+  dir = opendir(name);+#ifdef VMS+  needParent = strchr(name, '[') != NULL;+#endif+#endif+}++GDir::~GDir() {+  delete path;+#if defined(WIN32)+  if (hnd) {+    FindClose(hnd);+    hnd = NULL;+  }+#elif defined(ACORN)+#elif defined(MACOS)+#else+  if (dir)+    closedir(dir);+#endif+}++GDirEntry *GDir::getNextEntry() {+  GDirEntry *e;++#if defined(WIN32)+  if (hnd) {+    e = new GDirEntry(path->getCString(), ffd.cFileName, doStat);+    if (hnd  && !FindNextFile(hnd, &ffd)) {+      FindClose(hnd);+      hnd = NULL;+    }+  } else {+    e = NULL;+  }+#elif defined(ACORN)+#elif defined(MACOS)+#elif defined(VMS)+  struct dirent *ent;+  e = NULL;+  if (dir) {+    if (needParent) {+      e = new GDirEntry(path->getCString(), "-", doStat);+      needParent = gFalse;+      return e;+    }+    ent = readdir(dir);+    if (ent) {+      e = new GDirEntry(path->getCString(), ent->d_name, doStat);+    }+  }+#else+  struct dirent *ent;+  e = NULL;+  if (dir) {+    ent = (struct dirent *)readdir(dir);+    if (ent && !strcmp(ent->d_name, ".")) {+      ent = (struct dirent *)readdir(dir);+    }+    if (ent) {+      e = new GDirEntry(path->getCString(), ent->d_name, doStat);+    }+  }+#endif++  return e;+}++void GDir::rewind() {+#ifdef WIN32+  GString *tmp;++  if (hnd)+    FindClose(hnd);+  tmp = path->copy();+  tmp->append("/*.*");+  hnd = FindFirstFile(tmp->getCString(), &ffd);+  delete tmp;+#elif defined(ACORN)+#elif defined(MACOS)+#else+  if (dir)+    rewinddir(dir);+#ifdef VMS+  needParent = strchr(path->getCString(), '[') != NULL;+#endif+#endif+}
+ xpdf/gfile.h view
@@ -0,0 +1,138 @@+//========================================================================+//+// gfile.h+//+// Miscellaneous file and directory name manipulation.+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#ifndef GFILE_H+#define GFILE_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(MACOS)+#  include <ctime.h>+#else+#  include <unistd.h>+#  include <sys/types.h>+#  ifdef VMS+#    include "vms_dirent.h"+#  elif HAVE_DIRENT_H+#    include <dirent.h>+#    define NAMLEN(d) strlen((d)->d_name)+#  else+#    define dirent direct+#    define NAMLEN(d) (d)->d_namlen+#    if HAVE_SYS_NDIR_H+#      include <sys/ndir.h>+#    endif+#    if HAVE_SYS_DIR_H+#      include <sys/dir.h>+#    endif+#    if HAVE_NDIR_H+#      include <ndir.h>+#    endif+#  endif+#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, 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, char *mode, char *ext);++// Execute <command>.  Returns true on success.+extern GBool executeCommand(char *cmd);++// Just like fgets, but handles Unix, Mac, and/or DOS end-of-line+// conventions.+extern char *getLine(char *buf, int size, FILE *f);++//------------------------------------------------------------------------+// GDir and GDirEntry+//------------------------------------------------------------------------++class GDirEntry {+public:++  GDirEntry(char *dirPath, char *nameA, GBool doStat);+  ~GDirEntry();+  GString *getName() { return name; }+  GBool isDir() { return dir; }++private:++  GString *name;		// dir/file name+  GBool dir;			// is it a directory?+};++class GDir {+public:++  GDir(char *name, GBool doStatA = gTrue);+  ~GDir();+  GDirEntry *getNextEntry();+  void rewind();++private:++  GString *path;		// directory path+  GBool doStat;			// call stat() for each entry?+#if defined(WIN32)+  WIN32_FIND_DATA ffd;+  HANDLE hnd;+#elif defined(ACORN)+#elif defined(MACOS)+#else+  DIR *dir;			// the DIR structure from opendir()+#ifdef VMS+  GBool needParent;		// need to return an entry for [-]+#endif+#endif+};++#endif
+ xpdf/gmem.cc view
@@ -0,0 +1,264 @@+/*+ * 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>+#include <string.h>+#include <limits.h>+#include "gmem.h"++#ifdef DEBUG_MEM++typedef struct _GMemHdr {+  unsigned int magic;+  int size;+  int index;+  struct _GMemHdr *next, *prev;+} GMemHdr;++#define gMemHdrSize ((sizeof(GMemHdr) + 7) & ~7)+#define gMemTrlSize (sizeof(long))++#define gMemMagic 0xabcd9999++#if gmemTrlSize==8+#define gMemDeadVal 0xdeadbeefdeadbeefUL+#else+#define gMemDeadVal 0xdeadbeefUL+#endif++/* round data size so trailer will be aligned */+#define gMemDataSize(size) \+  ((((size) + gMemTrlSize - 1) / gMemTrlSize) * gMemTrlSize)++static GMemHdr *gMemHead = NULL;+static GMemHdr *gMemTail = NULL;++static int gMemIndex = 0;+static int gMemAlloc = 0;+static int gMemInUse = 0;++#endif /* DEBUG_MEM */++void *gmalloc(int size) GMEM_EXCEP {+#ifdef DEBUG_MEM+  int size1;+  char *mem;+  GMemHdr *hdr;+  void *data;+  unsigned long *trl, *p;++  if (size <= 0) {+    return NULL;+  }+  size1 = gMemDataSize(size);+  if (!(mem = (char *)malloc(size1 + gMemHdrSize + gMemTrlSize))) {+#if USE_EXCEPTIONS+    throw GMemException();+#else+    fprintf(stderr, "Out of memory\n");+    exit(1);+#endif+  }+  hdr = (GMemHdr *)mem;+  data = (void *)(mem + gMemHdrSize);+  trl = (unsigned long *)(mem + gMemHdrSize + size1);+  hdr->magic = gMemMagic;+  hdr->size = size;+  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;+  for (p = (unsigned long *)data; p <= trl; ++p) {+    *p = gMemDeadVal;+  }+  return data;+#else+  void *p;++  if (size <= 0) {+    return NULL;+  }+  if (!(p = malloc(size))) {+#if USE_EXCEPTIONS+    throw GMemException();+#else+    fprintf(stderr, "Out of memory\n");+    exit(1);+#endif+  }+  return p;+#endif+}++void *grealloc(void *p, int size) GMEM_EXCEP {+#ifdef DEBUG_MEM+  GMemHdr *hdr;+  void *q;+  int oldSize;++  if (size <= 0) {+    if (p) {+      gfree(p);+    }+    return NULL;+  }+  if (p) {+    hdr = (GMemHdr *)((char *)p - gMemHdrSize);+    oldSize = 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) {+    if (p) {+      free(p);+    }+    return NULL;+  }+  if (p) {+    q = realloc(p, size);+  } else {+    q = malloc(size);+  }+  if (!q) {+#if USE_EXCEPTIONS+    throw GMemException();+#else+    fprintf(stderr, "Out of memory\n");+    exit(1);+#endif+  }+  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) {+#if USE_EXCEPTIONS+    throw GMemException();+#else+    fprintf(stderr, "Bogus memory allocation size\n");+    exit(1);+#endif+  }+  return gmalloc(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) {+#if USE_EXCEPTIONS+    throw GMemException();+#else+    fprintf(stderr, "Bogus memory allocation size\n");+    exit(1);+#endif+  }+  return grealloc(p, n);+}++void gfree(void *p) {+#ifdef DEBUG_MEM+  int size;+  GMemHdr *hdr;+  unsigned long *trl, *clr;++  if (p) {+    hdr = (GMemHdr *)((char *)p - gMemHdrSize);+    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;+      size = gMemDataSize(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 {+      fprintf(stderr, "Attempted to free bad address %p\n", p);+    }+  }+#else+  if (p) {+    free(p);+  }+#endif+}++#ifdef DEBUG_MEM+void gMemReport(FILE *f) {+  GMemHdr *p;++  fprintf(f, "%d memory allocations in all\n", gMemIndex);+  if (gMemAlloc > 0) {+    fprintf(f, "%d memory blocks left allocated:\n", gMemAlloc);+    fprintf(f, " index     size\n");+    fprintf(f, "-------- --------\n");+    for (p = gMemHead; p; p = p->next) {+      fprintf(f, "%8d %8d\n", p->index, p->size);+    }+  } else {+    fprintf(f, "No memory blocks left allocated\n");+  }+}+#endif++char *copyString(char *s) {+  char *s1;++  s1 = (char *)gmalloc(strlen(s) + 1);+  strcpy(s1, s);+  return s1;+}
+ xpdf/gmem.h view
@@ -0,0 +1,79 @@+/*+ * 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() {}+};++#define GMEM_EXCEP throw(GMemException)++#else // USE_EXCEPTIONS++#define GMEM_EXCEP++#endif // USE_EXCEPTIONS++#ifdef __cplusplus+extern "C" {+#endif++/*+ * Same as malloc, but prints error message and exits if malloc()+ * returns NULL.+ */+extern void *gmalloc(int size) GMEM_EXCEP;++/*+ * 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 free, but checks for and ignores NULL pointers.+ */+extern void gfree(void *p);++#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(char *s);++#ifdef __cplusplus+}+#endif++#endif
+ xpdf/gmempp.cc view
@@ -0,0 +1,32 @@+//========================================================================+//+// gmempp.cc+//+// Use gmalloc/gfree for C++ new/delete operators.+//+// Copyright 1996-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>+#include "gmem.h"++#ifdef DEBUG_MEM++void *operator new(size_t size) {+  return gmalloc((int)size);+}++void *operator new[](size_t size) {+  return gmalloc((int)size);+}++void operator delete(void *p) {+  gfree(p);+}++void operator delete[](void *p) {+  gfree(p);+}++#endif
+ xpdf/gtypes.h view
@@ -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
+ xpdf/parseargs.c view
@@ -0,0 +1,190 @@+/*+ * 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 {+      ++i;+    }+  }+  return ok;+}++void printUsage(char *program, char *otherArgs, ArgDesc *args) {+  ArgDesc *arg;+  char *typ;+  int w, w1;++  w = 0;+  for (arg = args; arg->arg; ++arg) {+    if ((w1 = 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 - strlen(arg->arg);+    switch (arg->kind) {+    case argInt:+    case argIntDummy:+      typ = " <int>";+      break;+    case argFP:+    case argFPDummy:+      typ = " <fp>";+      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;+}
+ xpdf/parseargs.h view
@@ -0,0 +1,71 @@+/*+ * 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 {+  char *arg;			/* the command line switch */+  ArgKind kind;			/* kind of arg */+  void *val;			/* place to store value */+  int size;			/* for argString: size of string */+  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(char *program, 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
+ xpdf/pdfdump.cc view
@@ -0,0 +1,326 @@+//========================================================================+//+// pdftotext.cc+//+// Copyright 1997-2003 Glyph & Cog, LLC+//+//========================================================================++#include <aconf.h>+#include <stdio.h>+#include <stdlib.h>+#include <stddef.h>+#include <string.h>+#include "parseargs.h"+#include "GString.h"+#include "gmem.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 "Error.h"+#include "config.h"++static void printInfoString(FILE *f, Dict *infoDict, char *key,+			    char *text1, char *text2, UnicodeMap *uMap);+static void printInfoDate(FILE *f, Dict *infoDict, char *key, char *fmt);++static int firstPage = 1;+static int lastPage = 0;+static GBool physLayout = gFalse;+static GBool rawOrder = gFalse;+static GBool htmlMeta = gFalse;+static char textEncName[128] = "";+static char textEOL[16] = "";+static GBool noPageBreaks = 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"},+  {"-layout",  argFlag,     &physLayout,    0,+   "maintain original physical layout"},+  {"-raw",     argFlag,     &rawOrder,      0,+   "keep strings in content stream order"},+  {"-htmlmeta", argFlag,   &htmlMeta,       0,+   "generate a simple HTML file, including the meta information"},+  {"-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"},+  {"-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;+  GString *fileName;+  GString *textFileName;+  GString *ownerPW, *userPW;+  TextOutputDev *textOut;+  FILE *f;+  UnicodeMap *uMap;+  Object info;+  GBool ok;+  char *p;+  int exitCode;++  exitCode = 99;++  // parse args+  ok = parseArgs(argDesc, &argc, argv);+  if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) {+    fprintf(stderr, "pdftotext version %s\n", xpdfVersion);+    fprintf(stderr, "%s\n", xpdfCopyright);+    if (!printVersion) {+      printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc);+    }+    goto err0;+  }+  fileName = new GString(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(-1, "Couldn't get text encoding");+    delete fileName;+    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;+  }++  // construct text file name+  if (argc == 3) {+    textFileName = new GString(argv[2]);+  } else {+    p = fileName->getCString() + fileName->getLength() - 4;+    if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {+      textFileName = new GString(fileName->getCString(),+				 fileName->getLength() - 4);+    } else {+      textFileName = fileName->copy();+    }+    textFileName->append(htmlMeta ? ".html" : ".txt");+  }++  // get page range+  if (firstPage < 1) {+    firstPage = 1;+  }+  if (lastPage < 1 || lastPage > doc->getNumPages()) {+    lastPage = doc->getNumPages();+  }++  // write HTML header+  if (htmlMeta) {+    if (!textFileName->cmp("-")) {+      f = stdout;+    } else {+      if (!(f = fopen(textFileName->getCString(), "wb"))) {+	error(-1, "Couldn't open text file '%s'", textFileName->getCString());+	exitCode = 2;+	goto err3;+      }+    }+    fputs("<html>\n", f);+    fputs("<head>\n", f);+    doc->getDocInfo(&info);+    if (info.isDict()) {+      printInfoString(f, info.getDict(), "Title", "<title>", "</title>\n",+		      uMap);+      printInfoString(f, info.getDict(), "Subject",+		      "<meta name=\"Subject\" content=\"", "\">\n", uMap);+      printInfoString(f, info.getDict(), "Keywords",+		      "<meta name=\"Keywords\" content=\"", "\">\n", uMap);+      printInfoString(f, info.getDict(), "Author",+		      "<meta name=\"Author\" content=\"", "\">\n", uMap);+      printInfoString(f, info.getDict(), "Creator",+		      "<meta name=\"Creator\" content=\"", "\">\n", uMap);+      printInfoString(f, info.getDict(), "Producer",+		      "<meta name=\"Producer\" content=\"", "\">\n", uMap);+      printInfoDate(f, info.getDict(), "CreationDate",+		    "<meta name=\"CreationDate\" content=\"%s\">\n");+      printInfoDate(f, info.getDict(), "LastModifiedDate",+		    "<meta name=\"ModDate\" content=\"%s\">\n");+    }+    info.free();+    fputs("</head>\n", f);+    fputs("<body>\n", f);+    fputs("<pre>\n", f);+    if (f != stdout) {+      fclose(f);+    }+  }++  // write text file+  textOut = new TextOutputDev(textFileName->getCString(),+			      physLayout, rawOrder, htmlMeta);+  if (textOut->isOk()) {+    doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0,+		      gFalse, gTrue, gFalse);+  } else {+    delete textOut;+    exitCode = 2;+    goto err3;+  }+  delete textOut;++  // write end of HTML file+  if (htmlMeta) {+    if (!textFileName->cmp("-")) {+      f = stdout;+    } else {+      if (!(f = fopen(textFileName->getCString(), "ab"))) {+	error(-1, "Couldn't open text file '%s'", textFileName->getCString());+	exitCode = 2;+	goto err3;+      }+    }+    fputs("</pre>\n", f);+    fputs("</body>\n", f);+    fputs("</html>\n", f);+    if (f != stdout) {+      fclose(f);+    }+  }++  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;+}++static void printInfoString(FILE *f, Dict *infoDict, char *key,+			    char *text1, char *text2, UnicodeMap *uMap) {+  Object obj;+  GString *s1;+  GBool isUnicode;+  Unicode u;+  char buf[8];+  int i, n;++  if (infoDict->lookup(key, &obj)->isString()) {+    fputs(text1, f);+    s1 = obj.getString();+    if ((s1->getChar(0) & 0xff) == 0xfe &&+	(s1->getChar(1) & 0xff) == 0xff) {+      isUnicode = gTrue;+      i = 2;+    } else {+      isUnicode = gFalse;+      i = 0;+    }+    while (i < obj.getString()->getLength()) {+      if (isUnicode) {+	u = ((s1->getChar(i) & 0xff) << 8) |+	    (s1->getChar(i+1) & 0xff);+	i += 2;+      } else {+	u = s1->getChar(i) & 0xff;+	++i;+      }+      n = uMap->mapUnicode(u, buf, sizeof(buf));+      fwrite(buf, 1, n, f);+    }+    fputs(text2, f);+  }+  obj.free();+}++static void printInfoDate(FILE *f, Dict *infoDict, char *key, char *fmt) {+  Object obj;+  char *s;++  if (infoDict->lookup(key, &obj)->isString()) {+    s = obj.getString()->getCString();+    if (s[0] == 'D' && s[1] == ':') {+      s += 2;+    }+    fprintf(f, fmt, s);+  }+  obj.free();+}