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 +339/−0
- LICENSE +6/−0
- Makefile.pdfdump +92/−0
- README +8/−0
- Setup.lhs +14/−0
- pdf2line.cabal +190/−0
- pdf2line.hs +149/−0
- xpdf/Annot.cc +1556/−0
- xpdf/Annot.h +142/−0
- xpdf/Array.cc +73/−0
- xpdf/Array.h +58/−0
- xpdf/BuiltinFont.cc +65/−0
- xpdf/BuiltinFont.h +57/−0
- xpdf/BuiltinFontTables.cc +4284/−0
- xpdf/BuiltinFontTables.h +23/−0
- xpdf/CMap.cc +408/−0
- xpdf/CMap.h +102/−0
- xpdf/Catalog.cc +374/−0
- xpdf/Catalog.h +97/−0
- xpdf/CharCodeToUnicode.cc +540/−0
- xpdf/CharCodeToUnicode.h +117/−0
- xpdf/CharTypes.h +24/−0
- xpdf/CompactFontTables.h +464/−0
- xpdf/CoreOutputDev.cc +61/−0
- xpdf/CoreOutputDev.h +61/−0
- xpdf/Decrypt.cc +776/−0
- xpdf/Decrypt.h +95/−0
- xpdf/Dict.cc +95/−0
- xpdf/Dict.h +77/−0
- xpdf/Error.cc +38/−0
- xpdf/Error.h +23/−0
- xpdf/ErrorCodes.h +36/−0
- xpdf/FixedPoint.cc +118/−0
- xpdf/FixedPoint.h +155/−0
- xpdf/FoFiBase.cc +156/−0
- xpdf/FoFiBase.h +57/−0
- xpdf/FoFiEncodings.cc +994/−0
- xpdf/FoFiEncodings.h +36/−0
- xpdf/FoFiTrueType.cc +2027/−0
- xpdf/FoFiTrueType.h +174/−0
- xpdf/FoFiType1.cc +252/−0
- xpdf/FoFiType1.h +59/−0
- xpdf/FoFiType1C.cc +2603/−0
- xpdf/FoFiType1C.h +233/−0
- xpdf/FontEncodingTables.cc +1824/−0
- xpdf/FontEncodingTables.h +20/−0
- xpdf/Function.cc +1573/−0
- xpdf/Function.h +229/−0
- xpdf/GHash.cc +380/−0
- xpdf/GHash.h +78/−0
- xpdf/GList.cc +97/−0
- xpdf/GList.h +96/−0
- xpdf/GMutex.h +49/−0
- xpdf/GString.cc +718/−0
- xpdf/GString.h +136/−0
- xpdf/Gfx.cc +4181/−0
- xpdf/Gfx.h +312/−0
- xpdf/GfxFont.cc +1568/−0
- xpdf/GfxFont.h +320/−0
- xpdf/GfxState.cc +4137/−0
- xpdf/GfxState.h +1244/−0
- xpdf/GlobalParams.cc +2908/−0
- xpdf/GlobalParams.h +463/−0
- xpdf/ImageOutputDev.cc +195/−0
- xpdf/ImageOutputDev.h +76/−0
- xpdf/JArithmeticDecoder.cc +322/−0
- xpdf/JArithmeticDecoder.h +109/−0
- xpdf/JBIG2Stream.cc +3413/−0
- xpdf/JBIG2Stream.h +145/−0
- xpdf/JPXStream.cc +3144/−0
- xpdf/JPXStream.h +351/−0
- xpdf/Lexer.cc +485/−0
- xpdf/Lexer.h +80/−0
- xpdf/Link.cc +784/−0
- xpdf/Link.h +369/−0
- xpdf/NameToCharCode.cc +116/−0
- xpdf/NameToCharCode.h +42/−0
- xpdf/NameToUnicodeTable.h +1097/−0
- xpdf/Object.cc +231/−0
- xpdf/Object.h +303/−0
- xpdf/Outline.cc +151/−0
- xpdf/Outline.h +76/−0
- xpdf/OutputDev.cc +131/−0
- xpdf/OutputDev.h +250/−0
- xpdf/PDFCore.cc +2044/−0
- xpdf/PDFCore.h +321/−0
- xpdf/PDFDoc.cc +402/−0
- xpdf/PDFDoc.h +183/−0
- xpdf/PDFDocEncoding.cc +44/−0
- xpdf/PDFDocEncoding.h +16/−0
- xpdf/PSOutputDev.cc +6222/−0
- xpdf/PSOutputDev.h +395/−0
- xpdf/PSTokenizer.cc +135/−0
- xpdf/PSTokenizer.h +41/−0
- xpdf/Page.cc +441/−0
- xpdf/Page.h +187/−0
- xpdf/Parser.cc +227/−0
- xpdf/Parser.h +59/−0
- xpdf/PreScanOutputDev.cc +257/−0
- xpdf/PreScanOutputDev.h +130/−0
- xpdf/SecurityHandler.cc +390/−0
- xpdf/SecurityHandler.h +160/−0
- xpdf/Splash.cc +3310/−0
- xpdf/Splash.h +293/−0
- xpdf/SplashBitmap.cc +188/−0
- xpdf/SplashBitmap.h +61/−0
- xpdf/SplashClip.cc +382/−0
- xpdf/SplashClip.h +107/−0
- xpdf/SplashErrorCodes.h +32/−0
- xpdf/SplashFTFont.cc +357/−0
- xpdf/SplashFTFont.h +58/−0
- xpdf/SplashFTFontEngine.cc +179/−0
- xpdf/SplashFTFontEngine.h +65/−0
- xpdf/SplashFTFontFile.cc +114/−0
- xpdf/SplashFTFontFile.h +73/−0
- xpdf/SplashFont.cc +176/−0
- xpdf/SplashFont.h +104/−0
- xpdf/SplashFontEngine.cc +317/−0
- xpdf/SplashFontEngine.h +91/−0
- xpdf/SplashFontFile.cc +55/−0
- xpdf/SplashFontFile.h +60/−0
- xpdf/SplashFontFileID.cc +23/−0
- xpdf/SplashFontFileID.h +30/−0
- xpdf/SplashGlyphBitmap.h +26/−0
- xpdf/SplashMath.h +89/−0
- xpdf/SplashOutputDev.cc +2845/−0
- xpdf/SplashOutputDev.h +247/−0
- xpdf/SplashPath.cc +184/−0
- xpdf/SplashPath.h +121/−0
- xpdf/SplashPattern.cc +40/−0
- xpdf/SplashPattern.h +65/−0
- xpdf/SplashScreen.cc +383/−0
- xpdf/SplashScreen.h +56/−0
- xpdf/SplashState.cc +165/−0
- xpdf/SplashState.h +103/−0
- xpdf/SplashT1Font.cc +287/−0
- xpdf/SplashT1Font.h +57/−0
- xpdf/SplashT1FontEngine.cc +124/−0
- xpdf/SplashT1FontEngine.h +53/−0
- xpdf/SplashT1FontFile.cc +97/−0
- xpdf/SplashT1FontFile.h +58/−0
- xpdf/SplashTypes.h +132/−0
- xpdf/SplashXPath.cc +438/−0
- xpdf/SplashXPath.h +100/−0
- xpdf/SplashXPathScanner.cc +428/−0
- xpdf/SplashXPathScanner.h +87/−0
- xpdf/Stream-CCITT.h +459/−0
- xpdf/Stream.cc +4686/−0
- xpdf/Stream.h +860/−0
- xpdf/TextOutputDev.cc +4103/−0
- xpdf/TextOutputDev.h +661/−0
- xpdf/UTF8.h +56/−0
- xpdf/UnicodeMap.cc +293/−0
- xpdf/UnicodeMap.h +123/−0
- xpdf/UnicodeMapTables.h +361/−0
- xpdf/UnicodeTypeTable.cc +949/−0
- xpdf/UnicodeTypeTable.h +20/−0
- xpdf/XRef.cc +896/−0
- xpdf/XRef.h +133/−0
- xpdf/XpdfPluginAPI.cc +262/−0
- xpdf/XpdfPluginAPI.h +341/−0
- xpdf/about-text.h +48/−0
- xpdf/aconf.h +116/−0
- xpdf/aconf2.h +38/−0
- xpdf/config.h +112/−0
- xpdf/gfile.cc +731/−0
- xpdf/gfile.h +138/−0
- xpdf/gmem.cc +264/−0
- xpdf/gmem.h +79/−0
- xpdf/gmempp.cc +32/−0
- xpdf/gtypes.h +29/−0
- xpdf/parseargs.c +190/−0
- xpdf/parseargs.h +71/−0
- xpdf/pdfdump.cc +326/−0
+ 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,+ ®Key) == 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", ¶ms);+ if (params.isNull()) {+ params.free();+ dict->dictLookup("DP", ¶ms);+ }+ if (obj.isName()) {+ str = makeFilter(obj.getName(), str, ¶ms);+ } else if (obj.isArray()) {+ for (i = 0; i < obj.arrayGetLength(); ++i) {+ obj.arrayGet(i, &obj2);+ if (params.isArray())+ params.arrayGet(i, ¶ms2);+ else+ params2.initNull();+ if (obj2.isName()) {+ str = makeFilter(obj2.getName(), str, ¶ms2);+ } 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();+}