pax_global_header00006660000000000000000000000064136205530200014506gustar00rootroot0000000000000052 comment=7e45faa81deadc4a3b4419a9e76a17782e8034f4 jbig2dec-0.18/000077500000000000000000000000001362055302000131075ustar00rootroot00000000000000jbig2dec-0.18/.gitignore000066400000000000000000000006571362055302000151070ustar00rootroot00000000000000# ignore editor backup files *~ *.swp # ignore generated files .deps .libs m4 configure.ac Makefile Makefile.in aclocal.m4 ar-lib compile config.guess config.h config.h.in config.sub config_types.h config_types.h.in depcomp install-sh libtool ltmain.sh missing stamp-h1 test-driver test_arith test_huffman test_sha1 # ignore object files *.o *.lo # ignore normal build products jbig2dec jbig2dec.pc libjbig2dec.a libjbig2dec.la jbig2dec-0.18/CHANGES000066400000000000000000000071461362055302000141120ustar00rootroot00000000000000Version 0.18 (2020 February 11) * Performance enhancements related to decoding of MMR and generic regions as well as composing images onto pages. * Bug fixes for a few issues reported by Coverity and OSS-Fuzz. Version 0.17 (2019 September 16) * Improved test suite by verifying input file contents and adding all ubc test streams. Fixed bugs where previously missing ubc test streams were decoded incorrectly. * Bug fixes for a few issues reported by Coverity. * Moved version number to jbig2.h, and adapted configure correspondingly. Added pkg-config file to be installed along side library. Version 0.16 (2019 April 04) * API change allowing for library users to get the custom allocator so it may be freed if necessary. This is useful if the allocator is extended with e.g. a custom pointer needed by the allocator callbacks. Version 0.15 (2018 September 04) * Bug fix release, with many security related and stability fixes as well as better tolerance/behaviour with out of spec files Version 0.14 (2017 October 04) * Bug fix release. Version 0.13 (2016 April 07) * Bug fix release. Version 0.12 (2014 October 1) * Bug fix release. Version 0.11 (2010 February 2) * Support for generic regions with typical prediction (042_8.jb2) * Correct bitmap offsets with transposed text (042_19.jb2) * Autotools build now uses libtool and provides a shared library * Manpage for jbig2dec * Code cleanup and robustness fixes Version 0.10 (2009 May 28) * Security fix for malicious symbol dictionaries CVE-2009-0196 * Fix various resource leaks and error handling issues * Dynamically allocate huffman symbol length histogram to save space * Support aggregate symbol coding * Work around invalid Xerox WorkCentre streams which write the final segment length as -1 * Fix an issue with huffman table runcodes Version 0.9 (2006 July 27) * striped page support * successfully decodes ubc test streams 042_9, 042_20 Version 0.8 (2005 April 6) * Fix an allocation error in the page array * properly handle non-OR image composition operators * Fix a UMR bug in the compositor * successfully decodes ubc test streams 042_12,15,16,17,18 * various memory leak fixes Version 0.7 (2004 December 8) * properly initialize page buffers * refinement region handling * successfully decodes ubc test streams 042_21, 042_22 and 042_23 * generic region template 3 handling with arbitrary AT locations * successfully decodes ubc test streams 042_6 and 042_7 Version 0.6 (2003 December 31) * minor portability fix for cygwin Version 0.5 (2003 December 4) * compiler warning fixes * Properly handle the export specification for symbol dictionaries. * successfully decodes multipage documents from the Adobe encoder. Version 0.4 (released 2003 August 1) * redid license header to simplify relabelling for commercial distribution, borrowing from Ghostscript. Version 0.3 (released 2003 May 20) * win32 portability fixes related to ghostscript integration * generic mmr region support * successfully decodes ubc test stream 042_3 Version 0.2 (released 2003 April 17) * portability fixes * support for metadata extension segments (latin-1 only) * decodes single-page documents from the Adobe encoder * various other bugfixes Version 0.1 (released 2002 August 6) * decodes artithmetic and huffman-coded generic regions - some templates not working * decodes arithmetic symbol dictionaries * decodes artithmetic text regions * successfully decodes ubc test streams 042_1,2,4,5 and 10. * successfully decodes CVision embedded bitstream str-p39 * regression testing harness based on SHA-1 hashes of known files jbig2dec-0.18/COPYING000066400000000000000000001033301362055302000141420ustar00rootroot00000000000000 GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . jbig2dec-0.18/LICENSE000066400000000000000000000022221362055302000141120ustar00rootroot00000000000000 The files in this directory (folder) and any subdirectories (sub-folders) thereof are part of jbig2dec, with the exception of certain source files included to support portability which are marked otherwise in their copyright headers. jbig2dec is free software; you can redistribute it and/or modify it under the terms the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This software 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 Affero General Public License along with this program in the file named COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place Suite 330, Boston, MA 02111-1307, USA. In addition, specific permission is given to link jbig2dec to or compile jbig2dec into AFPL Ghostscript and to distribute same under the Aladdin Free Public License (AFPL) version 9. jbig2dec-0.18/Makefile.am000066400000000000000000000032251362055302000151450ustar00rootroot00000000000000## process this file with automake to generate Makefile.in # require automake 1.7 AUTOMAKE_OPTIONS = foreign 1.7 dist-bzip2 dist-zip -Wall ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libjbig2dec.la include_HEADERS = jbig2.h AM_CFLAGS = $(XCFLAGS) libjbig2dec_la_LDFLAGS = -version-info @JBIG2DEC_LT_CURRENT@:@JBIG2DEC_LT_REVISION@:@JBIG2DEC_LT_AGE@ -no-undefined libjbig2dec_la_SOURCES = jbig2.c \ jbig2_arith.c jbig2_arith_int.c jbig2_arith_iaid.c jbig2_huffman.c jbig2_hufftab.c \ jbig2_segment.c jbig2_page.c \ jbig2_symbol_dict.c jbig2_text.c \ jbig2_generic.c jbig2_refinement.c jbig2_mmr.c \ jbig2_halftone.c \ jbig2_image.c jbig2_image_pbm.c \ os_types.h config_types.h config_win32.h \ jbig2.h jbig2_priv.h jbig2_image.h \ jbig2_arith.h jbig2_arith_iaid.h jbig2_arith_int.h \ jbig2_huffman.h jbig2_hufftab.h jbig2_mmr.h \ jbig2_generic.h jbig2_symbol_dict.h jbig2_text.h \ memento.c memento.h bin_PROGRAMS = jbig2dec noinst_PROGRAMS = test_sha1 test_huffman test_arith jbig2dec_SOURCES = jbig2dec.c sha1.c sha1.h \ jbig2.h jbig2_image.h getopt.h \ os_types.h config_types.h config_win32.h jbig2dec_LDADD = libjbig2dec.la @LIBOBJS@ $(PNG_LIBS) pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = jbig2dec.pc dist_man_MANS = jbig2dec.1 EXTRA_DIST = test_jbig2dec.py msvc.mak LICENSE CHANGES MAINTAINERCLEANFILES = config_types.h.in TESTS = test_sha1 test_jbig2dec.py test_huffman test_arith test_sha1_SOURCES = sha1.c sha1.h test_sha1_CFLAGS = -DTEST test_arith_SOURCES = jbig2_arith.c test_arith_CFLAGS = -DTEST test_arith_LDADD = libjbig2dec.la test_huffman_SOURCES = jbig2_huffman.c test_huffman_CFLAGS = -DTEST test_huffman_LDADD = libjbig2dec.la jbig2dec-0.18/Makefile.unix000066400000000000000000000023071362055302000155330ustar00rootroot00000000000000# Simple unix makefile default: all prefix ?= /usr/local CFLAGS := -Wall -Wextra -Wno-unused-parameter -g -O2 LIB_SRCS := \ jbig2_arith.c jbig2_arith_int.c jbig2_arith_iaid.c \ jbig2_huffman.c jbig2_hufftab.c jbig2_segment.c jbig2_page.c \ jbig2_symbol_dict.c jbig2_text.c jbig2_halftone.c jbig2_generic.c \ jbig2_refinement.c jbig2_mmr.c jbig2_image.c jbig2.c LIB_OBJS := $(LIB_SRCS:%.c=%.o) LIB_HDRS := \ jbig2.h jbig2_arith.h jbig2_arith_iaid.h jbig2_arith_int.h \ jbig2_generic.h jbig2_huffman.h jbig2_hufftab.h jbig2_image.h \ jbig2_mmr.h jbig2_priv.h jbig2_symbol_dict.h jbig2_text.h os_types.h APP_SRCS := jbig2_image_pbm.c jbig2_image_png.c jbig2dec.c sha1.c APP_OBJS := $(APP_SRCS:%.c=%.o) APP_HDRS := sha1.h $(LIB_OBJS): $(LIB_HDRS) $(APP_OBJS): $(LIB_HDRS) $(APP_HDRS) libjbig2dec.a: $(LIB_OBJS) ar cru $@ $^ jbig2dec: $(APP_OBJS) libjbig2dec.a $(CC) -o $@ $^ -lpng -lz all: jbig2dec libjbig2dec.a install: jbig2dec libjbig2dec.a install -d $(prefix)/bin $(prefix)/lib $(prefix)/include $(prefix)/man/man1 install jbig2dec $(prefix)/bin install jbig2dec.1 $(prefix)/man/man1 install jbig2.h $(prefix)/include install libjbig2dec.a $(prefix)/lib clean: rm -f *.o jbig2dec libjbig2dec.a jbig2dec-0.18/README000066400000000000000000000022151362055302000137670ustar00rootroot00000000000000jbig2dec is a decoder library and example utility implementing the JBIG2 bi-level image compression spec. Also known as ITU T.88 and ISO IEC 14492, and included by reference in Adobe's PDF version 1.4 and later. The basic invocation is: jbig2dec [-o ] file.jbig2 It also supports separate 'global' and 'page' streams, generally extracted from some embedded format: jbig2dec [-o ] The program is only partially functional at this time, but should be useful in some limited contexts. We welcome files that the decoder can't handle, or renders incorrectly. More information about jbig2dec and updated versions are available from: https://jbig2dec.com/ A set of example files is available from https://jbig2dec.com/tests/ Development source code is kept in a git repository at: http://git.ghostscript.com/?p=jbig2dec.git Report bugs at https://bugs.ghostscript.com For security issues please use the "Security - Other" product, and for normal bugs the "jbig2dec" product. Contact the developers at the IRC channel #ghostscript at freenode.net or via the mailing list . jbig2dec-0.18/annex-h.jbig2000066400000000000000000000015341362055302000153670ustar00rootroot00000000000000JB2  &x/@0@8yyB1%  @pA',6, &qΧ- фaE|ÞE}B b/Db"5* w W $ 6UkZ@.RҊJ #$JTJ)*I@@10@8 O IJo @ % nZ@ '#6, + qkmI}Hhn@  > $ ˂f /dev/null 2>&1 || { echo echo "You must have autoconf installed to compile $package." echo "Download the appropriate package for your distribution," echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" exit 1 } VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/" VERSIONMKMAJ="sed -e s/\([0-9][0-9]*\)[^0-9].*/\\1/" VERSIONMKMIN="sed -e s/.*[0-9][0-9]*\.//" JBIG2VERSIONGREP="sed -e s/^.*(\([0-9][0-9]*\)).*/\\1/" JBIG2MAJOR=$(grep 'define JBIG2_VERSION_MAJOR' jbig2.h | $JBIG2VERSIONGREP) JBIG2MINOR=$(grep 'define JBIG2_VERSION_MINOR' jbig2.h | $JBIG2VERSIONGREP) sed -e "s/^\(AC_INIT[^,]*,\)[^,]*\(,.*\)$/\1 [$JBIG2MAJOR.$JBIG2MINOR]\2/" configure.ac.in > configure.ac # do we need automake? if test "x$USE_OLD" = "xyes" ; then if test -r Makefile.am; then AM_OPTIONS=`fgrep AUTOMAKE_OPTIONS Makefile.am` AM_NEEDED=`echo $AM_OPTIONS | $VERSIONGREP` if test "x$AM_NEEDED" = "x$AM_OPTIONS"; then AM_NEEDED="" fi if test -z "$AM_NEEDED"; then echo -n "checking for automake... " AUTOMAKE=automake ACLOCAL=aclocal if ($AUTOMAKE --version < /dev/null > /dev/null 2>&1); then echo "yes" else echo "no" AUTOMAKE= fi else echo -n "checking for automake $AM_NEEDED or later... " majneeded=`echo $AM_NEEDED | $VERSIONMKMAJ` minneeded=`echo $AM_NEEDED | $VERSIONMKMIN` for am in automake-$AM_NEEDED automake$AM_NEEDED automake \ automake-1.7 automake-1.8 automake-1.9 automake-1.10; do ($am --version < /dev/null > /dev/null 2>&1) || continue ver=`$am --version < /dev/null | head -n 1 | $VERSIONGREP` maj=`echo $ver | $VERSIONMKMAJ` min=`echo $ver | $VERSIONMKMIN` if test $maj -eq $majneeded -a $min -ge $minneeded; then AUTOMAKE=$am echo $AUTOMAKE break fi done test -z $AUTOMAKE && echo "no" echo -n "checking for aclocal $AM_NEEDED or later... " for ac in aclocal-$AM_NEEDED aclocal$AM_NEEDED aclocal\ aclocal-1.7 aclocal-1.8 aclocal-1.9 aclocal-1.10; do ($ac --version < /dev/null > /dev/null 2>&1) || continue ver=`$ac --version < /dev/null | head -n 1 | $VERSIONGREP` maj=`echo $ver | $VERSIONMKMAJ` min=`echo $ver | $VERSIONMKMIN` if test $maj -eq $majneeded -a $min -ge $minneeded; then ACLOCAL=$ac echo $ACLOCAL break fi done test -z $ACLOCAL && echo "no" fi test -z $AUTOMAKE || test -z $ACLOCAL && { echo echo "You must have automake installed to compile $package." echo "Download the appropriate package for your distribution," echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" exit 1 } fi else AUTOMAKE=automake ACLOCAL=aclocal AM_VER=`$AUTOMAKE --version | grep "automake (GNU automake)" | sed 's/[^0-9\.]*//g'` AM_MAJ=`echo $AM_VER |cut -d. -f1` AM_MIN=`echo $AM_VER |cut -d. -f2` AM_PAT=`echo $AM_VER |cut -d. -f3` AM_NEEDED=`fgrep AUTOMAKE_OPTIONS Makefile.am | $VERSIONGREP` AM_MAJOR_REQ=`echo $AM_NEEDED |cut -d. -f1` AM_MINOR_REQ=`echo $AM_NEEDED |cut -d. -f2` echo "checking for automake $AM_NEEDED or later..." if [ $AM_MAJ -lt $AM_MAJOR_REQ -o $AM_MIN -lt $AM_MINOR_REQ ] ; then echo echo "You must have automake $AM_NEEDED or better installed to compile $package." echo "Download the appropriate package for your distribution," echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" exit 1 fi fi # do we need libtool? if ! test -z `grep -l -s -e PROG_LIBTOOL configure.ac configure.in`; then echo -n "Checking for libtoolize... " LIBTOOLIZE= for lt in glibtoolize libtoolize; do if ($lt --version < /dev/null > /dev/null 2>&1); then LIBTOOLIZE=$lt echo $lt break; fi done if test -z $LIBTOOLIZE; then echo echo "You must have GNU libtool installed to compile $package." echo "Download the appropriate package for your distribution," echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" exit 1 fi fi echo "Generating configuration files for $package, please wait...." echo " $ACLOCAL $ACLOCAL_FLAGS" $ACLOCAL $ACLOCAL_FLAGS echo " $LIBTOOLIZE" $LIBTOOLIZE --copy echo " autoheader" autoheader echo " creating config_types.h.in" cat >config_types.h.in < #else # ifdef JBIG2_REPLACE_STDINT_H # include <@JBIG2_STDINT_H@> # else typedef unsigned @JBIG2_INT32_T@ uint32_t; typedef unsigned @JBIG2_INT16_T@ uint16_t; typedef unsigned @JBIG2_INT8_T@ uint8_t; typedef signed @JBIG2_INT32_T@ int32_t; typedef signed @JBIG2_INT16_T@ int16_t; typedef signed @JBIG2_INT8_T@ int8_t; # endif /* JBIG2_REPLACE_STDINT */ #endif /* HAVE_STDINT_H */ EOF echo " $AUTOMAKE --add-missing $AUTOMAKE_FLAGS" $AUTOMAKE --add-missing --copy $AUTOMAKE_FLAGS echo " autoconf" autoconf if test -z "$*"; then echo "I am going to run ./configure with no arguments - if you wish " echo "to pass any to it, please specify them on the $0 command line." else echo "running ./configure $@" fi $srcdir/configure "$@" && echo jbig2dec-0.18/config_win32.h000066400000000000000000000032361362055302000155530ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* configuration header file for compiling under Microsoft Windows */ #ifdef _MSC_VER /* VS 2012 and later have stdint.h */ # if _MSC_VER >= 1700 # include # else typedef signed char int8_t; typedef short int int16_t; typedef int int32_t; typedef __int64 int64_t; typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; typedef unsigned __int64 uint64_t; #ifndef SIZE_MAX #define SIZE_MAX (~((size_t) 0)) #endif # endif /* VS 2008 and later have vsnprintf */ # if _MSC_VER < 1500 # define vsnprintf _vsnprintf /* Previously we defined inline as nothing for 2005 and below */ # define inline #else /* VS 2008 has __inline but not inline, later versiosn (unknown exactly where) define inline * so cater for it being defined already. */ # if !(defined(inline)) # define inline __inline # endif # endif /* VS 2014 and later have (finally) snprintf */ # if _MSC_VER >= 1900 # define STDC99 # else # define snprintf _snprintf # endif #else /* _MSC_VER */ /* Not VS -- it had best behave */ # include #endif /* _MSC_VER */ jbig2dec-0.18/configure.ac.in000066400000000000000000000111321362055302000160000ustar00rootroot00000000000000# Process this file with autoconf to produce a configure script. AC_INIT([jbig2dec], [unknown-version], [gs-devel@ghostscript.com]) AC_PREREQ(2.53) AC_CONFIG_SRCDIR([jbig2dec.c]) AM_INIT_AUTOMAKE([-Wall]) AM_CONFIG_HEADER(config.h) dnl Library versioning - Adapted from the libtool info page dnl dnl 1. If source has changed at all: increment revision dnl 2. If the ABI changed: increment current, reset revision to 0 dnl 3. If interfaces have been added since last public release: increment age dnl 4. If interfaces have been removed: reset age to 0 AC_SUBST([JBIG2DEC_LT_CURRENT], [0]) AC_SUBST([JBIG2DEC_LT_REVISION], [0]) AC_SUBST([JBIG2DEC_LT_AGE], [0]) # Checks for programs. AC_PROG_CC AM_PROG_CC_C_O AM_PROG_AR AC_LIBTOOL_WIN32_DLL AC_PROG_LIBTOOL # platform specific compiler flags if test "x$GCC" = xyes; then CFLAGS="$CFLAGS -Wall" fi # Checks for libraries. dnl by default we want png support if possible AC_ARG_WITH([libpng], AC_HELP_STRING([--with-libpng[=prefix]], [include support for png output (if libpng is available)]), [ac_cv_want_libpng="$withval"], [ac_cv_want_libpng="yes"]) save_cflags="$CFLAGS" save_ldflags="$LDFLAGS" have_libpng="no" if test "x$ac_cv_want_libpng" != "xno"; then if test "x$ac_cv_want_libpng" != "xyes"; then dnl if it's not yes or no, treat as a prefix CFLAGS="$CFLAGS -I$ac_cv_want_libpng/include" LDFLAGS="$LDFLAGS -L$ac_cv_want_libpng/lib" fi dnl libpng requires pow() which may be in libm AC_SEARCH_LIBS([pow], [m]) AC_CHECK_LIB([png], [png_create_write_struct], [ AC_CHECK_LIB([z], [deflate], [ AC_DEFINE(HAVE_LIBPNG, 1, [Define if libpng is available (-lpng)]) PNG_LIBS="-lpng -lz" AC_LIBOBJ([jbig2_image_png]) have_libpng="yes" ]) ]) fi dnl restore (possibly changed) flags if we didn't find working libpng if test "x$have_libpng" != "xyes"; then CFLAGS="$save_cflags" LDFLAGS="$save_ldflags" fi AC_SUBST(PNG_LIBS) # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADERS([libintl.h stddef.h unistd.h strings.h]) dnl We assume the fixed-size types from stdint.h. If that header is dnl not available, look for the same types in a few other headers. dnl We also attempt to define them ourselves, but only use those if dnl the native versions aren't available. The substitutions happen dnl in a file config_types.h, whose template is created by autogen.sh stdint_types_in="no_replacement_found" stdint_types_discovered="yes" AC_CHECK_SIZEOF(char) AC_CHECK_SIZEOF(short) AC_CHECK_SIZEOF(int) AC_CHECK_SIZEOF(long) case 1 in $ac_cv_sizeof_char) int8_type="char";; *) stdint_types_discovered="no" esac case 2 in $ac_cv_sizeof_short) int16_type="short";; $ac_cv_sizeof_char) int16_type="char";; $ac_cv_sizeof_int) int16_type="char";; *) stdint_types_discovered="no";; esac case 4 in $ac_cv_sizeof_int) int32_type="int";; $ac_cv_sizeof_long) int32_type="long";; $ac_cv_sizeof_short) int32_type="short";; *) stdint_types_discovered="no";; esac AC_CHECK_HEADER([stdint.h]) if test "x$ac_cv_header_stdint_h" != "xyes"; then for include in sys/types.h inttypes.h sys/inttypes.h sys/int_types.h ; do AC_MSG_CHECKING([for uint32_t in $include]) AC_TRY_COMPILE([#include <$include>], [uint32_t canary;], [ AC_MSG_RESULT([yes]) stdint_types_in="$include" break; ], AC_MSG_RESULT([no]) ) done if test "x$stdint_types_in" != "xno_replacement_found"; then AC_MSG_RESULT([Adding $stdint_types_in to config header for stdint types]) AC_DEFINE([JBIG2_REPLACE_STDINT_H],, [set by configure if an alternate header with the stdint.h types is found]) elif test "x$stdint_types_discovered" = "xno"; then AC_MSG_ERROR([ Unable to find suitable definitions of the stdint.h types (uint32_t and friends) You will have to define these yourself in a separate header. See config_win32.h for an example. ]) fi fi AC_SUBST(JBIG2_INT32_T, [$int32_type]) AC_SUBST(JBIG2_INT16_T, [$int16_type]) AC_SUBST(JBIG2_INT8_T, [$int8_type]) AC_SUBST(JBIG2_STDINT_H, [$stdint_types_in]) # Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_SIZE_T AC_C_BIGENDIAN # Checks for library functions. AC_FUNC_MEMCMP dnl we use realloc() but don't depend on the zero-length behavior dnl tested by AC_FUNC_REALLOC AC_REPLACE_FUNCS([snprintf]) AC_CHECK_FUNCS([memset strdup]) dnl use our included getopt if the system doesn't have getopt_long() AC_CHECK_FUNC(getopt_long, AC_DEFINE(HAVE_GETOPT_LONG,, [Define if the local libc includes getopt_long()] ),[ AC_LIBOBJ([getopt]) AC_LIBOBJ([getopt1]) ]) # generate output AC_CONFIG_FILES([Makefile config_types.h jbig2dec.pc]) AC_OUTPUT jbig2dec-0.18/getopt.c000066400000000000000000001017031362055302000145570ustar00rootroot00000000000000/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to drepper@gnu.org before changing it! Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO # define _NO_PROTO #endif #ifdef HAVE_CONFIG_H # include #endif #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ # ifndef const # define const # endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 # include # if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION # define ELIDE_CODE # endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ # include # include #endif /* GNU C library. */ #ifdef VMS # include # if HAVE_STRING_H - 0 # include # endif #endif #ifndef _ /* This is for other GNU distributions with internationalized messages. */ # if defined HAVE_LIBINTL_H || defined _LIBC # include # ifndef _ # define _(msgid) gettext (msgid) # endif # else # define _(msgid) (msgid) # endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ # include # define my_index strchr #else # if HAVE_STRING_H # include # else # include # endif /* Avoid depending on library functions or files whose names are inconsistent. */ #ifndef getenv extern char *getenv(); #endif static char * my_index(str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *)str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ # if (!defined __STDC__ || !__STDC__) && !defined strlen /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen(const char *); # endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Stored original parameters. XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ extern int __libc_argc; extern char **__libc_argv; /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ # ifdef USE_NONOPTION_FLAGS /* Defined in getopt_init.c */ extern char *__getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; # endif # ifdef USE_NONOPTION_FLAGS # define SWAP_FLAGS(ch1, ch2) \ if (nonoption_flags_len > 0) \ { \ char __tmp = __getopt_nonoption_flags[ch1]; \ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ __getopt_nonoption_flags[ch2] = __tmp; \ } # else # define SWAP_FLAGS(ch1, ch2) # endif #else /* !_LIBC */ # define SWAP_FLAGS(ch1, ch2) #endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ #if defined __STDC__ && __STDC__ static void exchange(char **); #endif static void exchange(argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and presents new arguments. */ char *new_str = malloc(top + 1); if (new_str == NULL) nonoption_flags_len = nonoption_flags_max_len = 0; else { memset(__mempcpy(new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS(bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS(bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ #if defined __STDC__ && __STDC__ static const char *_getopt_initialize(int, char *const *, const char *); #endif static const char * _getopt_initialize(argc, argv, optstring) int argc; char *const *argv; const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind; nextchar = NULL; posixly_correct = getenv("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #if defined _LIBC && defined USE_NONOPTION_FLAGS if (posixly_correct == NULL && argc == __libc_argc && argv == __libc_argv) { if (nonoption_flags_max_len == 0) { if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') nonoption_flags_max_len = -1; else { const char *orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen(orig_str); if (nonoption_flags_max_len < argc) nonoption_flags_max_len = argc; __getopt_nonoption_flags = (char *)malloc(nonoption_flags_max_len); if (__getopt_nonoption_flags == NULL) nonoption_flags_max_len = -1; else memset(__mempcpy(__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); } } nonoption_flags_len = nonoption_flags_max_len; } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal(argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { int print_errors = opterr; if (optstring[0] == ':') print_errors = 0; if (argc < 1) return -1; optarg = NULL; if (optind == 0 || !__getopt_initialized) { if (optind == 0) optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize(argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && __getopt_nonoption_flags[optind] == '1')) #else # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **)argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp(argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange((char **)argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index(optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp(p->name, nextchar, nameend - nextchar)) { if ((unsigned int)(nameend - nextchar) == (unsigned int)strlen(p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf(stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen(nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) { if (argv[optind - 1][1] == '-') /* --option */ fprintf(stderr, _("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); else /* +option or -option */ fprintf(stderr, _("%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen(nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf(stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen(nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index(optstring, *nextchar) == NULL) { if (print_errors) { if (argv[optind][1] == '-') /* --option */ fprintf(stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); else /* +option or -option */ fprintf(stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); } nextchar = (char *)""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index(optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (print_errors) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf(stderr, _("%s: illegal option -- %c\n"), argv[0], c); else fprintf(stderr, _("%s: invalid option -- %c\n"), argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf(stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp(p->name, nextchar, nameend - nextchar)) { if ((unsigned int)(nameend - nextchar) == strlen(p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf(stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen(nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) fprintf(stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); nextchar += strlen(nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf(stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen(nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen(nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf(stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt(argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal(argc, argv, optstring, (const struct option *)0, (int *)0, 0); } #endif /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main(argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt(argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf("option %c\n", c); break; case 'a': printf("option a\n"); break; case 'b': printf("option b\n"); break; case 'c': printf("option c with value `%s'\n", optarg); break; case '?': break; default: printf("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } exit(0); } #endif /* TEST */ jbig2dec-0.18/getopt.h000066400000000000000000000146111362055302000145650ustar00rootroot00000000000000/* Declarations for getopt. Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef _GETOPT_H #ifndef __need_getopt # define _GETOPT_H 1 #endif /* If __GNU_LIBRARY__ is not already defined, either we are being used standalone, or this is the first header included in the source file. If we are being used with glibc, we need to include , but that does not exist if we are standalone. So: if __GNU_LIBRARY__ is not defined, include , which will pull in for us if it's from glibc. (Why ctype.h? It's guaranteed to exist and it doesn't flood the namespace with stuff the way some other headers do.) */ #if !defined __GNU_LIBRARY__ # include #endif #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; #ifndef __need_getopt /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { # if (defined __STDC__ && __STDC__) || defined __cplusplus const char *name; # else char *name; # endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ # define no_argument 0 # define required_argument 1 # define optional_argument 2 #endif /* need getopt */ /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, `optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in `optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU `getopt'. The argument `--' causes premature termination of argument scanning, explicitly telling `getopt' that there are no more options. If OPTS begins with `--', then non-option arguments are treated as arguments to the option '\0'. This behavior is specific to the GNU `getopt'. */ #if (defined __STDC__ && __STDC__) || defined __cplusplus # ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt(int __argc, char *const *__argv, const char *__shortopts); # else /* not __GNU_LIBRARY__ */ extern int getopt(); # endif /* __GNU_LIBRARY__ */ # ifndef __need_getopt extern int getopt_long(int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); extern int getopt_long_only(int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal(int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only); # endif #else /* not __STDC__ */ extern int getopt(); # ifndef __need_getopt extern int getopt_long(); extern int getopt_long_only(); extern int _getopt_internal(); # endif #endif /* __STDC__ */ #ifdef __cplusplus } #endif /* Make sure we later can get all the definitions and declarations. */ #undef __need_getopt #endif /* getopt.h */ jbig2dec-0.18/getopt1.c000066400000000000000000000113721362055302000146420ustar00rootroot00000000000000/* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifdef HAVE_CONFIG_H #include #endif #include "getopt.h" #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 #include #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION #define ELIDE_CODE #endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ #include #endif #ifndef NULL #define NULL 0 #endif int getopt_long(argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal(argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only(argc, argv, options, long_options, opt_index) int argc; char *const *argv; const char *options; const struct option *long_options; int *opt_index; { return _getopt_internal(argc, argv, options, long_options, opt_index, 1); } #endif /* Not ELIDE_CODE. */ #ifdef TEST #include int main(argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; int option_index = 0; static struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, {"delete", 1, 0, 0}, {"verbose", 0, 0, 0}, {"create", 0, 0, 0}, {"file", 1, 0, 0}, {0, 0, 0, 0} }; c = getopt_long(argc, argv, "abc:d:0123456789", long_options, &option_index); if (c == -1) break; switch (c) { case 0: printf("option %s", long_options[option_index].name); if (optarg) printf(" with arg %s", optarg); printf("\n"); break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf("option %c\n", c); break; case 'a': printf("option a\n"); break; case 'b': printf("option b\n"); break; case 'c': printf("option c with value `%s'\n", optarg); break; case 'd': printf("option d with value `%s'\n", optarg); break; case '?': break; default: printf("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) printf("%s ", argv[optind++]); printf("\n"); } exit(0); } #endif /* TEST */ jbig2dec-0.18/jbig2.c000066400000000000000000000426771362055302000142700ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include #include #include #include #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_image.h" #include "jbig2_page.h" #include "jbig2_segment.h" static void * jbig2_default_alloc(Jbig2Allocator *allocator, size_t size) { return malloc(size); } static void jbig2_default_free(Jbig2Allocator *allocator, void *p) { free(p); } static void * jbig2_default_realloc(Jbig2Allocator *allocator, void *p, size_t size) { return realloc(p, size); } static Jbig2Allocator jbig2_default_allocator = { jbig2_default_alloc, jbig2_default_free, jbig2_default_realloc }; void * jbig2_alloc(Jbig2Allocator *allocator, size_t size, size_t num) { /* Check for integer multiplication overflow when computing the full size of the allocation. */ if (num > 0 && size > SIZE_MAX / num) return NULL; return allocator->alloc(allocator, size * num); } /* jbig2_free and jbig2_realloc moved to the bottom of this file */ static void jbig2_default_error(void *data, const char *msg, Jbig2Severity severity, int32_t seg_idx) { /* report only fatal errors by default */ if (severity == JBIG2_SEVERITY_FATAL) { fprintf(stderr, "jbig2 decoder FATAL ERROR: %s", msg); if (seg_idx != -1) fprintf(stderr, " (segment 0x%02x)", seg_idx); fprintf(stderr, "\n"); fflush(stderr); } } int jbig2_error(Jbig2Ctx *ctx, Jbig2Severity severity, int32_t segment_number, const char *fmt, ...) { char buf[1024]; va_list ap; int n; va_start(ap, fmt); n = vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (n < 0 || n == sizeof(buf)) strncpy(buf, "failed to generate error string", sizeof(buf)); ctx->error_callback(ctx->error_callback_data, buf, severity, segment_number); return -1; } Jbig2Ctx * jbig2_ctx_new_imp(Jbig2Allocator *allocator, Jbig2Options options, Jbig2GlobalCtx *global_ctx, Jbig2ErrorCallback error_callback, void *error_callback_data, int jbig2_version_major, int jbig2_version_minor) { Jbig2Ctx *result; if (jbig2_version_major != JBIG2_VERSION_MAJOR || jbig2_version_minor != JBIG2_VERSION_MINOR) { Jbig2Ctx fakectx; fakectx.error_callback = error_callback; fakectx.error_callback_data = error_callback_data; jbig2_error(&fakectx, JBIG2_SEVERITY_FATAL, -1, "incompatible jbig2dec header (%d.%d) and library (%d.%d) versions", jbig2_version_major, jbig2_version_minor, JBIG2_VERSION_MAJOR, JBIG2_VERSION_MINOR); return NULL; } if (allocator == NULL) allocator = &jbig2_default_allocator; if (error_callback == NULL) error_callback = &jbig2_default_error; result = (Jbig2Ctx *) jbig2_alloc(allocator, sizeof(Jbig2Ctx), 1); if (result == NULL) { error_callback(error_callback_data, "failed to allocate initial context", JBIG2_SEVERITY_FATAL, -1); return NULL; } result->allocator = allocator; result->options = options; result->global_ctx = (const Jbig2Ctx *)global_ctx; result->error_callback = error_callback; result->error_callback_data = error_callback_data; result->state = (options & JBIG2_OPTIONS_EMBEDDED) ? JBIG2_FILE_SEQUENTIAL_HEADER : JBIG2_FILE_HEADER; result->buf = NULL; result->n_segments = 0; result->n_segments_max = 16; result->segments = jbig2_new(result, Jbig2Segment *, result->n_segments_max); if (result->segments == NULL) { error_callback(error_callback_data, "failed to allocate initial segments", JBIG2_SEVERITY_FATAL, -1); jbig2_free(allocator, result); return NULL; } result->segment_index = 0; result->current_page = 0; result->max_page_index = 4; result->pages = jbig2_new(result, Jbig2Page, result->max_page_index); if (result->pages == NULL) { error_callback(error_callback_data, "failed to allocated initial pages", JBIG2_SEVERITY_FATAL, -1); jbig2_free(allocator, result->segments); jbig2_free(allocator, result); return NULL; } { int index; for (index = 0; index < result->max_page_index; index++) { result->pages[index].state = JBIG2_PAGE_FREE; result->pages[index].number = 0; result->pages[index].width = 0; result->pages[index].height = 0xffffffff; result->pages[index].x_resolution = 0; result->pages[index].y_resolution = 0; result->pages[index].stripe_size = 0; result->pages[index].striped = 0; result->pages[index].end_row = 0; result->pages[index].flags = 0; result->pages[index].image = NULL; } } return result; } #define get_uint16(bptr)\ (((bptr)[0] << 8) | (bptr)[1]) #define get_int16(bptr)\ (((int)get_uint16(bptr) ^ 0x8000) - 0x8000) /* coverity[ -tainted_data_return ] */ /* coverity[ -tainted_data_argument : arg-0 ] */ int16_t jbig2_get_int16(const byte *bptr) { return get_int16(bptr); } /* coverity[ -tainted_data_return ] */ /* coverity[ -tainted_data_argument : arg-0 ] */ uint16_t jbig2_get_uint16(const byte *bptr) { return get_uint16(bptr); } /* coverity[ -tainted_data_return ] */ /* coverity[ -tainted_data_argument : arg-0 ] */ int32_t jbig2_get_int32(const byte *bptr) { return ((int32_t) get_int16(bptr) << 16) | get_uint16(bptr + 2); } /* coverity[ -tainted_data_return ] */ /* coverity[ -tainted_data_argument : arg-0 ] */ uint32_t jbig2_get_uint32(const byte *bptr) { return ((uint32_t) get_uint16(bptr) << 16) | get_uint16(bptr + 2); } /** * jbig2_data_in: submit data for decoding * @ctx: The jbig2dec decoder context * @data: a pointer to the data buffer * @size: the size of the data buffer in bytes * * Copies the specified data into internal storage and attempts * to (continue to) parse it as part of a jbig2 data stream. * * Return code: 0 on success * -1 if there is a parsing error **/ int jbig2_data_in(Jbig2Ctx *ctx, const unsigned char *data, size_t size) { const size_t initial_buf_size = 1024; if (ctx->buf == NULL) { size_t buf_size = initial_buf_size; do buf_size <<= 1; while (buf_size < size); ctx->buf = jbig2_new(ctx, byte, buf_size); if (ctx->buf == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate buffer when reading data"); } ctx->buf_size = buf_size; ctx->buf_rd_ix = 0; ctx->buf_wr_ix = 0; } else if (ctx->buf_wr_ix + size > ctx->buf_size) { if (ctx->buf_rd_ix <= (ctx->buf_size >> 1) && ctx->buf_wr_ix - ctx->buf_rd_ix + size <= ctx->buf_size) { memmove(ctx->buf, ctx->buf + ctx->buf_rd_ix, ctx->buf_wr_ix - ctx->buf_rd_ix); } else { byte *buf; size_t buf_size = initial_buf_size; do buf_size <<= 1; while (buf_size < ctx->buf_wr_ix - ctx->buf_rd_ix + size); buf = jbig2_new(ctx, byte, buf_size); if (buf == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate bigger buffer when reading data"); } memcpy(buf, ctx->buf + ctx->buf_rd_ix, ctx->buf_wr_ix - ctx->buf_rd_ix); jbig2_free(ctx->allocator, ctx->buf); ctx->buf = buf; ctx->buf_size = buf_size; } ctx->buf_wr_ix -= ctx->buf_rd_ix; ctx->buf_rd_ix = 0; } memcpy(ctx->buf + ctx->buf_wr_ix, data, size); ctx->buf_wr_ix += size; /* data has now been added to buffer */ for (;;) { const byte jbig2_id_string[8] = { 0x97, 0x4a, 0x42, 0x32, 0x0d, 0x0a, 0x1a, 0x0a }; Jbig2Segment *segment; size_t header_size; int code; switch (ctx->state) { case JBIG2_FILE_HEADER: /* D.4.1 */ if (ctx->buf_wr_ix - ctx->buf_rd_ix < 9) return 0; if (memcmp(ctx->buf + ctx->buf_rd_ix, jbig2_id_string, 8)) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "not a JBIG2 file header"); /* D.4.2 */ ctx->file_header_flags = ctx->buf[ctx->buf_rd_ix + 8]; /* Check for T.88 amendment 2 */ if (ctx->file_header_flags & 0x04) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "file header indicates use of 12 adaptive template pixels (NYI)"); /* Check for T.88 amendment 3 */ if (ctx->file_header_flags & 0x08) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "file header indicates use of colored region segments (NYI)"); if (ctx->file_header_flags & 0xFC) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "reserved bits (2-7) of file header flags are not zero (0x%02x)", ctx->file_header_flags); } /* D.4.3 */ if (!(ctx->file_header_flags & 2)) { /* number of pages is known */ if (ctx->buf_wr_ix - ctx->buf_rd_ix < 13) return 0; ctx->n_pages = jbig2_get_uint32(ctx->buf + ctx->buf_rd_ix + 9); ctx->buf_rd_ix += 13; if (ctx->n_pages == 1) jbig2_error(ctx, JBIG2_SEVERITY_INFO, -1, "file header indicates a single page document"); else jbig2_error(ctx, JBIG2_SEVERITY_INFO, -1, "file header indicates a %d page document", ctx->n_pages); } else { /* number of pages not known */ ctx->n_pages = 0; ctx->buf_rd_ix += 9; } /* determine the file organization based on the flags - D.4.2 again */ if (ctx->file_header_flags & 1) { ctx->state = JBIG2_FILE_SEQUENTIAL_HEADER; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "file header indicates sequential organization"); } else { ctx->state = JBIG2_FILE_RANDOM_HEADERS; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "file header indicates random-access organization"); } break; case JBIG2_FILE_SEQUENTIAL_HEADER: case JBIG2_FILE_RANDOM_HEADERS: segment = jbig2_parse_segment_header(ctx, ctx->buf + ctx->buf_rd_ix, ctx->buf_wr_ix - ctx->buf_rd_ix, &header_size); if (segment == NULL) return 0; /* need more data */ ctx->buf_rd_ix += header_size; if (ctx->n_segments == ctx->n_segments_max) { Jbig2Segment **segments; segments = jbig2_renew(ctx, ctx->segments, Jbig2Segment *, (ctx->n_segments_max <<= 2)); if (segments == NULL) { ctx->state = JBIG2_FILE_EOF; return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate space for more segments"); } ctx->segments = segments; } ctx->segments[ctx->n_segments++] = segment; if (ctx->state == JBIG2_FILE_RANDOM_HEADERS) { if ((segment->flags & 63) == 51) /* end of file */ ctx->state = JBIG2_FILE_RANDOM_BODIES; } else /* JBIG2_FILE_SEQUENTIAL_HEADER */ ctx->state = JBIG2_FILE_SEQUENTIAL_BODY; break; case JBIG2_FILE_SEQUENTIAL_BODY: case JBIG2_FILE_RANDOM_BODIES: segment = ctx->segments[ctx->segment_index]; /* immediate generic regions may have unknown size */ if (segment->data_length == 0xffffffff && (segment->flags & 63) == 38) { byte *s, *e, *p; int mmr; byte mmr_marker[2] = { 0x00, 0x00 }; byte arith_marker[2] = { 0xff, 0xac }; byte *desired_marker; s = p = ctx->buf + ctx->buf_rd_ix; e = ctx->buf + ctx->buf_wr_ix; if (e - p < 18) return 0; /* need more data */ mmr = p[17] & 1; p += 18; desired_marker = mmr ? mmr_marker : arith_marker; /* look for two byte marker */ if (e - p < 2) return 0; /* need more data */ while (p[0] != desired_marker[0] || p[1] != desired_marker[1]) { p++; if (e - p < 2) return 0; /* need more data */ } p += 2; /* the marker is followed by a four byte row count */ if (e - p < 4) return 0; /* need more data */ segment->rows = jbig2_get_uint32(p); p += 4; segment->data_length = p - s; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "unknown length determined to be %lu", (long) segment->data_length); } else if (segment->data_length > ctx->buf_wr_ix - ctx->buf_rd_ix) return 0; /* need more data */ code = jbig2_parse_segment(ctx, segment, ctx->buf + ctx->buf_rd_ix); ctx->buf_rd_ix += segment->data_length; ctx->segment_index++; if (ctx->state == JBIG2_FILE_RANDOM_BODIES) { if (ctx->segment_index == ctx->n_segments) ctx->state = JBIG2_FILE_EOF; } else { /* JBIG2_FILE_SEQUENTIAL_BODY */ ctx->state = JBIG2_FILE_SEQUENTIAL_HEADER; } if (code < 0) { ctx->state = JBIG2_FILE_EOF; return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode; treating as end of file"); } break; case JBIG2_FILE_EOF: if (ctx->buf_rd_ix == ctx->buf_wr_ix) return 0; return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "garbage beyond end of file"); } } } Jbig2Allocator * jbig2_ctx_free(Jbig2Ctx *ctx) { Jbig2Allocator *ca; int i; if (ctx == NULL) return NULL; ca = ctx->allocator; jbig2_free(ca, ctx->buf); if (ctx->segments != NULL) { for (i = 0; i < ctx->n_segments; i++) jbig2_free_segment(ctx, ctx->segments[i]); jbig2_free(ca, ctx->segments); } if (ctx->pages != NULL) { for (i = 0; i <= ctx->current_page; i++) if (ctx->pages[i].image != NULL) jbig2_image_release(ctx, ctx->pages[i].image); jbig2_free(ca, ctx->pages); } jbig2_free(ca, ctx); return ca; } Jbig2GlobalCtx * jbig2_make_global_ctx(Jbig2Ctx *ctx) { return (Jbig2GlobalCtx *) ctx; } Jbig2Allocator * jbig2_global_ctx_free(Jbig2GlobalCtx *global_ctx) { return jbig2_ctx_free((Jbig2Ctx *) global_ctx); } /* I'm not committed to keeping the word stream interface. It's handy when you think you may be streaming your input, but if you're not (as is currently the case), it just adds complexity. */ typedef struct { Jbig2WordStream super; const byte *data; size_t size; } Jbig2WordStreamBuf; static int jbig2_word_stream_buf_get_next_word(Jbig2WordStream *self, size_t offset, uint32_t *word) { Jbig2WordStreamBuf *z = (Jbig2WordStreamBuf *) self; uint32_t val = 0; int ret = 0; if (self == NULL || word == NULL) return -1; if (offset >= z->size) { *word = 0; return 0; } if (offset < z->size) { val |= z->data[offset] << 24; ret++; } if (offset + 1 < z->size) { val |= z->data[offset + 1] << 16; ret++; } if (offset + 2 < z->size) { val |= z->data[offset + 2] << 8; ret++; } if (offset + 3 < z->size) { val |= z->data[offset + 3]; ret++; } *word = val; return ret; } Jbig2WordStream * jbig2_word_stream_buf_new(Jbig2Ctx *ctx, const byte *data, size_t size) { Jbig2WordStreamBuf *result = jbig2_new(ctx, Jbig2WordStreamBuf, 1); if (result == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate word stream"); return NULL; } result->super.get_next_word = jbig2_word_stream_buf_get_next_word; result->data = data; result->size = size; return &result->super; } void jbig2_word_stream_buf_free(Jbig2Ctx *ctx, Jbig2WordStream *ws) { jbig2_free(ctx->allocator, ws); } /* When Memento is in use, the ->free and ->realloc calls get * turned into ->Memento_free and ->Memento_realloc, which is * obviously problematic. Undefine free and realloc here to * avoid this. */ #ifdef MEMENTO #undef free #undef realloc #endif void jbig2_free(Jbig2Allocator *allocator, void *p) { allocator->free(allocator, p); } void * jbig2_realloc(Jbig2Allocator *allocator, void *p, size_t size, size_t num) { /* check for integer multiplication overflow */ if (num > 0 && size >= SIZE_MAX / num) return NULL; return allocator->realloc(allocator, p, size * num); } jbig2dec-0.18/jbig2.h000066400000000000000000000074311362055302000142620ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef __cplusplus extern "C" { #endif #ifndef _JBIG2_H #define _JBIG2_H #define JBIG2_VERSION_MAJOR (0) #define JBIG2_VERSION_MINOR (18) /* warning levels */ typedef enum { JBIG2_SEVERITY_DEBUG, JBIG2_SEVERITY_INFO, JBIG2_SEVERITY_WARNING, JBIG2_SEVERITY_FATAL } Jbig2Severity; typedef enum { JBIG2_OPTIONS_EMBEDDED = 1 } Jbig2Options; /* forward public structure declarations */ typedef struct _Jbig2Allocator Jbig2Allocator; typedef struct _Jbig2Ctx Jbig2Ctx; typedef struct _Jbig2GlobalCtx Jbig2GlobalCtx; /* this is the general image structure used by the jbig2dec library images are 1 bpp, packed into rows a byte at a time. stride gives the byte offset to the next row, while width and height define the size of the image area in pixels. */ typedef struct _Jbig2Image Jbig2Image; struct _Jbig2Image { uint32_t width; uint32_t height; uint32_t stride; uint8_t *data; int refcount; }; /* errors are returned from the library via a callback. If no callback is provided (a NULL argument is passed to jbig2_ctx_new) a default handler is used which prints fatal errors to the stderr stream. */ /* error callback */ typedef void (*Jbig2ErrorCallback)(void *data, const char *msg, Jbig2Severity severity, int32_t seg_idx); /* memory allocation is likewise done via a set of callbacks so that clients can better control memory usage. If a NULL is passed for this argument of jbig2_ctx_new, a default allocator based on malloc() is used. */ /* dynamic memory callbacks */ struct _Jbig2Allocator { void *(*alloc)(Jbig2Allocator *allocator, size_t size); void (*free)(Jbig2Allocator *allocator, void *p); void *(*realloc)(Jbig2Allocator *allocator, void *p, size_t size); }; /* decoder context */ #define jbig2_ctx_new(allocator, options, global_ctx, error_callback, error_callback_data) jbig2_ctx_new_imp((allocator), (options), (global_ctx), (error_callback), (error_callback_data), JBIG2_VERSION_MAJOR, JBIG2_VERSION_MINOR) Jbig2Ctx *jbig2_ctx_new_imp(Jbig2Allocator *allocator, Jbig2Options options, Jbig2GlobalCtx *global_ctx, Jbig2ErrorCallback error_callback, void *error_callback_data, int jbig2_version_major, int jbig2_version_minor); Jbig2Allocator *jbig2_ctx_free(Jbig2Ctx *ctx); /* global context for embedded streams */ Jbig2GlobalCtx *jbig2_make_global_ctx(Jbig2Ctx *ctx); Jbig2Allocator *jbig2_global_ctx_free(Jbig2GlobalCtx *global_ctx); /* submit data to the decoder */ int jbig2_data_in(Jbig2Ctx *ctx, const unsigned char *data, size_t size); /* get the next available decoded page image. NULL means there isn't one. */ Jbig2Image *jbig2_page_out(Jbig2Ctx *ctx); /* mark a returned page image as no longer needed. */ void jbig2_release_page(Jbig2Ctx *ctx, Jbig2Image *image); /* mark the current page as complete, simulating an end-of-page segment (for broken streams) */ int jbig2_complete_page(Jbig2Ctx *ctx); #endif /* _JBIG2_H */ /* If we don't have a definition for inline, make it nothing so the code will compile */ #ifndef inline #define inline #endif #ifdef __cplusplus } #endif jbig2dec-0.18/jbig2_arith.c000066400000000000000000000237341362055302000154500ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" struct _Jbig2ArithState { uint32_t C; int A; int CT; uint32_t next_word; int next_word_bytes; Jbig2WordStream *ws; int offset; }; /* Previous versions of this code had a #define to allow us to choose between using the revised arithmetic decoding specified in the 'Software Convention' section of the spec. Back to back tests showed that the 'Software Convention' version was indeed slightly faster. We therefore enable it by default. We also strip the option out, because a) it makes the code harder to read, and b) such things are an invitation to bitrot. */ static void jbig2_arith_bytein(Jbig2ArithState *as) { int new_bytes; byte B; /* invariant: as->next_word_bytes > 0 */ /* This code confused me no end when I first read it, so a quick note * to save others (and future me's) from being similarly confused. * 'next_word' does indeed contain 'next_word_bytes' of valid data * (always starting at the most significant byte). The confusing * thing is that the first byte has always already been read. * i.e. it serves only as an indication that the last byte we read * was FF or not. * * The jbig2 bytestream uses FF bytes, followed by a byte > 0x8F as * marker bytes. These never occur in normal streams of arithmetic * encoding, so meeting one terminates the stream (with an infinite * series of 1 bits). * * If we meet an FF byte, we return it as normal. We just 'remember' * that fact for the next byte we read. */ /* Figure G.3 */ B = (byte)((as->next_word >> 24) & 0xFF); if (B == 0xFF) { byte B1; if (as->next_word_bytes == 1) { Jbig2WordStream *ws = as->ws; new_bytes = ws->get_next_word(ws, as->offset, &as->next_word); as->next_word_bytes = new_bytes; as->offset += new_bytes; B1 = (byte)((as->next_word >> 24) & 0xFF); if (B1 > 0x8F) { #ifdef JBIG2_DEBUG_ARITH fprintf(stderr, "read %02x (aa)\n", B); #endif as->CT = 8; as->next_word = 0xFF000000 | (as->next_word >> 8); as->next_word_bytes = 4; as->offset--; } else { #ifdef JBIG2_DEBUG_ARITH fprintf(stderr, "read %02x (a)\n", B); #endif as->C += 0xFE00 - (B1 << 9); as->CT = 7; } } else { B1 = (byte)((as->next_word >> 16) & 0xFF); if (B1 > 0x8F) { #ifdef JBIG2_DEBUG_ARITH fprintf(stderr, "read %02x (ba)\n", B); #endif as->CT = 8; } else { as->next_word_bytes--; as->next_word <<= 8; #ifdef JBIG2_DEBUG_ARITH fprintf(stderr, "read %02x (b)\n", B); #endif as->C += 0xFE00 - (B1 << 9); as->CT = 7; } } } else { #ifdef JBIG2_DEBUG_ARITH fprintf(stderr, "read %02x\n", B); #endif as->CT = 8; as->next_word <<= 8; as->next_word_bytes--; if (as->next_word_bytes == 0) { Jbig2WordStream *ws = as->ws; new_bytes = ws->get_next_word(ws, as->offset, &as->next_word); as->offset += new_bytes; as->next_word_bytes = new_bytes; } B = (byte)((as->next_word >> 24) & 0xFF); as->C += 0xFF00 - (B << 8); } } /** Allocate and initialize a new arithmetic coding state * the returned pointer can simply be freed; this does * not affect the associated Jbig2WordStream. */ Jbig2ArithState * jbig2_arith_new(Jbig2Ctx *ctx, Jbig2WordStream *ws) { Jbig2ArithState *result; int new_bytes; result = jbig2_new(ctx, Jbig2ArithState, 1); if (result == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate arithmetic coding state"); return NULL; } result->ws = ws; new_bytes = ws->get_next_word(ws, 0, &result->next_word); result->next_word_bytes = new_bytes; result->offset = new_bytes; /* Figure E.20 */ result->C = (~(result->next_word >> 8)) & 0xFF0000; jbig2_arith_bytein(result); result->C <<= 7; result->CT -= 7; result->A = 0x8000; return result; } #define MAX_QE_ARRAY_SIZE 47 /* could put bit fields in to minimize memory usage */ typedef struct { unsigned short Qe; byte mps_xor; /* mps_xor = index ^ NMPS */ byte lps_xor; /* lps_xor = index ^ NLPS ^ (SWITCH << 7) */ } Jbig2ArithQe; static const Jbig2ArithQe jbig2_arith_Qe[MAX_QE_ARRAY_SIZE] = { {0x5601, 1 ^ 0, 1 ^ 0 ^ 0x80}, {0x3401, 2 ^ 1, 6 ^ 1}, {0x1801, 3 ^ 2, 9 ^ 2}, {0x0AC1, 4 ^ 3, 12 ^ 3}, {0x0521, 5 ^ 4, 29 ^ 4}, {0x0221, 38 ^ 5, 33 ^ 5}, {0x5601, 7 ^ 6, 6 ^ 6 ^ 0x80}, {0x5401, 8 ^ 7, 14 ^ 7}, {0x4801, 9 ^ 8, 14 ^ 8}, {0x3801, 10 ^ 9, 14 ^ 9}, {0x3001, 11 ^ 10, 17 ^ 10}, {0x2401, 12 ^ 11, 18 ^ 11}, {0x1C01, 13 ^ 12, 20 ^ 12}, {0x1601, 29 ^ 13, 21 ^ 13}, {0x5601, 15 ^ 14, 14 ^ 14 ^ 0x80}, {0x5401, 16 ^ 15, 14 ^ 15}, {0x5101, 17 ^ 16, 15 ^ 16}, {0x4801, 18 ^ 17, 16 ^ 17}, {0x3801, 19 ^ 18, 17 ^ 18}, {0x3401, 20 ^ 19, 18 ^ 19}, {0x3001, 21 ^ 20, 19 ^ 20}, {0x2801, 22 ^ 21, 19 ^ 21}, {0x2401, 23 ^ 22, 20 ^ 22}, {0x2201, 24 ^ 23, 21 ^ 23}, {0x1C01, 25 ^ 24, 22 ^ 24}, {0x1801, 26 ^ 25, 23 ^ 25}, {0x1601, 27 ^ 26, 24 ^ 26}, {0x1401, 28 ^ 27, 25 ^ 27}, {0x1201, 29 ^ 28, 26 ^ 28}, {0x1101, 30 ^ 29, 27 ^ 29}, {0x0AC1, 31 ^ 30, 28 ^ 30}, {0x09C1, 32 ^ 31, 29 ^ 31}, {0x08A1, 33 ^ 32, 30 ^ 32}, {0x0521, 34 ^ 33, 31 ^ 33}, {0x0441, 35 ^ 34, 32 ^ 34}, {0x02A1, 36 ^ 35, 33 ^ 35}, {0x0221, 37 ^ 36, 34 ^ 36}, {0x0141, 38 ^ 37, 35 ^ 37}, {0x0111, 39 ^ 38, 36 ^ 38}, {0x0085, 40 ^ 39, 37 ^ 39}, {0x0049, 41 ^ 40, 38 ^ 40}, {0x0025, 42 ^ 41, 39 ^ 41}, {0x0015, 43 ^ 42, 40 ^ 42}, {0x0009, 44 ^ 43, 41 ^ 43}, {0x0005, 45 ^ 44, 42 ^ 44}, {0x0001, 45 ^ 45, 43 ^ 45}, {0x5601, 46 ^ 46, 46 ^ 46} }; static void jbig2_arith_renormd(Jbig2ArithState *as) { /* Figure E.18 */ do { if (as->CT == 0) jbig2_arith_bytein(as); as->A <<= 1; as->C <<= 1; as->CT--; } while ((as->A & 0x8000) == 0); } int jbig2_arith_decode(Jbig2ArithState *as, Jbig2ArithCx *pcx) { Jbig2ArithCx cx = *pcx; const Jbig2ArithQe *pqe; unsigned int index = cx & 0x7f; bool D; if (index >= MAX_QE_ARRAY_SIZE) return -1; /* Error */ pqe = &jbig2_arith_Qe[index]; /* Figure E.15 */ as->A -= pqe->Qe; if ((as->C >> 16) < as->A) { if ((as->A & 0x8000) == 0) { /* MPS_EXCHANGE, Figure E.16 */ if (as->A < pqe->Qe) { D = 1 - (cx >> 7); *pcx ^= pqe->lps_xor; } else { D = cx >> 7; *pcx ^= pqe->mps_xor; } jbig2_arith_renormd(as); return D; } else { return cx >> 7; } } else { as->C -= (as->A) << 16; /* LPS_EXCHANGE, Figure E.17 */ if (as->A < pqe->Qe) { as->A = pqe->Qe; D = cx >> 7; *pcx ^= pqe->mps_xor; } else { as->A = pqe->Qe; D = 1 - (cx >> 7); *pcx ^= pqe->lps_xor; } jbig2_arith_renormd(as); return D; } } #ifdef TEST static const byte test_stream[] = { 0x84, 0xC7, 0x3B, 0xFC, 0xE1, 0xA1, 0x43, 0x04, 0x02, 0x20, 0x00, 0x00, 0x41, 0x0D, 0xBB, 0x86, 0xF4, 0x31, 0x7F, 0xFF, 0x88, 0xFF, 0x37, 0x47, 0x1A, 0xDB, 0x6A, 0xDF, 0xFF, 0xAC, 0x00, 0x00 }; #if defined(JBIG2_DEBUG) || defined(JBIG2_DEBUG_ARITH) static void jbig2_arith_trace(Jbig2ArithState *as, Jbig2ArithCx cx) { fprintf(stderr, "I = %2d, MPS = %d, A = %04x, CT = %2d, C = %08x\n", cx & 0x7f, cx >> 7, as->A, as->CT, as->C); } #endif static int test_get_word(Jbig2WordStream *self, size_t offset, uint32_t *word) { uint32_t val = 0; int ret = 0; if (self == NULL || word == NULL) return -1; if (offset >= sizeof (test_stream)) return 0; if (offset < sizeof(test_stream)) { val |= test_stream[offset] << 24; ret++; } if (offset + 1 < sizeof(test_stream)) { val |= test_stream[offset + 1] << 16; ret++; } if (offset + 2 < sizeof(test_stream)) { val |= test_stream[offset + 2] << 8; ret++; } if (offset + 3 < sizeof(test_stream)) { val |= test_stream[offset + 3]; ret++; } *word = val; return ret; } int main(int argc, char **argv) { Jbig2Ctx *ctx; Jbig2WordStream ws; Jbig2ArithState *as; int i; Jbig2ArithCx cx = 0; ctx = jbig2_ctx_new(NULL, 0, NULL, NULL, NULL); ws.get_next_word = test_get_word; as = jbig2_arith_new(ctx, &ws); #ifdef JBIG2_DEBUG_ARITH jbig2_arith_trace(as, cx); #endif for (i = 0; i < 256; i++) { #ifdef JBIG2_DEBUG_ARITH bool D = #else (void) #endif jbig2_arith_decode(as, &cx); #ifdef JBIG2_DEBUG_ARITH fprintf(stderr, "%3d: D = %d, ", i, D); jbig2_arith_trace(as, cx); #endif } jbig2_free(ctx->allocator, as); jbig2_ctx_free(ctx); return 0; } #endif jbig2dec-0.18/jbig2_arith.h000066400000000000000000000025111362055302000154430ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_ARITH_H #define _JBIG2_ARITH_H typedef struct _Jbig2ArithState Jbig2ArithState; /* An arithmetic coding context is stored as a single byte, with the index in the low order 7 bits (actually only 6 are used), and the MPS in the top bit. */ typedef unsigned char Jbig2ArithCx; /* allocate and initialize a new arithmetic coding state */ Jbig2ArithState *jbig2_arith_new(Jbig2Ctx *ctx, Jbig2WordStream *ws); /* decode a bit */ /* Normally returns 0 or 1. May return negative in case of error. */ int jbig2_arith_decode(Jbig2ArithState *as, Jbig2ArithCx *pcx); /* returns true if the end of the data stream has been reached (for sanity checks) */ bool jbig2_arith_has_reached_marker(Jbig2ArithState *as); #endif /* _JBIG2_ARITH_H */ jbig2dec-0.18/jbig2_arith_iaid.c000066400000000000000000000052531362055302000164320ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* Annex A.3 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include /* memset() */ #ifdef VERBOSE #include /* for debug printing only */ #endif #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_arith_iaid.h" struct _Jbig2ArithIaidCtx { int SBSYMCODELEN; Jbig2ArithCx *IAIDx; }; Jbig2ArithIaidCtx * jbig2_arith_iaid_ctx_new(Jbig2Ctx *ctx, int SBSYMCODELEN) { Jbig2ArithIaidCtx *result = jbig2_new(ctx, Jbig2ArithIaidCtx, 1); int ctx_size = 1 << SBSYMCODELEN; if (result == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate IAID arithmetic coding state"); return NULL; } result->SBSYMCODELEN = SBSYMCODELEN; result->IAIDx = jbig2_new(ctx, Jbig2ArithCx, ctx_size); if (result->IAIDx == NULL) { jbig2_free(ctx->allocator, result); jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate symbol ID in IAID arithmetic coding state"); return NULL; } memset(result->IAIDx, 0, ctx_size); return result; } /* A.3 */ /* Return value: -1 on error, 0 on normal value */ int jbig2_arith_iaid_decode(Jbig2Ctx *ctx, Jbig2ArithIaidCtx *actx, Jbig2ArithState *as, int32_t *p_result) { Jbig2ArithCx *IAIDx = actx->IAIDx; int SBSYMCODELEN = actx->SBSYMCODELEN; int PREV = 1; int D; int i; /* A.3 (2) */ for (i = 0; i < SBSYMCODELEN; i++) { D = jbig2_arith_decode(as, &IAIDx[PREV]); if (D < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAIDx code"); #ifdef VERBOSE fprintf(stderr, "IAID%x: D = %d\n", PREV, D); #endif PREV = (PREV << 1) | D; } /* A.3 (3) */ PREV -= 1 << SBSYMCODELEN; #ifdef VERBOSE fprintf(stderr, "IAID result: %d\n", PREV); #endif *p_result = PREV; return 0; } void jbig2_arith_iaid_ctx_free(Jbig2Ctx *ctx, Jbig2ArithIaidCtx *iax) { if (iax != NULL) { jbig2_free(ctx->allocator, iax->IAIDx); jbig2_free(ctx->allocator, iax); } } jbig2dec-0.18/jbig2_arith_iaid.h000066400000000000000000000017531362055302000164400ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_ARITH_IAID_H #define _JBIG2_ARITH_IAID_H typedef struct _Jbig2ArithIaidCtx Jbig2ArithIaidCtx; Jbig2ArithIaidCtx *jbig2_arith_iaid_ctx_new(Jbig2Ctx *ctx, int SBSYMCODELEN); int jbig2_arith_iaid_decode(Jbig2Ctx *ctx, Jbig2ArithIaidCtx *actx, Jbig2ArithState *as, int32_t *p_result); void jbig2_arith_iaid_ctx_free(Jbig2Ctx *ctx, Jbig2ArithIaidCtx *iax); #endif /* _JBIG2_ARITH_IAID_H */ jbig2dec-0.18/jbig2_arith_int.c000066400000000000000000000103141362055302000163100ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* Annex A */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include /* memset() */ #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_arith_int.h" #if !defined (INT32_MIN) #define INT32_MIN (-0x7fffffff - 1) #endif #if !defined (INT32_MAX) #define INT32_MAX 0x7fffffff #endif struct _Jbig2ArithIntCtx { Jbig2ArithCx IAx[512]; }; Jbig2ArithIntCtx * jbig2_arith_int_ctx_new(Jbig2Ctx *ctx) { Jbig2ArithIntCtx *result = jbig2_new(ctx, Jbig2ArithIntCtx, 1); if (result == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate arithmetic integer coding state"); return NULL; } else { memset(result->IAx, 0, sizeof(result->IAx)); } return result; } /* A.2 */ /* Return value: -1 on error, 0 on normal value, 1 on OOB return. */ int jbig2_arith_int_decode(Jbig2Ctx *ctx, Jbig2ArithIntCtx *actx, Jbig2ArithState *as, int32_t *p_result) { Jbig2ArithCx *IAx = actx->IAx; int PREV = 1; int S; int32_t V; int bit; int n_tail, offset; int i; S = jbig2_arith_decode(as, &IAx[PREV]); if (S < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx S"); PREV = (PREV << 1) | S; bit = jbig2_arith_decode(as, &IAx[PREV]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx decision bit 0"); PREV = (PREV << 1) | bit; if (bit) { bit = jbig2_arith_decode(as, &IAx[PREV]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx decision bit 1"); PREV = (PREV << 1) | bit; if (bit) { bit = jbig2_arith_decode(as, &IAx[PREV]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx decision bit 2"); PREV = (PREV << 1) | bit; if (bit) { bit = jbig2_arith_decode(as, &IAx[PREV]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx decision bit 3"); PREV = (PREV << 1) | bit; if (bit) { bit = jbig2_arith_decode(as, &IAx[PREV]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx decision bit 4"); PREV = (PREV << 1) | bit; if (bit) { n_tail = 32; offset = 4436; } else { n_tail = 12; offset = 340; } } else { n_tail = 8; offset = 84; } } else { n_tail = 6; offset = 20; } } else { n_tail = 4; offset = 4; } } else { n_tail = 2; offset = 0; } V = 0; for (i = 0; i < n_tail; i++) { bit = jbig2_arith_decode(as, &IAx[PREV]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx V bit %d", i); PREV = ((PREV << 1) & 511) | (PREV & 256) | bit; V = (V << 1) | bit; } /* make sure not to underflow/overflow 32 bit value */ if (V < INT32_MAX - 4436 || V > INT32_MIN + 4436) V += offset; V = S ? -V : V; *p_result = V; return S && V == 0 ? 1 : 0; } void jbig2_arith_int_ctx_free(Jbig2Ctx *ctx, Jbig2ArithIntCtx *iax) { jbig2_free(ctx->allocator, iax); } jbig2dec-0.18/jbig2_arith_int.h000066400000000000000000000017161362055302000163230ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_ARITH_INT_H #define _JBIG2_ARITH_INT_H typedef struct _Jbig2ArithIntCtx Jbig2ArithIntCtx; Jbig2ArithIntCtx *jbig2_arith_int_ctx_new(Jbig2Ctx *ctx); int jbig2_arith_int_decode(Jbig2Ctx *ctx, Jbig2ArithIntCtx *actx, Jbig2ArithState *as, int32_t *p_result); void jbig2_arith_int_ctx_free(Jbig2Ctx *ctx, Jbig2ArithIntCtx *iax); #endif /* _JBIG2_ARITH_INT_H */ jbig2dec-0.18/jbig2_generic.c000066400000000000000000001755321362055302000157610ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /** * Generic region handlers. **/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include /* memcpy(), memset() */ #ifdef OUTPUT_PBM #include #endif #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_generic.h" #include "jbig2_image.h" #include "jbig2_mmr.h" #include "jbig2_page.h" #include "jbig2_segment.h" #if !defined (UINT32_MAX) #define UINT32_MAX 0xffffffff #endif /* This is an explanation of the unoptimized and optimized generic region decoder implementations below, wherein we try to explain all the magic numbers. The generic region decoders decode the output pixels one row at a time, top to bottom. Within each row the pixels are decoded left to right. The input for the arithmetic integer decoder used to decode each pixel is a context consisting of up to 16 previously decoded pixels. These pixels are chosen according to a predefined template placed relative to the location of the pixel to be decoded (6.2.5.3 figures 3, 4, 5 and 6). There are four different template that may be used (6.2.5.3). The template to use is determined by GBTEMPLATE. GBTEMPLATE is set in the symbol dictionary (6.5.8.1), generic region (7.4.6.4), or when decoding a halftone region's gray-scale image (annex C.5). Most of the pixels in each template have fixed locations relative to the pixel to be decoded. However, all templates have at least one adaptive pixel. The adaptive pixels have nominal locations, but these locations may be changed by GBAT. GBAT is set in the symbol dictionary (7.4.2.1.2), generic region (7.4.6.1), or hard coded as for halftone patterns (6.7.5). Adaptive pixels are restricted to fall within a field of previously decoded pixels relative to the pixel to be decoded (figure 7). The relative Y-coordinate for these adaptive pixels may vary between -128 and 0. The relative X-coordinate may vary between -128 and +127 (however, if the Y-coordinate is 0 the range of the X-coordinate is further restricted to -128 to -1 since the pixels at locations 0 to +127 have not yet been decoded). If a template refers to a pixel location that reside outside of the image boundaries its value is assumed to be 0. UNOPTIMIZED DECODER The unoptimized decoders first check the contents of GBAT. If GBAT specifies that any of the adaptive pixels reside outside the allowed field the decoding is aborted. Next, each row is processed top to bottom, left to right, one pixel at a time. For each pixel a context is created containing the bit values of the pixels that fall inside the template. The order these bits are stored in the context is implementation dependent (6.2.5.3). We store the bit values in the CONTEXT variable from LSB to MSB, starting with the value of the pixel to the left of the current pixel, continuing right to left, bottom to top following the template. Using the CONTEXT created from these pixel values, the arithmetic integer decoder retrieves the pixel value, which is then written into the output image. Example when GBTEMPLATE is 2: The figure below represents a pixel grid of the output image. Each pixel is a single bit in the image. The pixel "OO" in the figure below is about to be decoded. The pixels "??" have not been decoded yet. The CONTEXT variable is constructed by combining the bit values from the pixels referred to by the template, shifted to their corresponding bit position. . . . . . . . . . . . . . . . . ...+----+----+----+----+----+----+----+... | | | X9 | X8 | X7 | | | ...+----+----+----+----+----+----+----+... | | X6 | X5 | X4 | X3 | A1 | | ...+----+----+----+----+----+----+----+... | | X2 | X1 | OO | ?? | ?? | ?? | ...+----+----+----+----+----+----+----+... . . . . . . . . . . . . . . . . In the table below pixel OO is assumed to be at coordinate (x, y). Bit 9: Pixel at location (x-1, y-2) (This is fixed pixel X9) Bit 8: Pixel at location (x , y-2) (This is fixed pixel X8) Bit 7: Pixel at location (x+1, y-2) (This is fixed pixel X7) Bit 6: Pixel at location (x-2, y-1) (This is fixed pixel X6) Bit 5: Pixel at location (x-1, y-1) (This is fixed pixel X5) Bit 4: Pixel at location (x , y-1) (This is fixed pixel X4) Bit 3: Pixel at location (x+1, y-1) (This is fixed pixel X3) Bit 2: Pixel at location (x+2, y-1) (This is adaptive pixel A1) Bit 1: Pixel at location (x-2, y ) (This is fixed pixel X2) Bit 0: Pixel at location (x-1, y ) (This is fixed pixel X1) The location of adaptive pixel A1 may not always be at the nominal location (x+2, y-1). It could be at any pixel location to the left or above OO as specified by GBAT, e.g. at the location (x-128, y+127). OPTIMIZED DECODER The optimized decoders work differently. They strive to avoid recreating the arithmetic integer decoder context from scratch for every pixel decoded. Instead they reuse part of the CONTEXT used to compute the previous pixel (the pixel to left of the one now being decoded). They also keep two sliding windows of pixel bit values from the two rows of pixels immediately above the pixel to be decoded. These are stored in the 32-bit variables line_m1 (row above the pixel to be decoded) and line_m2 (row above that of line_m1). These optimized decoders ONLY work for the nominal adaptive pixel locations since these locations are hard-coded into the implementation. The bit ordering in the CONTEXT variable is identical to the unoptimized case described above. The optimized decoders decode the output pixels one row at a time, top to bottom. Within each row the pixels are decoded in batches of up to eight pixels at a time (except possibly the right most batch which may be less than eight pixels). The batches in a row are decoded in sequence from left to right. Within each such batch the pixels are decoded in sequence from left to right. Before decoding the pixels in a row the two sliding windows of pixel values are reset. The first eight pixels of the row above the pixel to be decoded is stored in line_m1, while line_m2 stores the first eight pixels of the row above that of line_m1. The figure below illustrates the situation where the template has been placed so that the decoded pixel OO is the very first pixel of a row. It also gives labels to various pixels that we will refer to below. . . . . . . . . . . . | . . . . . . . . . . + + +----+----+----+----+----+----+----+----+----+----+... X9 | X8 | X7 | m1 | m2 | m3 | m4 | m5 | m6 | m7 | | + + +----+----+----+----+----+----+----+----+----+----+... X6 X5 | X4 | X3 | A1 | n1 | n2 | n3 | n4 | n5 | n6 | n7 | + + +----+----+----+----+----+----+----+----+----+----+... X2 X1 | OO | | | | | | | | | | + + +----+----+----+----+----+----+----+----+----+----+... | . . . . . . . . . . . . . . . . . . . . . The pixels X1, X2, X5, X6 and X9 all reside outside the left edge of the image. These pixels (like all others outside the image) can according to 6.2.5.2 be assumed to be 0. line_m1 stores n5 through n1 as well as A1, and X3 through X6. line_m2 stores m6 through m1 as well as X7 through X9. The bits in line_m2 are also shifted left four bits as seen below. 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | bit position ------------------------------------------------+------------------ 0 0 0 0 0 0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5 | line_m1 0 0 0 X9 X8 X7 m1 m2 m3 m4 m5 m6 0 0 0 0 | line_m2 The way line_m1 and line_m2 are stored means we can simply shift them by the same amount to move the sliding window. The bit order in line_m1 and line_m2 matches the ordering in the CONTEXT variable. Each bit for the 'A' and 'X' pixels in line_m1 and line_m2 correspond to the equivalent bits in CONTEXT, only shifted right by 3 bits. Thus X3 is bit 3 in CONTEXT and bit 6 in line_m1, etc. The initial arithmetic integer decoder context is created and stored in the CONTEXT variable by masking, shifting, and bitwise ORing the contents of line_m1 and line_m2. The "CONTEXT contents" row is only shown for clarity, it is not present in the code. 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | bit position ------------------------------------------------+--------------------------- 0 0 0 0 0 0 0 0 0 X6 X5 X4 X3 A1 n1 n2 | line_m1 >> 3 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 | mask for line_m1 (0x7c) 0 0 0 0 0 0 0 0 0 X6 X5 X4 X3 A1 0 0 | line_m1 AND mask ------------------------------------------------+--------------------------- 0 0 0 0 0 0 X9 X8 X7 m1 m2 m3 m4 m5 m6 0 | line_m2 >> 3 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 | mask for line_m2 (0x380) 0 0 0 0 0 0 X9 X8 X7 0 0 0 0 0 0 0 | line_m2 AND mask ------------------------------------------------+--------------------------- 0 0 0 0 0 0 X9 X8 X7 X6 X5 X4 X3 A1 0 0 | CONTEXT = line_m1 OR line_m2 ------------------------------------------------+--------------------------- 0 0 0 0 0 0 X9 X8 X7 X6 X5 X4 X3 A1 X2 X1 | CONTEXT contents Each batch is normally 8 bits, but at the right edge of the image we may have fewer pixels to decode. The minor_width is how many pixels the current batch should decode, with a counter variable x_minor to keep track of the current pixel being decoded. In order to process a new batch of pixels, unless we're at the rightmost batch of pixels, we need to refill the sliding window variables with eight new bits. Looking at the diagram above we can see that in order to decode eight pixels starting with O0 we'll need to have bits up to pixel 'n7' for line_m1 and 'm7' for line_m2 available (A1 and X7 moved right 7 times). To do this simply and quickly, we shift line_m1 left by 8 bits, and OR in the next byte from corresponding row. Likewise for line_m2, but the next byte from the image is also shifted left by 4 bits to compensate for line_m2 having the four least significant bits unused. These new eight bits contain the bit values of the eight pixels to the right of those already present in line_m1 and line_m2. We call these new bits m7 through mE, and n6 through nD, as illustrated below. 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | bit position ------------------------------------------------------------------------+------------- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5 | original line_m1 0 0 0 0 0 0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5 0 0 0 0 0 0 0 0 | line_m1 shifted left by 8 0 0 0 0 0 0 X6 X5 X4 X3 A1 n1 n2 n3 n4 n5 n6 n7 n8 n9 nA nB nC nD | line_m1 with new bits ORed in ------------------------------------------------------------------------+------------- 0 0 0 0 0 0 0 0 0 0 0 X9 X8 X7 m1 m2 m3 m4 m5 m6 0 0 0 0 | original line_m2 0 0 0 X9 X8 X7 m1 m2 m3 m4 m5 m6 0 0 0 0 0 0 0 0 0 0 0 0 | line_m2 shifted left by 8 0 0 0 X9 X8 X7 m1 m2 m3 m4 m5 m6 m7 m8 m9 mA mB mC mD mE 0 0 0 0 | line_m2 with new bits ORed in . . . . . . . . . . . . . . . . . . . . | . . . . . . . . . . . . . . . . . . . + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... X9 | X8 | X7 | m1 | m2 | m3 | m4 | m5 | m6 | m7 | m8 | m9 | mA | mB | mC | mD | mE | | | | + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... X6 X5 | X4 | X3 | A1 | n1 | n2 | n3 | n4 | n5 | n6 | n7 | n8 | n9 | nA | nB | nC | nD | | | | + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... X2 X1 | OO | | | | | | | | | | | | | | | | | | | + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . CONTEXT, line_m1 and line_m2 now contain all necessary bits to decode a full batch of eight pixels. The first pixel in the batch is decoded using this CONTEXT. After that, for each following pixel we need to update the CONTEXT using both the last decoded pixel value and new bits from line_m1 and line_m2. . . . . . . . . . . . . . . . . . . . . | . . . . . . . . . . . . . . . . . . . + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... (X9)|_X8_|_X7_|>m1<| m2 | m3 | m4 | m5 | m6 | m7 | m8 | m9 | mA | mB | mC | mD | mE | | | | + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... (X6) _X5_|_X4_|_X3_|_A1_|>n1<| n2 | n3 | n4 | n5 | n6 | n7 | n8 | n9 | nA | nB | nC | nD | | | | + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... (X2) _X1_|>OO<| oo | | | | | | | | | | | | | | | | | | + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+... | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . This figure illustrates what happens when the same template is overlaid on itself shifted one pixel to the right in order to decode the next pixel. Pixels marked with _ _ are pixels that are present in both templates' CONTEXTs and can be reused. Pixels marked with ( ) are pixels from the first template that are no longer necessary and can be removed from CONTEXT. Pixels marked with > < are new pixels that were not part of the original CONTEXT, and so need to be moved into the CONTEXT at the appropriate locations. In general the leftmost pixels of each template row can be forgotten, while new pixels are needed at the right most location of each row. The CONTEXT corresponding to the current pixel OO and how it is masked is shown below. Note how the left most pixel of each row of the template is NOT propagated to the CONTEXT, these pixels are X2, X6 and X9. This is done by having the mask being 0 at the corresponding locations. 9 8 7 6 5 4 3 2 1 0 | bit position ------------------------------+------------- X9 X8 X7 X6 X5 X4 X3 A1 X2 X1 | pixel values from CONTEXT 0 1 1 0 1 1 1 1 0 1 | reused pixel bit value mask (0x1bd) 0 X8 X7 0 X5 X4 X3 A1 0 X1 | reused pixel values from CONTEXT Next the CONTEXT is shifted left by one bit to make it reference the next pixel to be decoded. The pixel bit value we just decoded is then written into the bit corresponding to X1. The sliding windows in line_m1 and line_m2 are both shifted (10 - x_minor) bits to the right to make the needed pixels' bit values appear at the correct positions to be ORed into CONTEXT. Note that this shift amount depends on which bit in the batch is currently being computed, as is given by the x_minor counter. In the example below we assume that x_minor is 0. 9 8 7 6 5 4 3 2 1 0 | bit position ------------------------------+-------------- 0 X8 X7 0 X5 X4 X3 A1 0 0 | reused pixels from CONTEXT X8 X7 0 X5 X4 X3 A1 0 0 0 | reused pixels shifted left 1 bit ------------------------------+-------------- X8 X7 0 X5 X4 X3 A1 0 X1 OO | new CONTEXT with current pixel at LSB ------------------------------+-------------- 0 0 X6 X5 X4 X3 A1 n1 n2 n3 | line_m1 shifted (10 - x_minor) bits to the right 0 0 0 0 0 0 0 1 0 0 | mask for new adaptive pixel one row above (0x4) X8 X7 0 X5 X4 X3 A1 n1 X1 OO | new CONTEXT with new adaptive pixel ------------------------------+-------------- X8 X7 m1 m2 m3 m4 m5 m6 m7 m8 | line_m2 with new bits ORed in 0 0 1 0 0 0 0 0 0 0 | mask for new pixel two rows above (0x80) X8 X7 m1 X5 X4 X3 A1 n1 X1 OO | new CONTEXT with new pixel This makes the computation of the new CONTEXT be: NEWCONTEXT = (CONTEXT & 0x1bd) << 1 NEWCONTEXT |= newbit; NEWCONTEXT |= (line_m1 >> (10-x_minor)) & 0x4; NEWCONTEXT |= (line_m2 >> (10-x_minor)) & 0x80; The optimized decoding functions for GBTEMPLATE 0, 1 and 3 all work similarly. */ /* Get a bit. No bounds checking. */ static inline int jbig2_image_get_pixel_fast(Jbig2Image *image, int x, int y) { const int byte = (x >> 3) + y * image->stride; const int bit = 7 - (x & 7); return ((image->data[byte] >> bit) & 1); } /* return the appropriate context size for the given template */ int jbig2_generic_stats_size(Jbig2Ctx *ctx, int template) { int stats_size = template == 0 ? 1 << 16 : template == 1 ? 1 << 13 : 1 << 10; return stats_size; } static int jbig2_decode_generic_template0(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) { const uint32_t GBW = image->width; const uint32_t GBH = image->height; const uint32_t rowstride = image->stride; uint32_t x, y; byte *line2 = NULL; byte *line1 = NULL; byte *gbreg_line = (byte *) image->data; #ifdef OUTPUT_PBM printf("P4\n%d %d\n", GBW, GBH); #endif if (GBW <= 0) return 0; for (y = 0; y < GBH; y++) { uint32_t CONTEXT; uint32_t line_m1; uint32_t line_m2; uint32_t padded_width = (GBW + 7) & -8; line_m1 = line1 ? line1[0] : 0; line_m2 = line2 ? line2[0] << 6 : 0; CONTEXT = (line_m1 & 0x7f0) | (line_m2 & 0xf800); /* 6.2.5.7 3d */ for (x = 0; x < padded_width; x += 8) { byte result = 0; int x_minor; int minor_width = GBW - x > 8 ? 8 : GBW - x; if (line1) line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0); if (line2) line_m2 = (line_m2 << 8) | (x + 8 < GBW ? line2[(x >> 3) + 1] << 6 : 0); /* This is the speed-critical inner loop. */ for (x_minor = 0; x_minor < minor_width; x_minor++) { int bit; bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 optimized"); result |= bit << (7 - x_minor); CONTEXT = ((CONTEXT & 0x7bf7) << 1) | bit | ((line_m1 >> (7 - x_minor)) & 0x10) | ((line_m2 >> (7 - x_minor)) & 0x800); } gbreg_line[x >> 3] = result; } #ifdef OUTPUT_PBM fwrite(gbreg_line, 1, rowstride, stdout); #endif line2 = line1; line1 = gbreg_line; gbreg_line += rowstride; } return 0; } #define pixel_outside_field(x, y) \ ((y) < -128 || (y) > 0 || (x) < -128 || ((y) < 0 && (x) > 127) || ((y) == 0 && (x) >= 0)) static int jbig2_decode_generic_template0_unopt(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) { const uint32_t GBW = image->width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int bit; if (pixel_outside_field(params->gbat[0], params->gbat[1]) || pixel_outside_field(params->gbat[2], params->gbat[3]) || pixel_outside_field(params->gbat[4], params->gbat[5]) || pixel_outside_field(params->gbat[6], params->gbat[7])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GBH; y++) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint8_t *ppline = &image->data[image->stride * (y-2)]; uint32_t pd = 0; uint32_t ppd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; if (y > 1) { ppd = (*ppline++ << 8); if (GBW > 8) ppd |= *ppline++; } } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x000F; /* First 4 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; CONTEXT |= (pd>>8) & 0x03E0; /* Next 5 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[2], y + params->gbat[3]) << 10; CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[4], y + params->gbat[5]) << 11; CONTEXT |= (ppd>>2) & 0x7000; /* Next 3 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[6], y + params->gbat[7]) << 15; bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 unoptimized"); } pd = pd<<1; ppd = ppd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = out_byte< 0) { pd |= *pline++; if (y > 1) ppd |= *ppline++; } } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int bit; if (pixel_outside_field(params->gbat[0], params->gbat[1])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GBH; y++) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint8_t *ppline = &image->data[image->stride * (y-2)]; uint32_t pd = 0; uint32_t ppd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; if (y > 1) { ppd = (*ppline++ << 8); if (GBW > 8) ppd |= *ppline++; } } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x0007; /* First 3 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 3; CONTEXT |= (pd>>9) & 0x01F0; /* Next 5 pixels */ CONTEXT |= (ppd>>4) & 0x1E00; /* Next 4 pixels */ bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 unoptimized"); } pd = pd<<1; ppd = ppd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = out_byte< 0) { pd |= *pline++; if (y > 1) ppd |= *ppline++; } } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<width; const uint32_t GBH = image->height; const uint32_t rowstride = image->stride; uint32_t x, y; byte *line2 = NULL; byte *line1 = NULL; byte *gbreg_line = (byte *) image->data; #ifdef OUTPUT_PBM printf("P4\n%d %d\n", GBW, GBH); #endif if (GBW <= 0) return 0; for (y = 0; y < GBH; y++) { uint32_t CONTEXT; uint32_t line_m1; uint32_t line_m2; uint32_t padded_width = (GBW + 7) & -8; line_m1 = line1 ? line1[0] : 0; line_m2 = line2 ? line2[0] << 5 : 0; CONTEXT = ((line_m1 >> 1) & 0x1f8) | ((line_m2 >> 1) & 0x1e00); /* 6.2.5.7 3d */ for (x = 0; x < padded_width; x += 8) { byte result = 0; int x_minor; int minor_width = GBW - x > 8 ? 8 : GBW - x; if (line1) line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0); if (line2) line_m2 = (line_m2 << 8) | (x + 8 < GBW ? line2[(x >> 3) + 1] << 5 : 0); /* This is the speed-critical inner loop. */ for (x_minor = 0; x_minor < minor_width; x_minor++) { int bit; bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 optimized"); result |= bit << (7 - x_minor); CONTEXT = ((CONTEXT & 0xefb) << 1) | bit | ((line_m1 >> (8 - x_minor)) & 0x8) | ((line_m2 >> (8 - x_minor)) & 0x200); } gbreg_line[x >> 3] = result; } #ifdef OUTPUT_PBM fwrite(gbreg_line, 1, rowstride, stdout); #endif line2 = line1; line1 = gbreg_line; gbreg_line += rowstride; } return 0; } static int jbig2_decode_generic_template2_unopt(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) { const uint32_t GBW = image->width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int bit; if (pixel_outside_field(params->gbat[0], params->gbat[1])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GBH; y++) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint8_t *ppline = &image->data[image->stride * (y-2)]; uint32_t pd = 0; uint32_t ppd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; if (y > 1) { ppd = (*ppline++ << 8); if (GBW > 8) ppd |= *ppline++; } } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x003; /* First 2 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 2; CONTEXT |= (pd>>11) & 0x078; /* Next 4 pixels */ CONTEXT |= (ppd>>7) & 0x380; /* Next 3 pixels */ bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 unoptimized"); } pd = pd<<1; ppd = ppd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = (uint8_t)out_byte< 0) { pd |= *pline++; if (y > 1) ppd |= *ppline++; } } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<width; const uint32_t GBH = image->height; const uint32_t rowstride = image->stride; uint32_t x, y; byte *line2 = NULL; byte *line1 = NULL; byte *gbreg_line = (byte *) image->data; #ifdef OUTPUT_PBM printf("P4\n%d %d\n", GBW, GBH); #endif if (GBW <= 0) return 0; for (y = 0; y < GBH; y++) { uint32_t CONTEXT; uint32_t line_m1; uint32_t line_m2; uint32_t padded_width = (GBW + 7) & -8; line_m1 = line1 ? line1[0] : 0; line_m2 = line2 ? line2[0] << 4 : 0; CONTEXT = ((line_m1 >> 3) & 0x7c) | ((line_m2 >> 3) & 0x380); /* 6.2.5.7 3d */ for (x = 0; x < padded_width; x += 8) { byte result = 0; int x_minor; int minor_width = GBW - x > 8 ? 8 : GBW - x; if (line1) line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0); if (line2) line_m2 = (line_m2 << 8) | (x + 8 < GBW ? line2[(x >> 3) + 1] << 4 : 0); /* This is the speed-critical inner loop. */ for (x_minor = 0; x_minor < minor_width; x_minor++) { int bit; bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 optimized"); result |= bit << (7 - x_minor); CONTEXT = ((CONTEXT & 0x1bd) << 1) | bit | ((line_m1 >> (10 - x_minor)) & 0x4) | ((line_m2 >> (10 - x_minor)) & 0x80); } gbreg_line[x >> 3] = result; } #ifdef OUTPUT_PBM fwrite(gbreg_line, 1, rowstride, stdout); #endif line2 = line1; line1 = gbreg_line; gbreg_line += rowstride; } return 0; } static int jbig2_decode_generic_template3(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) { const uint32_t GBW = image->width; const uint32_t GBH = image->height; const uint32_t rowstride = image->stride; byte *line1 = NULL; byte *gbreg_line = (byte *) image->data; uint32_t x, y; #ifdef OUTPUT_PBM printf("P4\n%d %d\n", GBW, GBH); #endif if (GBW <= 0) return 0; for (y = 0; y < GBH; y++) { uint32_t CONTEXT; uint32_t line_m1; uint32_t padded_width = (GBW + 7) & -8; line_m1 = line1 ? line1[0] : 0; CONTEXT = (line_m1 >> 1) & 0x3f0; /* 6.2.5.7 3d */ for (x = 0; x < padded_width; x += 8) { byte result = 0; int x_minor; int minor_width = GBW - x > 8 ? 8 : GBW - x; if (line1) line_m1 = (line_m1 << 8) | (x + 8 < GBW ? line1[(x >> 3) + 1] : 0); /* This is the speed-critical inner loop. */ for (x_minor = 0; x_minor < minor_width; x_minor++) { int bit; bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 optimized"); result |= bit << (7 - x_minor); CONTEXT = ((CONTEXT & 0x1f7) << 1) | bit | ((line_m1 >> (8 - x_minor)) & 0x10); } gbreg_line[x >> 3] = result; } #ifdef OUTPUT_PBM fwrite(gbreg_line, 1, rowstride, stdout); #endif line1 = gbreg_line; gbreg_line += rowstride; } return 0; } static int jbig2_decode_generic_template3_unopt(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) { const uint32_t GBW = image->width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int bit; if (pixel_outside_field(params->gbat[0], params->gbat[1])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GBH; y++) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint32_t pd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x00F; /* First 4 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; CONTEXT |= (pd>>9) & 0x3E0; /* Next 5 pixels */ bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 unoptimized"); } pd = pd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = (uint8_t)out_byte< 0) pd |= *pline++; } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<data, 0, image->stride); } else { /* duplicate data from the previous row */ uint8_t *src = image->data + (row - 1) * image->stride; memcpy(src + image->stride, src, image->stride); } } static int jbig2_decode_generic_template0_TPGDON(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) { const uint32_t GBW = image->width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int LTP = 0; int gmin, gmax; uint32_t left, right, top; if (pixel_outside_field(params->gbat[0], params->gbat[1]) || pixel_outside_field(params->gbat[2], params->gbat[3]) || pixel_outside_field(params->gbat[4], params->gbat[5]) || pixel_outside_field(params->gbat[6], params->gbat[7])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); /* JBig2 has 'standard' values for gbat (see 6.2.5.4 of the spec). * Have an optimised version for those locations. This greatly * simplifies some of the fetches. It's almost like they thought * it through. */ if (params->gbat[0] == 3 && params->gbat[1] == -1 && params->gbat[2] == -3 && params->gbat[3] == -1 && params->gbat[4] == 2 && params->gbat[5] == -2 && params->gbat[6] == -2 && params->gbat[7] == -2) { for (y = 0; y < GBH; y++) { int bit = jbig2_arith_decode(as, &GB_stats[0x9B25]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON1"); LTP ^= bit; if (!LTP) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint8_t *ppline = &image->data[image->stride * (y-2)]; uint32_t pd = 0; uint32_t ppd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; if (y > 1) { ppd = (*ppline++ << 8); if (GBW > 8) ppd |= *ppline++; } } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x00F; /* First 4 pixels */ CONTEXT |= (pd>>8) & 0x7F0; /* Next 7 pixels */ CONTEXT |= (ppd>>2) & 0xF800; /* Final 5 pixels */ bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON2"); } pd = pd<<1; ppd = ppd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; if (out_bits_to_go_in_byte == 0) { out_bits_to_go_in_byte = 8; *d++ = (uint8_t)out_byte; if (x+9 < GBW && y > 0) { pd |= *pline++; if (y > 1) ppd |= *ppline++; } } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<gbat[0]; if (params->gbat[2] < gmin) gmin = params->gbat[2]; if (gmax < params->gbat[2]) gmax = params->gbat[2]; if (params->gbat[4] < gmin) gmin = params->gbat[4]; if (gmax < params->gbat[4]) gmax = params->gbat[4]; if (params->gbat[6] < gmin) gmin = params->gbat[6]; if (gmax < params->gbat[6]) gmax = params->gbat[6]; if ((int)left < -gmin) left = -gmin; if ((int)right < gmax) right = gmax; if (right > GBW) right = GBW; right = GBW - right; /* So 0 <= x < left or right <= x < GBW needs bounds checking. */ /* Now we do the same for the height, but here there is no bottom * region, as we only ever look up for y. */ top = 2; gmin = params->gbat[1]; if (params->gbat[3] < gmin) gmin = params->gbat[3]; if (params->gbat[5] < gmin) gmin = params->gbat[5]; if (params->gbat[7] < gmin) gmin = params->gbat[7]; if ((int)top < -gmin) top = -gmin; /* So 0 <= y < top needs bounds checking. */ for (y = 0; y < GBH; y++) { int bit = jbig2_arith_decode(as, &GB_stats[0x9B25]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON1"); LTP ^= bit; if (!LTP) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint8_t *ppline = &image->data[image->stride * (y-2)]; uint32_t pd = 0; uint32_t ppd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; if (y > 1) { ppd = (*ppline++ << 8); if (GBW > 8) ppd |= *ppline++; } } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x000F; /* First 4 pixels */ CONTEXT |= (pd>>8) & 0x03E0; /* Skip one, next 5 pixels */ CONTEXT |= (ppd>>2) & 0x7000; /* Skip 2, next 3 pixels, skip one */ if (y >= top && x >= left && x < right) { CONTEXT |= jbig2_image_get_pixel_fast(image, x + params->gbat[0], y + params->gbat[1]) << 4; CONTEXT |= jbig2_image_get_pixel_fast(image, x + params->gbat[2], y + params->gbat[3]) << 10; CONTEXT |= jbig2_image_get_pixel_fast(image, x + params->gbat[4], y + params->gbat[5]) << 11; CONTEXT |= jbig2_image_get_pixel_fast(image, x + params->gbat[6], y + params->gbat[7]) << 15; } else { CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[2], y + params->gbat[3]) << 10; CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[4], y + params->gbat[5]) << 11; CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[6], y + params->gbat[7]) << 15; } bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON2"); } pd = pd<<1; ppd = ppd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = (uint8_t)out_byte< 0) { pd |= *pline++; if (y > 1) ppd |= *ppline++; } } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int LTP = 0; if (pixel_outside_field(params->gbat[0], params->gbat[1])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GBH; y++) { int bit = jbig2_arith_decode(as, &GB_stats[0x0795]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 TPGDON1"); LTP ^= bit; if (!LTP) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint8_t *ppline = &image->data[image->stride * (y-2)]; uint32_t pd = 0; uint32_t ppd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; if (y > 1) { ppd = (*ppline++ << 8); if (GBW > 8) ppd |= *ppline++; } } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x0007; /* First 3 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 3; CONTEXT |= (pd>>9) & 0x01F0; /* next 5 pixels */ CONTEXT |= (ppd>>4) & 0x1E00; /* next 4 pixels */ bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 TPGDON2"); } pd = pd<<1; ppd = ppd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = (uint8_t)out_byte< 0) { pd |= *pline++; if (y > 1) ppd |= *ppline++; } } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int LTP = 0; if (pixel_outside_field(params->gbat[0], params->gbat[1])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GBH; y++) { int bit = jbig2_arith_decode(as, &GB_stats[0xE5]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 TPGDON1"); LTP ^= bit; if (!LTP) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint8_t *ppline = &image->data[image->stride * (y-2)]; uint32_t pd = 0; uint32_t ppd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; if (y > 1) { ppd = (*ppline++ << 8); if (GBW > 8) ppd |= *ppline++; } } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x003; /* First 2 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 2; CONTEXT |= (pd>>11) & 0x078; /* next 4 pixels */ CONTEXT |= (ppd>>7) & 0x380; /* next 3 pixels */ bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 TPGDON2"); } pd = pd<<1; ppd = ppd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = (uint8_t)out_byte< 0) { pd |= *pline++; if (y > 1) ppd |= *ppline++; } } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<width; const uint32_t GBH = image->height; uint32_t CONTEXT; uint32_t x, y; int LTP = 0; if (pixel_outside_field(params->gbat[0], params->gbat[1])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GBH; y++) { int bit = jbig2_arith_decode(as, &GB_stats[0x0195]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 TPGDON1"); LTP ^= bit; if (!LTP) { uint32_t out_byte = 0; int out_bits_to_go_in_byte = 8; uint8_t *d = &image->data[image->stride * y]; uint8_t *pline = &image->data[image->stride * (y-1)]; uint32_t pd = 0; if (y > 0) { pd = (*pline++ << 8); if (GBW > 8) pd |= *pline++; } for (x = 0; x < GBW; x++) { if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { bit = 0; } else { CONTEXT = out_byte & 0x0F; /* First 4 pixels */ CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; CONTEXT |= (pd>>9) & 0x3E0; /* next 5 pixels */ bit = jbig2_arith_decode(as, &GB_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 TPGDON2"); } pd = pd<<1; out_byte = (out_byte<<1) | bit; out_bits_to_go_in_byte--; *d = (uint8_t)out_byte< 0) pd |= *pline++; } } if (out_bits_to_go_in_byte != 8) *d = (uint8_t)out_byte<GBTEMPLATE) { case 0: return jbig2_decode_generic_template0_TPGDON(ctx, segment, params, as, image, GB_stats); case 1: return jbig2_decode_generic_template1_TPGDON(ctx, segment, params, as, image, GB_stats); case 2: return jbig2_decode_generic_template2_TPGDON(ctx, segment, params, as, image, GB_stats); case 3: return jbig2_decode_generic_template3_TPGDON(ctx, segment, params, as, image, GB_stats); } return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unsupported GBTEMPLATE (%d)", params->GBTEMPLATE); } /** * jbig2_decode_generic_region: Decode a generic region. * @ctx: The context for allocation and error reporting. * @segment: A segment reference for error reporting. * @params: Decoding parameter set. * @as: Arithmetic decoder state. * @image: Where to store the decoded data. * @GB_stats: Arithmetic stats. * * Decodes a generic region, according to section 6.2. The caller should * pass an already allocated Jbig2Image object for @image * * Because this API is based on an arithmetic decoding state, it is * not suitable for MMR decoding. * * Return code: 0 on success. **/ int jbig2_decode_generic_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) { const int8_t *gbat = params->gbat; if (image->stride * image->height > (1 << 26) && segment->data_length < image->stride * image->height / (1 << 16)) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "region is far larger than data provided (%li << %d), aborting to prevent DOS", (long) segment->data_length, image->stride * image->height); } if (!params->MMR && params->TPGDON) return jbig2_decode_generic_region_TPGDON(ctx, segment, params, as, image, GB_stats); if (!params->MMR && params->GBTEMPLATE == 0) { if (!params->USESKIP && gbat[0] == +3 && gbat[1] == -1 && gbat[2] == -3 && gbat[3] == -1 && gbat[4] == +2 && gbat[5] == -2 && gbat[6] == -2 && gbat[7] == -2) return jbig2_decode_generic_template0(ctx, segment, params, as, image, GB_stats); else return jbig2_decode_generic_template0_unopt(ctx, segment, params, as, image, GB_stats); } else if (!params->MMR && params->GBTEMPLATE == 1) { if (!params->USESKIP && gbat[0] == +3 && gbat[1] == -1) return jbig2_decode_generic_template1(ctx, segment, params, as, image, GB_stats); else return jbig2_decode_generic_template1_unopt(ctx, segment, params, as, image, GB_stats); } else if (!params->MMR && params->GBTEMPLATE == 2) { if (!params->USESKIP && gbat[0] == 2 && gbat[1] == -1) return jbig2_decode_generic_template2(ctx, segment, params, as, image, GB_stats); else return jbig2_decode_generic_template2_unopt(ctx, segment, params, as, image, GB_stats); } else if (!params->MMR && params->GBTEMPLATE == 3) { if (!params->USESKIP && gbat[0] == 2 && gbat[1] == -1) return jbig2_decode_generic_template3(ctx, segment, params, as, image, GB_stats); else return jbig2_decode_generic_template3_unopt(ctx, segment, params, as, image, GB_stats); } { int i; for (i = 0; i < 8; i++) jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "gbat[%d] = %d", i, params->gbat[i]); } return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unsupported generic region (MMR=%d, GBTEMPLATE=%d)", params->MMR, params->GBTEMPLATE); } /** * Handler for immediate generic region segments */ int jbig2_immediate_generic_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { Jbig2RegionSegmentInfo rsi; byte seg_flags; int8_t gbat[8]; int offset; uint32_t gbat_bytes = 0; Jbig2GenericRegionParams params; int code = 0; Jbig2Image *image = NULL; Jbig2WordStream *ws = NULL; Jbig2ArithState *as = NULL; Jbig2ArithCx *GB_stats = NULL; uint32_t height; Jbig2Page *page = &ctx->pages[ctx->current_page]; /* 7.4.6 */ if (segment->data_length < 18) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); jbig2_get_region_segment_info(&rsi, segment_data); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "generic region: %u x %u @ (%u, %u), flags = %02x", rsi.width, rsi.height, rsi.x, rsi.y, rsi.flags); /* 7.4.6.4 */ height = rsi.height; if (segment->rows != UINT32_MAX) { if (segment->rows > rsi.height) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment contains more rows than stated in header"); height = segment->rows; } /* 7.4.6.2 */ seg_flags = segment_data[17]; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "segment flags = %02x", seg_flags); if ((seg_flags & 1) && (seg_flags & 6)) jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "MMR is 1, but GBTEMPLATE is not 0"); /* 7.4.6.3 */ if (!(seg_flags & 1)) { gbat_bytes = (seg_flags & 6) ? 2 : 8; if (18 + gbat_bytes > segment->data_length) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); memcpy(gbat, segment_data + 18, gbat_bytes); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "gbat: %d, %d", gbat[0], gbat[1]); } offset = 18 + gbat_bytes; /* Check for T.88 amendment 2 */ if ((seg_flags >> 5) & 1) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment uses 12 adaptive template pixels (NYI)"); /* Table 34 */ params.MMR = seg_flags & 1; params.GBTEMPLATE = (seg_flags & 6) >> 1; params.TPGDON = (seg_flags & 8) >> 3; params.USESKIP = 0; memcpy(params.gbat, gbat, gbat_bytes); if (page->height == 0xffffffff && page->striped && page->stripe_size > 0) { if (rsi.y >= page->end_row + page->stripe_size) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "ignoring %u x %u region at (%u, %u) outside of stripe at row %u covering %u rows, on page of height %u", rsi.width, rsi.height, rsi.x, rsi.y, page->end_row, page->stripe_size, page->image->height); return 0; } if (height > page->end_row + page->stripe_size) { height = page->end_row + page->stripe_size; } } else { if (rsi.y >= page->height) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "ignoring %u x %u region at (%u, %u) outside of page of height %u", rsi.width, rsi.height, rsi.x, rsi.y, page->height); return 0; } if (height > page->height - rsi .y) { height = page->height - rsi.y; } } if (height == 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "nothing remains of region, ignoring"); return 0; } image = jbig2_image_new(ctx, rsi.width, height); if (image == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate generic image"); jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "allocated %d x %d image buffer for region decode results", rsi.width, height); if (params.MMR) { code = jbig2_decode_generic_mmr(ctx, segment, ¶ms, segment_data + offset, segment->data_length - offset, image); if (code < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode MMR-coded generic region"); goto cleanup; } } else { int stats_size = jbig2_generic_stats_size(ctx, params.GBTEMPLATE); GB_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GB_stats == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate arithmetic decoder states when handling immediate generic region"); goto cleanup; } memset(GB_stats, 0, stats_size); ws = jbig2_word_stream_buf_new(ctx, segment_data + offset, segment->data_length - offset); if (ws == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocated word stream when handling immediate generic region"); goto cleanup; } as = jbig2_arith_new(ctx, ws); if (as == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding state when handling immediate generic region"); goto cleanup; } code = jbig2_decode_generic_region(ctx, segment, ¶ms, as, image, GB_stats); if (code < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode immediate generic region"); goto cleanup; } } code = jbig2_page_add_result(ctx, &ctx->pages[ctx->current_page], image, rsi.x, rsi.y, rsi.op); if (code < 0) code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to add result to page"); cleanup: jbig2_free(ctx->allocator, as); jbig2_word_stream_buf_free(ctx, ws); jbig2_free(ctx->allocator, GB_stats); jbig2_image_release(ctx, image); return code; } jbig2dec-0.18/jbig2_generic.h000066400000000000000000000025651362055302000157610ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_GENERIC_H #define _JBIG2_GENERIC_H /** * Headers for Generic and Generic Refinement region handling **/ /* 6.4 Table 2 */ typedef struct { bool MMR; /* GBW */ /* GBH */ int GBTEMPLATE; bool TPGDON; bool USESKIP; Jbig2Image *SKIP; int8_t gbat[8]; } Jbig2GenericRegionParams; /* return the appropriate context size for the given template */ int jbig2_generic_stats_size(Jbig2Ctx *ctx, int template); int jbig2_decode_generic_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats); /* 7.4 */ int jbig2_immediate_generic_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data); #endif /* _JBIG2_GENERIC_H */ jbig2dec-0.18/jbig2_halftone.c000066400000000000000000000557471362055302000161520ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* JBIG2 Pattern Dictionary and Halftone Region decoding */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include /* memset() */ #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_generic.h" #include "jbig2_image.h" #include "jbig2_halftone.h" #include "jbig2_mmr.h" #include "jbig2_page.h" #include "jbig2_segment.h" /** * jbig2_hd_new: create a new dictionary from a collective bitmap */ static Jbig2PatternDict * jbig2_hd_new(Jbig2Ctx *ctx, const Jbig2PatternDictParams *params, Jbig2Image *image) { Jbig2PatternDict *new; const uint32_t N = params->GRAYMAX + 1; const uint32_t HPW = params->HDPW; const uint32_t HPH = params->HDPH; int code; uint32_t i; int j; if (N == 0) { /* We've wrapped. */ jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "params->GRAYMAX out of range"); return NULL; } /* allocate a new struct */ new = jbig2_new(ctx, Jbig2PatternDict, 1); if (new != NULL) { new->patterns = jbig2_new(ctx, Jbig2Image *, N); if (new->patterns == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate pattern in collective bitmap dictionary"); jbig2_free(ctx->allocator, new); return NULL; } new->n_patterns = N; new->HPW = HPW; new->HPH = HPH; /* 6.7.5(4) - copy out the individual pattern images */ for (i = 0; i < N; i++) { new->patterns[i] = jbig2_image_new(ctx, HPW, HPH); if (new->patterns[i] == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to allocate pattern element image"); for (j = 0; j < i; j++) jbig2_free(ctx->allocator, new->patterns[j]); jbig2_free(ctx->allocator, new); return NULL; } /* compose with the REPLACE operator; the source will be clipped to the destination, selecting the proper sub image */ code = jbig2_image_compose(ctx, new->patterns[i], image, -i * (int32_t) HPW, 0, JBIG2_COMPOSE_REPLACE); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to compose image into collective bitmap dictionary"); for (j = 0; j < i; j++) jbig2_free(ctx->allocator, new->patterns[j]); jbig2_free(ctx->allocator, new); return NULL; } } } else { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate collective bitmap dictionary"); } return new; } /** * jbig2_hd_release: release a pattern dictionary */ void jbig2_hd_release(Jbig2Ctx *ctx, Jbig2PatternDict *dict) { int i; if (dict == NULL) return; if (dict->patterns != NULL) for (i = 0; i < dict->n_patterns; i++) jbig2_image_release(ctx, dict->patterns[i]); jbig2_free(ctx->allocator, dict->patterns); jbig2_free(ctx->allocator, dict); } /** * jbig2_decode_pattern_dict: decode pattern dictionary data * * @ctx: jbig2 decoder context * @segment: jbig2 segment (header) structure * @params: parameters from the pattern dictionary header * @data: pointer to text region data to be decoded * @size: length of text region data * @GB_stats: arithmetic coding context to use * * Implements the pattern dictionary decoding procedure * described in section 6.7 of the JBIG2 spec. * * returns: a pointer to the resulting dictionary on success * returns: 0 on failure **/ static Jbig2PatternDict * jbig2_decode_pattern_dict(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2PatternDictParams *params, const byte *data, const size_t size, Jbig2ArithCx *GB_stats) { Jbig2PatternDict *hd = NULL; Jbig2Image *image = NULL; Jbig2GenericRegionParams rparams; int code = 0; /* allocate the collective image */ image = jbig2_image_new(ctx, params->HDPW * (params->GRAYMAX + 1), params->HDPH); if (image == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate collective bitmap for halftone dictionary"); return NULL; } /* fill out the generic region decoder parameters */ rparams.MMR = params->HDMMR; rparams.GBTEMPLATE = params->HDTEMPLATE; rparams.TPGDON = 0; /* not used if HDMMR = 1 */ rparams.USESKIP = 0; rparams.gbat[0] = -(int8_t) params->HDPW; rparams.gbat[1] = 0; rparams.gbat[2] = -3; rparams.gbat[3] = -1; rparams.gbat[4] = 2; rparams.gbat[5] = -2; rparams.gbat[6] = -2; rparams.gbat[7] = -2; if (params->HDMMR) { code = jbig2_decode_generic_mmr(ctx, segment, &rparams, data, size, image); } else { Jbig2WordStream *ws = jbig2_word_stream_buf_new(ctx, data, size); if (ws != NULL) { Jbig2ArithState *as = jbig2_arith_new(ctx, ws); if (as != NULL) { code = jbig2_decode_generic_region(ctx, segment, &rparams, as, image, GB_stats); } else { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding state when handling halftone dictionary"); } jbig2_free(ctx->allocator, as); jbig2_word_stream_buf_free(ctx, ws); } else { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate word stream when handling halftone dictionary"); } } if (code == 0) hd = jbig2_hd_new(ctx, params, image); else jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode immediate generic region"); jbig2_image_release(ctx, image); return hd; } /* 7.4.4 */ int jbig2_pattern_dictionary(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { Jbig2PatternDictParams params; Jbig2ArithCx *GB_stats = NULL; byte flags; int offset = 0; /* 7.4.4.1 - Data header */ if (segment->data_length < 7) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); } flags = segment_data[0]; params.HDMMR = flags & 1; params.HDTEMPLATE = (flags & 6) >> 1; params.HDPW = segment_data[1]; params.HDPH = segment_data[2]; params.GRAYMAX = jbig2_get_uint32(segment_data + 3); offset += 7; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "pattern dictionary, flags=%02x, %d grays (%dx%d cell)", flags, params.GRAYMAX + 1, params.HDPW, params.HDPH); if (params.HDMMR && params.HDTEMPLATE) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "HDTEMPLATE is %d when HDMMR is %d, contrary to spec", params.HDTEMPLATE, params.HDMMR); } if (flags & 0xf8) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "reserved flag bits non-zero"); } /* 7.4.4.2 */ if (!params.HDMMR) { /* allocate and zero arithmetic coding stats */ int stats_size = jbig2_generic_stats_size(ctx, params.HDTEMPLATE); GB_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GB_stats == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding state when handling pattern dictionary"); memset(GB_stats, 0, stats_size); } segment->result = jbig2_decode_pattern_dict(ctx, segment, ¶ms, segment_data + offset, segment->data_length - offset, GB_stats); /* todo: retain GB_stats? */ if (!params.HDMMR) { jbig2_free(ctx->allocator, GB_stats); } return (segment->result != NULL) ? 0 : -1; } /** * jbig2_decode_gray_scale_image: decode gray-scale image * * @ctx: jbig2 decoder context * @segment: jbig2 segment (header) structure * @data: pointer to text region data to be decoded * @size: length of text region data * @GSMMR: if MMR is used * @GSW: width of gray-scale image * @GSH: height of gray-scale image * @GSBPP: number of bitplanes/Jbig2Images to use * @GSKIP: mask indicating which values should be skipped * @GSTEMPLATE: template used to code the gray-scale bitplanes * @GB_stats: arithmetic coding context to use * * Implements the decoding a gray-scale image described in * annex C.5. This is part of the halftone region decoding. * * returns: array of gray-scale values with GSW x GSH width/height * 0 on failure **/ static uint16_t ** jbig2_decode_gray_scale_image(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *data, const size_t size, bool GSMMR, uint32_t GSW, uint32_t GSH, uint32_t GSBPP, bool GSUSESKIP, Jbig2Image *GSKIP, int GSTEMPLATE, Jbig2ArithCx *GB_stats) { uint16_t **GSVALS = NULL; size_t consumed_bytes = 0; uint32_t i, j, stride, x, y; int code; Jbig2Image **GSPLANES; Jbig2GenericRegionParams rparams; Jbig2WordStream *ws = NULL; Jbig2ArithState *as = NULL; /* allocate GSPLANES */ GSPLANES = jbig2_new(ctx, Jbig2Image *, GSBPP); if (GSPLANES == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate %d bytes for GSPLANES", GSBPP); return NULL; } for (i = 0; i < GSBPP; ++i) { GSPLANES[i] = jbig2_image_new(ctx, GSW, GSH); if (GSPLANES[i] == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate %dx%d image for GSPLANES", GSW, GSH); /* free already allocated */ for (j = i; j > 0;) jbig2_image_release(ctx, GSPLANES[--j]); jbig2_free(ctx->allocator, GSPLANES); return NULL; } } /* C.5 step 1. Decode GSPLANES[GSBPP-1] */ /* fill generic region decoder parameters */ rparams.MMR = GSMMR; rparams.GBTEMPLATE = GSTEMPLATE; rparams.TPGDON = 0; rparams.USESKIP = GSUSESKIP; rparams.SKIP = GSKIP; rparams.gbat[0] = (GSTEMPLATE <= 1 ? 3 : 2); rparams.gbat[1] = -1; rparams.gbat[2] = -3; rparams.gbat[3] = -1; rparams.gbat[4] = 2; rparams.gbat[5] = -2; rparams.gbat[6] = -2; rparams.gbat[7] = -2; if (GSMMR) { code = jbig2_decode_halftone_mmr(ctx, &rparams, data, size, GSPLANES[GSBPP - 1], &consumed_bytes); } else { ws = jbig2_word_stream_buf_new(ctx, data, size); if (ws == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate word stream when decoding gray scale image"); goto cleanup; } as = jbig2_arith_new(ctx, ws); if (as == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding state when decoding gray scale image"); goto cleanup; } code = jbig2_decode_generic_region(ctx, segment, &rparams, as, GSPLANES[GSBPP - 1], GB_stats); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "error decoding GSPLANES for halftone image"); goto cleanup; } /* C.5 step 2. Set j = GSBPP-2 */ j = GSBPP - 1; /* C.5 step 3. decode loop */ while (j > 0) { j--; /* C.5 step 3. (a) */ if (GSMMR) { code = jbig2_decode_halftone_mmr(ctx, &rparams, data + consumed_bytes, size - consumed_bytes, GSPLANES[j], &consumed_bytes); } else { code = jbig2_decode_generic_region(ctx, segment, &rparams, as, GSPLANES[j], GB_stats); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode GSPLANES for halftone image"); goto cleanup; } /* C.5 step 3. (b): * for each [x,y] * GSPLANES[j][x][y] = GSPLANES[j+1][x][y] XOR GSPLANES[j][x][y] */ stride = GSPLANES[j]->stride; for (i = 0; i < stride * GSH; ++i) GSPLANES[j]->data[i] ^= GSPLANES[j + 1]->data[i]; /* C.5 step 3. (c) */ } /* allocate GSVALS */ GSVALS = jbig2_new(ctx, uint16_t *, GSW); if (GSVALS == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate GSVALS: %d bytes", GSW); goto cleanup; } for (i = 0; i < GSW; ++i) { GSVALS[i] = jbig2_new(ctx, uint16_t, GSH); if (GSVALS[i] == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate GSVALS: %d bytes", GSH * GSW); /* free already allocated */ for (j = i; j > 0;) jbig2_free(ctx->allocator, GSVALS[--j]); jbig2_free(ctx->allocator, GSVALS); GSVALS = NULL; goto cleanup; } } /* C.5 step 4. */ for (x = 0; x < GSW; ++x) { for (y = 0; y < GSH; ++y) { GSVALS[x][y] = 0; for (j = 0; j < GSBPP; ++j) GSVALS[x][y] += jbig2_image_get_pixel(GSPLANES[j], x, y) << j; } } cleanup: /* free memory */ if (!GSMMR) { jbig2_free(ctx->allocator, as); jbig2_word_stream_buf_free(ctx, ws); } for (i = 0; i < GSBPP; ++i) jbig2_image_release(ctx, GSPLANES[i]); jbig2_free(ctx->allocator, GSPLANES); return GSVALS; } /** * jbig2_decode_ht_region_get_hpats: get pattern dictionary * * @ctx: jbig2 decoder context * @segment: jbig2 halftone region segment * * Returns the first referred pattern dictionary of segment * * returns: pattern dictionary * 0 if search failed **/ static Jbig2PatternDict * jbig2_decode_ht_region_get_hpats(Jbig2Ctx *ctx, Jbig2Segment *segment) { int index = 0; Jbig2PatternDict *pattern_dict = NULL; Jbig2Segment *rsegment = NULL; /* loop through all referred segments */ while (!pattern_dict && segment->referred_to_segment_count > index) { rsegment = jbig2_find_segment(ctx, segment->referred_to_segments[index]); if (rsegment) { /* segment type is pattern dictionary and result is not empty */ if ((rsegment->flags & 0x3f) == 16 && rsegment->result) { pattern_dict = (Jbig2PatternDict *) rsegment->result; return pattern_dict; } } index++; } return pattern_dict; } /** * jbig2_decode_halftone_region: decode a halftone region * * @ctx: jbig2 decoder context * @segment: jbig2 halftone region segment * @params: parameters * @data: pointer to halftone region data to be decoded * @size: length of halftone region data * @GB_stats: arithmetic coding context to use * * Implements the halftone region decoding procedure * described in section 6.6.5 of the JBIG2 spec. * * returns: 0 on success * <0 on failure **/ static int jbig2_decode_halftone_region(Jbig2Ctx *ctx, Jbig2Segment *segment, Jbig2HalftoneRegionParams *params, const byte *data, const size_t size, Jbig2Image *image, Jbig2ArithCx *GB_stats) { uint32_t HBPP; uint32_t HNUMPATS; uint16_t **GI = NULL; Jbig2Image *HSKIP = NULL; Jbig2PatternDict *HPATS; uint32_t i; int32_t mg, ng; int32_t x, y; uint16_t gray_val; int code = 0; /* We need the patterns used in this region, get them from the referred pattern dictionary */ HPATS = jbig2_decode_ht_region_get_hpats(ctx, segment); if (!HPATS) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "no pattern dictionary found, skipping halftone image"); goto cleanup; } /* 6.6.5 point 1. Fill bitmap with HDEFPIXEL */ memset(image->data, params->HDEFPIXEL, image->stride * image->height); /* 6.6.5 point 2. compute HSKIP according to 6.6.5.1 */ if (params->HENABLESKIP == 1) { HSKIP = jbig2_image_new(ctx, params->HGW, params->HGH); if (HSKIP == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate skip image"); for (mg = 0; mg < params->HGH; ++mg) { for (ng = 0; ng < params->HGW; ++ng) { x = (params->HGX + mg * params->HRY + ng * params->HRX) >> 8; y = (params->HGY + mg * params->HRX - ng * params->HRY) >> 8; if (x + HPATS->HPW <= 0 || x >= (int32_t) image->width || y + HPATS->HPH <= 0 || y >= (int32_t) image->height) { jbig2_image_set_pixel(HSKIP, ng, mg, 1); } else { jbig2_image_set_pixel(HSKIP, ng, mg, 0); } } } } /* 6.6.5 point 3. set HBPP to ceil(log2(HNUMPATS)): */ HNUMPATS = HPATS->n_patterns; HBPP = 0; while (HNUMPATS > (1U << ++HBPP)); if (HBPP > 16) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "HBPP is larger than supported (%u)", HBPP); goto cleanup; } /* 6.6.5 point 4. decode gray-scale image as mentioned in annex C */ GI = jbig2_decode_gray_scale_image(ctx, segment, data, size, params->HMMR, params->HGW, params->HGH, HBPP, params->HENABLESKIP, HSKIP, params->HTEMPLATE, GB_stats); if (!GI) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to acquire gray-scale image, skipping halftone image"); goto cleanup; } /* 6.6.5 point 5. place patterns with procedure mentioned in 6.6.5.2 */ for (mg = 0; mg < params->HGH; ++mg) { for (ng = 0; ng < params->HGW; ++ng) { x = (params->HGX + mg * (int32_t) params->HRY + ng * (int32_t) params->HRX) >> 8; y = (params->HGY + mg * (int32_t) params->HRX - ng * (int32_t) params->HRY) >> 8; /* prevent pattern index >= HNUMPATS */ gray_val = GI[ng][mg]; if (gray_val >= HNUMPATS) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "gray-scale index %d out of range, using largest index", gray_val); /* use highest available pattern */ gray_val = HNUMPATS - 1; } code = jbig2_image_compose(ctx, image, HPATS->patterns[gray_val], x, y, params->HCOMBOP); if (code < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to compose pattern with gray-scale image"); goto cleanup; } } } cleanup: if (GI) { for (i = 0; i < params->HGW; ++i) { jbig2_free(ctx->allocator, GI[i]); } } jbig2_free(ctx->allocator, GI); jbig2_image_release(ctx, HSKIP); return code; } /** * jbig2_halftone_region: read a halftone region segment header **/ int jbig2_halftone_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { int offset = 0; Jbig2RegionSegmentInfo region_info; Jbig2HalftoneRegionParams params; Jbig2Image *image = NULL; Jbig2ArithCx *GB_stats = NULL; int code = 0; /* 7.4.5.1 */ if (segment->data_length < 17) goto too_short; jbig2_get_region_segment_info(®ion_info, segment_data); offset += 17; if (segment->data_length < 18) goto too_short; /* 7.4.5.1.1 Figure 42 */ params.flags = segment_data[offset]; params.HMMR = params.flags & 1; params.HTEMPLATE = (params.flags & 6) >> 1; params.HENABLESKIP = (params.flags & 8) >> 3; params.HCOMBOP = (Jbig2ComposeOp)((params.flags & 0x70) >> 4); params.HDEFPIXEL = (params.flags & 0x80) >> 7; offset += 1; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "halftone region: %u x %u @ (%u, %u), flags = %02x", region_info.width, region_info.height, region_info.x, region_info.y, params.flags); if (params.HMMR && params.HTEMPLATE) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "HTEMPLATE is %d when HMMR is %d, contrary to spec", params.HTEMPLATE, params.HMMR); } if (params.HMMR && params.HENABLESKIP) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "HENABLESKIP is %d when HMMR is %d, contrary to spec", params.HENABLESKIP, params.HMMR); } /* 7.4.5.1.2 Figure 43 */ if (segment->data_length - offset < 16) goto too_short; params.HGW = jbig2_get_uint32(segment_data + offset); params.HGH = jbig2_get_uint32(segment_data + offset + 4); params.HGX = jbig2_get_int32(segment_data + offset + 8); params.HGY = jbig2_get_int32(segment_data + offset + 12); offset += 16; /* 7.4.5.1.3 Figure 44 */ if (segment->data_length - offset < 4) goto too_short; params.HRX = jbig2_get_uint16(segment_data + offset); params.HRY = jbig2_get_uint16(segment_data + offset + 2); offset += 4; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "grid %d x %d @ (%d.%d,%d.%d) vector (%d.%d,%d.%d)", params.HGW, params.HGH, params.HGX >> 8, params.HGX & 0xff, params.HGY >> 8, params.HGY & 0xff, params.HRX >> 8, params.HRX & 0xff, params.HRY >> 8, params.HRY & 0xff); /* 7.4.5.2 */ if (!params.HMMR) { /* allocate and zero arithmetic coding stats */ int stats_size = jbig2_generic_stats_size(ctx, params.HTEMPLATE); GB_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GB_stats == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate arithmetic decoder states in halftone region"); } memset(GB_stats, 0, stats_size); } image = jbig2_image_new(ctx, region_info.width, region_info.height); if (image == NULL) { jbig2_free(ctx->allocator, GB_stats); return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate halftone image"); } code = jbig2_decode_halftone_region(ctx, segment, ¶ms, segment_data + offset, segment->data_length - offset, image, GB_stats); if (code < 0) { jbig2_image_release(ctx, image); jbig2_free(ctx->allocator, GB_stats); return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode halftone region"); } /* todo: retain GB_stats? */ if (!params.HMMR) { jbig2_free(ctx->allocator, GB_stats); } code = jbig2_page_add_result(ctx, &ctx->pages[ctx->current_page], image, region_info.x, region_info.y, region_info.op); if (code < 0) { jbig2_image_release(ctx, image); return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to add halftone region to page"); } jbig2_image_release(ctx, image); return code; too_short: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); } jbig2dec-0.18/jbig2_halftone.h000066400000000000000000000027051362055302000161410ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_HALFTONE_H #define _JBIG2_HALFTONE_H typedef struct { int n_patterns; Jbig2Image **patterns; int HPW, HPH; } Jbig2PatternDict; /* Table 24 */ typedef struct { bool HDMMR; uint32_t HDPW; uint32_t HDPH; uint32_t GRAYMAX; int HDTEMPLATE; } Jbig2PatternDictParams; /* Table 33 */ typedef struct { byte flags; uint32_t HGW; uint32_t HGH; int32_t HGX; int32_t HGY; uint16_t HRX; uint16_t HRY; bool HMMR; int HTEMPLATE; bool HENABLESKIP; Jbig2ComposeOp HCOMBOP; bool HDEFPIXEL; } Jbig2HalftoneRegionParams; void jbig2_hd_release(Jbig2Ctx *ctx, Jbig2PatternDict *dict); int jbig2_halftone_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data); int jbig2_pattern_dictionary(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data); #endif /* _JBIG2_HALFTONE_H */ jbig2dec-0.18/jbig2_huffman.c000066400000000000000000002317621362055302000157670ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* Huffman table decoding procedures -- See Annex B of the JBIG2 specification */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include #ifdef JBIG2_DEBUG #include #endif #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_huffman.h" #include "jbig2_hufftab.h" #include "jbig2_image.h" #include "jbig2_segment.h" #define JBIG2_HUFFMAN_FLAGS_ISOOB 1 #define JBIG2_HUFFMAN_FLAGS_ISLOW 2 #define JBIG2_HUFFMAN_FLAGS_ISEXT 4 struct _Jbig2HuffmanState { /* The current bit offset is equal to (offset * 8) + offset_bits. The MSB of this_word is the current bit offset. The MSB of next_word is (offset + 4) * 8. */ uint32_t this_word; uint32_t next_word; uint32_t offset_bits; uint32_t offset; uint32_t offset_limit; Jbig2WordStream *ws; Jbig2Ctx *ctx; }; #define huff_get_next_word(hs, offset, word) \ (hs)->ws->get_next_word((hs)->ws, (offset), (word)) /** Allocate and initialize a new huffman coding state * the returned pointer can simply be freed; this does * not affect the associated Jbig2WordStream. */ Jbig2HuffmanState * jbig2_huffman_new(Jbig2Ctx *ctx, Jbig2WordStream *ws) { Jbig2HuffmanState *result = NULL; int code; result = jbig2_new(ctx, Jbig2HuffmanState, 1); if (result != NULL) { result->offset = 0; result->offset_bits = 0; result->offset_limit = 0; result->ws = ws; result->ctx = ctx; code = huff_get_next_word(result, 0, &result->this_word); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed read first huffman word"); jbig2_huffman_free(ctx, result); return NULL; } code = huff_get_next_word(result, 4, &result->next_word); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed read second huffman word"); jbig2_huffman_free(ctx, result); return NULL; } } else { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate new huffman coding state"); return NULL; } return result; } /** Free an allocated huffman coding state. * This just calls jbig2_free() if the pointer is not NULL */ void jbig2_huffman_free(Jbig2Ctx *ctx, Jbig2HuffmanState *hs) { jbig2_free(ctx->allocator, hs); } /** debug routines **/ #ifdef JBIG2_DEBUG /** print current huffman state */ void jbig2_dump_huffman_state(Jbig2HuffmanState *hs) { fprintf(stderr, "huffman state %08x %08x offset %d.%d\n", hs->this_word, hs->next_word, hs->offset, hs->offset_bits); } /** print the binary string we're reading from */ void jbig2_dump_huffman_binary(Jbig2HuffmanState *hs) { const uint32_t word = hs->this_word; int i; fprintf(stderr, "huffman binary "); for (i = 31; i >= 0; i--) fprintf(stderr, ((word >> i) & 1) ? "1" : "0"); fprintf(stderr, "\n"); } /** print huffman table */ void jbig2_dump_huffman_table(const Jbig2HuffmanTable *table) { int i; int table_size = (1 << table->log_table_size); fprintf(stderr, "huffman table %p (log_table_size=%d, %d entries, entries=%p):\n", table, table->log_table_size, table_size, table->entries); for (i = 0; i < table_size; i++) { fprintf(stderr, "%6d: PREFLEN=%d, RANGELEN=%d, ", i, table->entries[i].PREFLEN, table->entries[i].RANGELEN); if (table->entries[i].flags & JBIG2_HUFFMAN_FLAGS_ISEXT) { fprintf(stderr, "ext=%p", table->entries[i].u.ext_table); } else { fprintf(stderr, "RANGELOW=%d", table->entries[i].u.RANGELOW); } if (table->entries[i].flags) { int need_comma = 0; fprintf(stderr, ", flags=0x%x(", table->entries[i].flags); if (table->entries[i].flags & JBIG2_HUFFMAN_FLAGS_ISOOB) { fprintf(stderr, "OOB"); need_comma = 1; } if (table->entries[i].flags & JBIG2_HUFFMAN_FLAGS_ISLOW) { if (need_comma) fprintf(stderr, ","); fprintf(stderr, "LOW"); need_comma = 1; } if (table->entries[i].flags & JBIG2_HUFFMAN_FLAGS_ISEXT) { if (need_comma) fprintf(stderr, ","); fprintf(stderr, "EXT"); } fprintf(stderr, ")"); } fprintf(stderr, "\n"); } fprintf(stderr, "\n"); } #endif /* JBIG2_DEBUG */ /** Skip bits up to the next byte boundary */ int jbig2_huffman_skip(Jbig2HuffmanState *hs) { int bits = hs->offset_bits & 7; int code; if (bits) { bits = 8 - bits; hs->offset_bits += bits; hs->this_word = (hs->this_word << bits) | (hs->next_word >> (32 - hs->offset_bits)); } if (hs->offset_bits >= 32) { hs->this_word = hs->next_word; hs->offset += 4; code = huff_get_next_word(hs, hs->offset + 4, &hs->next_word); if (code < 0) { return jbig2_error(hs->ctx, JBIG2_SEVERITY_WARNING, -1, "failed to read next huffman word when skipping"); } hs->offset_bits -= 32; if (hs->offset_bits) { hs->this_word = (hs->this_word << hs->offset_bits) | (hs->next_word >> (32 - hs->offset_bits)); } } return 0; } /* skip ahead a specified number of bytes in the word stream */ int jbig2_huffman_advance(Jbig2HuffmanState *hs, size_t advance) { int code; hs->offset += advance & ~3; hs->offset_bits += (advance & 3) << 3; if (hs->offset_bits >= 32) { hs->offset += 4; hs->offset_bits -= 32; } code = huff_get_next_word(hs, hs->offset, &hs->this_word); if (code < 0) { return jbig2_error(hs->ctx, JBIG2_SEVERITY_WARNING, -1, "failed to get first huffman word after advancing"); } code = huff_get_next_word(hs, hs->offset + 4, &hs->next_word); if (code < 0) { return jbig2_error(hs->ctx, JBIG2_SEVERITY_WARNING, -1, "failed to get second huffman word after advancing"); } if (hs->offset_bits > 0) hs->this_word = (hs->this_word << hs->offset_bits) | (hs->next_word >> (32 - hs->offset_bits)); return 0; } /* return the offset of the huffman decode pointer (in bytes) * from the beginning of the WordStream */ uint32_t jbig2_huffman_offset(Jbig2HuffmanState *hs) { return hs->offset + (hs->offset_bits >> 3); } /* read a number of bits directly from the huffman state * without decoding against a table */ int32_t jbig2_huffman_get_bits(Jbig2HuffmanState *hs, const int bits, int *err) { uint32_t this_word = hs->this_word; int32_t result; int code; if (hs->offset_limit && hs->offset >= hs->offset_limit) { *err = -1; return jbig2_error(hs->ctx, JBIG2_SEVERITY_FATAL, -1, "end of jbig2 buffer reached at offset %d", hs->offset); } result = this_word >> (32 - bits); hs->offset_bits += bits; if (hs->offset_bits >= 32) { hs->offset += 4; hs->offset_bits -= 32; hs->this_word = hs->next_word; code = huff_get_next_word(hs, hs->offset + 4, &hs->next_word); if (code < 0) { return jbig2_error(hs->ctx, JBIG2_SEVERITY_WARNING, -1, "failed to get next huffman word"); } if (hs->offset_bits) { hs->this_word = (hs->this_word << hs->offset_bits) | (hs->next_word >> (32 - hs->offset_bits)); } else { hs->this_word = (hs->this_word << hs->offset_bits); } } else { hs->this_word = (this_word << bits) | (hs->next_word >> (32 - hs->offset_bits)); } return result; } int32_t jbig2_huffman_get(Jbig2HuffmanState *hs, const Jbig2HuffmanTable *table, bool *oob) { Jbig2HuffmanEntry *entry; byte flags; int offset_bits = hs->offset_bits; uint32_t this_word = hs->this_word; uint32_t next_word; int RANGELEN; int32_t result; if (hs->offset_limit && hs->offset >= hs->offset_limit) { if (oob) *oob = -1; return jbig2_error(hs->ctx, JBIG2_SEVERITY_FATAL, -1, "end of Jbig2WordStream reached at offset %d", hs->offset); } for (;;) { int log_table_size = table->log_table_size; int PREFLEN; int code; /* SumatraPDF: shifting by the size of the operand is undefined */ entry = &table->entries[log_table_size > 0 ? this_word >> (32 - log_table_size) : 0]; flags = entry->flags; PREFLEN = entry->PREFLEN; if (flags == (byte) -1 || PREFLEN == (byte) -1) { if (oob) *oob = -1; return jbig2_error(hs->ctx, JBIG2_SEVERITY_FATAL, -1, "encountered unpopulated huffman table entry"); } next_word = hs->next_word; offset_bits += PREFLEN; if (offset_bits >= 32) { this_word = next_word; hs->offset += 4; code = huff_get_next_word(hs, hs->offset + 4, &next_word); if (code < 0) { return jbig2_error(hs->ctx, JBIG2_SEVERITY_WARNING, -1, "failed to get next huffman word"); } offset_bits -= 32; hs->next_word = next_word; PREFLEN = offset_bits; } if (PREFLEN) this_word = (this_word << PREFLEN) | (next_word >> (32 - offset_bits)); if (flags & JBIG2_HUFFMAN_FLAGS_ISEXT) { table = entry->u.ext_table; } else break; } result = entry->u.RANGELOW; RANGELEN = entry->RANGELEN; if (RANGELEN > 0) { int32_t HTOFFSET; int code; HTOFFSET = this_word >> (32 - RANGELEN); if (flags & JBIG2_HUFFMAN_FLAGS_ISLOW) result -= HTOFFSET; else result += HTOFFSET; offset_bits += RANGELEN; if (offset_bits >= 32) { this_word = next_word; hs->offset += 4; code = huff_get_next_word(hs, hs->offset + 4, &next_word); if (code < 0) { return jbig2_error(hs->ctx, JBIG2_SEVERITY_WARNING, -1, "failed to get next huffman word"); } offset_bits -= 32; hs->next_word = next_word; RANGELEN = offset_bits; } if (RANGELEN) this_word = (this_word << RANGELEN) | (next_word >> (32 - offset_bits)); } hs->this_word = this_word; hs->offset_bits = offset_bits; if (oob != NULL) *oob = (flags & JBIG2_HUFFMAN_FLAGS_ISOOB); return result; } /* TODO: more than 8 bits here is wasteful of memory. We have support for sub-trees in jbig2_huffman_get() above, but don't use it here. We should, and then revert to 8 bits */ #define LOG_TABLE_SIZE_MAX 16 /** Build an in-memory representation of a Huffman table from the * set of template params provided by the spec or a table segment */ Jbig2HuffmanTable * jbig2_build_huffman_table(Jbig2Ctx *ctx, const Jbig2HuffmanParams *params) { int *LENCOUNT; int LENMAX = -1; const int lencountcount = 256; const Jbig2HuffmanLine *lines = params->lines; int n_lines = params->n_lines; int i, j; uint32_t max_j; int log_table_size = 0; Jbig2HuffmanTable *result; Jbig2HuffmanEntry *entries; int CURLEN; int firstcode = 0; int CURCODE; int CURTEMP; LENCOUNT = jbig2_new(ctx, int, lencountcount); if (LENCOUNT == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate huffman histogram"); return NULL; } memset(LENCOUNT, 0, sizeof(int) * lencountcount); /* B.3, 1. */ for (i = 0; i < params->n_lines; i++) { int PREFLEN = lines[i].PREFLEN; int lts; if (PREFLEN > LENMAX) { for (j = LENMAX + 1; j < PREFLEN + 1; j++) LENCOUNT[j] = 0; LENMAX = PREFLEN; } LENCOUNT[PREFLEN]++; lts = PREFLEN + lines[i].RANGELEN; if (lts > LOG_TABLE_SIZE_MAX) lts = PREFLEN; if (lts <= LOG_TABLE_SIZE_MAX && log_table_size < lts) log_table_size = lts; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "constructing huffman table log size %d", log_table_size); max_j = 1 << log_table_size; result = jbig2_new(ctx, Jbig2HuffmanTable, 1); if (result == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate result"); jbig2_free(ctx->allocator, LENCOUNT); return NULL; } result->log_table_size = log_table_size; entries = jbig2_new(ctx, Jbig2HuffmanEntry, max_j); if (entries == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate result entries"); jbig2_free(ctx->allocator, result); jbig2_free(ctx->allocator, LENCOUNT); return NULL; } /* fill now to catch missing JBIG2Globals later */ memset(entries, 0xFF, sizeof(Jbig2HuffmanEntry) * max_j); result->entries = entries; LENCOUNT[0] = 0; for (CURLEN = 1; CURLEN <= LENMAX; CURLEN++) { int shift = log_table_size - CURLEN; /* B.3 3.(a) */ firstcode = (firstcode + LENCOUNT[CURLEN - 1]) << 1; CURCODE = firstcode; /* B.3 3.(b) */ for (CURTEMP = 0; CURTEMP < n_lines; CURTEMP++) { int PREFLEN = lines[CURTEMP].PREFLEN; if (PREFLEN == CURLEN) { int RANGELEN = lines[CURTEMP].RANGELEN; uint32_t start_j = CURCODE << shift; uint32_t end_j = (CURCODE + 1) << shift; uint32_t cur_j; byte eflags = 0; if (end_j > max_j) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "ran off the end of the entries table! (%d >= %d)", end_j, max_j); jbig2_free(ctx->allocator, result->entries); jbig2_free(ctx->allocator, result); jbig2_free(ctx->allocator, LENCOUNT); return NULL; } /* todo: build extension tables */ if (params->HTOOB && CURTEMP == n_lines - 1) eflags |= JBIG2_HUFFMAN_FLAGS_ISOOB; if (CURTEMP == n_lines - (params->HTOOB ? 3 : 2)) eflags |= JBIG2_HUFFMAN_FLAGS_ISLOW; if (PREFLEN + RANGELEN > LOG_TABLE_SIZE_MAX) { for (cur_j = start_j; cur_j < end_j; cur_j++) { entries[cur_j].u.RANGELOW = lines[CURTEMP].RANGELOW; entries[cur_j].PREFLEN = PREFLEN; entries[cur_j].RANGELEN = RANGELEN; entries[cur_j].flags = eflags; } } else { for (cur_j = start_j; cur_j < end_j; cur_j++) { int32_t HTOFFSET = (cur_j >> (shift - RANGELEN)) & ((1 << RANGELEN) - 1); if (eflags & JBIG2_HUFFMAN_FLAGS_ISLOW) entries[cur_j].u.RANGELOW = lines[CURTEMP].RANGELOW - HTOFFSET; else entries[cur_j].u.RANGELOW = lines[CURTEMP].RANGELOW + HTOFFSET; entries[cur_j].PREFLEN = PREFLEN + RANGELEN; entries[cur_j].RANGELEN = 0; entries[cur_j].flags = eflags; } } CURCODE++; } } } jbig2_free(ctx->allocator, LENCOUNT); return result; } /** Free the memory associated with the representation of table */ void jbig2_release_huffman_table(Jbig2Ctx *ctx, Jbig2HuffmanTable *table) { if (table != NULL) { jbig2_free(ctx->allocator, table->entries); jbig2_free(ctx->allocator, table); } } /* Routines to handle "code table segment (53)" */ /* return 'bitlen' bits from 'bitoffset' of 'data' */ static uint32_t jbig2_table_read_bits(const byte *data, size_t *bitoffset, const int bitlen) { uint32_t result = 0; uint32_t byte_offset = *bitoffset / 8; const int endbit = (*bitoffset & 7) + bitlen; const int n_proc_bytes = (endbit + 7) / 8; const int rshift = n_proc_bytes * 8 - endbit; int i; for (i = n_proc_bytes - 1; i >= 0; i--) { uint32_t d = data[byte_offset++]; const int nshift = i * 8 - rshift; if (nshift > 0) d <<= nshift; else if (nshift < 0) d >>= -nshift; result |= d; } result &= ~(-1 << bitlen); *bitoffset += bitlen; return result; } /* Parse a code table segment, store Jbig2HuffmanParams in segment->result */ int jbig2_table(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { Jbig2HuffmanParams *params = NULL; Jbig2HuffmanLine *line = NULL; segment->result = NULL; if (segment->data_length < 10) goto too_short; { /* B.2 1) (B.2.1) Code table flags */ const int code_table_flags = segment_data[0]; const int HTOOB = code_table_flags & 0x01; /* Bit 0: HTOOB */ /* Bits 1-3: Number of bits used in code table line prefix size fields */ const int HTPS = (code_table_flags >> 1 & 0x07) + 1; /* Bits 4-6: Number of bits used in code table line range size fields */ const int HTRS = (code_table_flags >> 4 & 0x07) + 1; /* B.2 2) (B.2.2) The lower bound of the first table line in the encoded table */ const int32_t HTLOW = jbig2_get_int32(segment_data + 1); /* B.2 3) (B.2.3) One larger than the upper bound of the last normal table line in the encoded table */ const int32_t HTHIGH = jbig2_get_int32(segment_data + 5); /* estimated number of lines in this table, used for allocating memory for lines */ const size_t lines_max = (segment->data_length * 8 - HTPS * (HTOOB ? 3 : 2)) / (HTPS + HTRS) + (HTOOB ? 3 : 2); /* points to a first table line data */ const byte *lines_data = segment_data + 9; const size_t lines_data_bitlen = (segment->data_length - 9) * 8; /* length in bit */ /* bit offset: controls bit reading */ size_t boffset = 0; /* B.2 4) */ int32_t CURRANGELOW = HTLOW; size_t NTEMP = 0; #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "DECODING USER TABLE... Flags: %d, HTOOB: %d, HTPS: %d, HTRS: %d, HTLOW: %d, HTHIGH: %d", code_table_flags, HTOOB, HTPS, HTRS, HTLOW, HTHIGH); #endif if (HTLOW >= HTHIGH) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "invalid Huffman Table range"); goto error_exit; } /* allocate HuffmanParams & HuffmanLine */ params = jbig2_new(ctx, Jbig2HuffmanParams, 1); if (params == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate Huffman Table Parameter"); goto error_exit; } line = jbig2_new(ctx, Jbig2HuffmanLine, lines_max); if (line == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate huffman table lines"); goto error_exit; } /* B.2 5) */ while (CURRANGELOW < HTHIGH) { /* B.2 5) a) */ if (boffset + HTPS >= lines_data_bitlen) goto too_short; line[NTEMP].PREFLEN = jbig2_table_read_bits(lines_data, &boffset, HTPS); /* B.2 5) b) */ if (boffset + HTRS >= lines_data_bitlen) goto too_short; line[NTEMP].RANGELEN = jbig2_table_read_bits(lines_data, &boffset, HTRS); /* B.2 5) c) */ line[NTEMP].RANGELOW = CURRANGELOW; CURRANGELOW += (1 << line[NTEMP].RANGELEN); NTEMP++; } /* B.2 6), B.2 7) lower range table line */ if (boffset + HTPS >= lines_data_bitlen) goto too_short; line[NTEMP].PREFLEN = jbig2_table_read_bits(lines_data, &boffset, HTPS); line[NTEMP].RANGELEN = 32; line[NTEMP].RANGELOW = HTLOW - 1; NTEMP++; /* B.2 8), B.2 9) upper range table line */ if (boffset + HTPS >= lines_data_bitlen) goto too_short; line[NTEMP].PREFLEN = jbig2_table_read_bits(lines_data, &boffset, HTPS); line[NTEMP].RANGELEN = 32; line[NTEMP].RANGELOW = HTHIGH; NTEMP++; /* B.2 10) */ if (HTOOB) { /* B.2 10) a), B.2 10) b) out-of-bound table line */ if (boffset + HTPS >= lines_data_bitlen) goto too_short; line[NTEMP].PREFLEN = jbig2_table_read_bits(lines_data, &boffset, HTPS); line[NTEMP].RANGELEN = 0; line[NTEMP].RANGELOW = 0; NTEMP++; } if (NTEMP != lines_max) { Jbig2HuffmanLine *new_line = jbig2_renew(ctx, line, Jbig2HuffmanLine, NTEMP); if (new_line == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to reallocate huffman table lines"); goto error_exit; } line = new_line; } params->HTOOB = HTOOB; params->n_lines = NTEMP; params->lines = line; segment->result = params; #ifdef JBIG2_DEBUG { int i; for (i = 0; i < NTEMP; i++) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "Line: %d, PREFLEN: %d, RANGELEN: %d, RANGELOW: %d", i, params->lines[i].PREFLEN, params->lines[i].RANGELEN, params->lines[i].RANGELOW); } } #endif } return 0; too_short: jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); error_exit: jbig2_free(ctx->allocator, line); jbig2_free(ctx->allocator, params); return -1; } /* free Jbig2HuffmanParams allocated by jbig2_huffman_table() */ void jbig2_table_free(Jbig2Ctx *ctx, Jbig2HuffmanParams *params) { if (params != NULL) { jbig2_free(ctx->allocator, (void *)params->lines); jbig2_free(ctx->allocator, params); } } /* find a user supplied table used by 'segment' and by 'index' */ const Jbig2HuffmanParams * jbig2_find_table(Jbig2Ctx *ctx, Jbig2Segment *segment, int index) { int i, table_index = 0; for (i = 0; i < segment->referred_to_segment_count; i++) { const Jbig2Segment *const rsegment = jbig2_find_segment(ctx, segment->referred_to_segments[i]); if (rsegment && (rsegment->flags & 63) == 53) { if (table_index == index) return (const Jbig2HuffmanParams *)rsegment->result; ++table_index; } } jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "huffman table not found (%d)", index); return NULL; } #ifdef TEST #include /* cc -g -o jbig2_huffman.test1 -DTEST jbig2_huffman.c .libs/libjbig2dec.a */ /* a test bitstream, and a list of the table indices to use in decoding it. 1 = table B.1 (A), 2 = table B.2 (B), and so on */ /* this test stream should decode to { 8, 5, oob, 8 } */ static const byte test_stream[] = { 0xe9, 0xcb, 0xf4, 0x00 }; static const byte test_tabindex[] = { 4, 2, 2, 1 }; static int test_get_word1(Jbig2WordStream *self, size_t offset, uint32_t *word) { uint32_t val = 0; int ret = 0; if (self == NULL || word == NULL) return -1; if (offset >= sizeof (test_stream)) return 0; if (offset < sizeof(test_stream)) { val |= test_stream[offset] << 24; ret++; } if (offset + 1 < sizeof(test_stream)) { val |= test_stream[offset + 1] << 16; ret++; } if (offset + 2 < sizeof(test_stream)) { val |= test_stream[offset + 2] << 8; ret++; } if (offset + 3 < sizeof(test_stream)) { val |= test_stream[offset + 3]; ret++; } *word = val; return ret; } static int test1() { Jbig2Ctx *ctx; Jbig2HuffmanTable *tables[5]; Jbig2HuffmanState *hs = NULL; Jbig2WordStream ws; bool oob; int32_t code; int i; int success = 0; ctx = jbig2_ctx_new(NULL, 0, NULL, NULL, NULL); if (ctx == NULL) { fprintf(stderr, "Failed to allocate jbig2 context\n"); goto cleanup; } tables[0] = NULL; tables[1] = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A); tables[2] = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_B); tables[3] = NULL; tables[4] = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_D); if (tables[1] == NULL || tables[2] == NULL || tables[4] == NULL) { fprintf(stderr, "Failed to build huffman tables"); goto cleanup; } ws.get_next_word = test_get_word1; hs = jbig2_huffman_new(ctx, &ws); if (hs == NULL) { fprintf(stderr, "Failed to allocate huffman state"); goto cleanup; } printf("testing jbig2 huffman decoding..."); printf("\t(should be 8 5 (oob) 8)\n"); { int i; int sequence_length = sizeof(test_tabindex); for (i = 0; i < sequence_length; i++) { code = jbig2_huffman_get(hs, tables[test_tabindex[i]], &oob); if (oob) printf("(oob) "); else printf("%d ", code); } } printf("\n"); success = 1; cleanup: jbig2_huffman_free(ctx, hs); for (i = 0; i < 5; i++) jbig2_release_huffman_table(ctx, tables[i]); jbig2_ctx_free(ctx); return success; } #include /* a decoding test of each line from each standard table */ /* test code for Table B.1 - Standard Huffman table A */ const int32_t test_output_A[] = { /* line 0, PREFLEN=1, RANGELEN=4, VAL=0..15, 0+VAL */ 0, /* 0 0000 */ 1, /* 0 0001 */ 14, /* 0 1110 */ 15, /* 0 1111 */ /* line 1, PREFLEN=2, RANGELEN=8, VAL=16..271, 10+(VAL-16) */ 16, /* 10 00000000 */ 17, /* 10 00000001 */ 270, /* 10 11111110 */ 271, /* 10 11111111 */ /* line 2, PREFLEN=3, RANGELEN=16, VAL=272..65807, 110+(VAL-272) */ 272, /* 110 00000000 00000000 */ 273, /* 110 00000000 00000001 */ 65806, /* 110 11111111 11111110 */ 65807, /* 110 11111111 11111111 */ /* line 3, PREFLEN=3, RANGELEN=32, VAL=65808..INF, 111+(VAL-65808) */ 65808, /* 111 00000000 00000000 00000000 00000000 */ 65809, /* 111 00000000 00000000 00000000 00000001 */ }; const byte test_input_A[] = { /* 0000 0000 0101 1100 1111 1000 0000 0010 */ 0x00, 0x5c, 0xf8, 0x02, /* 0000 0001 1011 1111 1010 1111 1111 1100 */ 0x01, 0xbf, 0xaf, 0xfc, /* 0000 0000 0000 0001 1000 0000 0000 0000 */ 0x00, 0x01, 0x80, 0x00, /* 0111 0111 1111 1111 1111 0110 1111 1111 */ 0x77, 0xff, 0xf6, 0xff, /* 1111 1111 1110 0000 0000 0000 0000 0000 */ 0xff, 0xe0, 0x00, 0x00, /* 0000 0000 0001 1100 0000 0000 0000 0000 */ 0x00, 0x1c, 0x00, 0x00, /* 0000 0000 0000 01 */ 0x00, 0x04, }; /* test code for Table B.2 - Standard Huffman table B */ const int32_t test_output_B[] = { /* line 0, PREFLEN=1, RANGELEN=0, VAL=0, 0 */ 0, /* 0 */ /* line 1, PREFLEN=2, RANGELEN=0, VAL=1, 10 */ 1, /* 10 */ /* line 2, PREFLEN=3, RANGELEN=0, VAL=2, 110 */ 2, /* 110 */ /* line 3, PREFLEN=4, RANGELEN=3, VAL=3..10, 1110+(VAL-3) */ 3, /* 1110 000 */ 4, /* 1110 001 */ 9, /* 1110 110 */ 10, /* 1110 111 */ /* line 4, PREFLEN=5, RANGELEN=6, VAL=11..74, 11110+(VAL-11) */ 11, /* 11110 000000 */ 12, /* 11110 000001 */ 73, /* 11110 111110 */ 74, /* 11110 111111 */ /* line 5, PREFLEN=6, RANGELEN=32, VAL=75..INF, 111110+(VAL-75) */ 75, /* 111110 00000000 00000000 00000000 00000000 */ 76, /* 111110 00000000 00000000 00000000 00000001 */ /* line 6, PREFLEN=6, VAL=OOB, 111111 */ /*OOB*/ /* 111111 */ }; const byte test_input_B[] = { /* 0101 1011 1000 0111 0001 1110 1101 1101 */ 0x5b, 0x87, 0x1e, 0xdd, /* 1111 1100 0000 0111 1000 0001 1111 0111 */ 0xfc, 0x07, 0x81, 0xf7, /* 1101 1110 1111 1111 1110 0000 0000 0000 */ 0xde, 0xff, 0xe0, 0x00, /* 0000 0000 0000 0000 0000 1111 1000 0000 */ 0x00, 0x00, 0x0f, 0x80, /* 0000 0000 0000 0000 0000 0000 0111 1111 */ 0x00, 0x00, 0x00, 0x7f, }; /* test code for Table B.3 - Standard Huffman table C */ const int32_t test_output_C[] = { /* line 0, PREFLEN=8, RANGELEN=8, VAL=-256..-1, 11111110+(VAL+256) */ -256, /* 11111110 00000000 */ -255, /* 11111110 00000001 */ -2, /* 11111110 11111110 */ -1, /* 11111110 11111111 */ /* line 1, PREFLEN=1, RANGELEN=0, VAL=0, 0 */ 0, /* 0 */ /* line 2, PREFLEN=2, RANGELEN=0, VAL=1, 10 */ 1, /* 10 */ /* line 3, PREFLEN=3, RANGELEN=0, VAL=2, 110 */ 2, /* 110 */ /* line 4, PREFLEN=4, RANGELEN=3, VAL=3..10, 1110+(VAL-3) */ 3, /* 1110 000 */ 4, /* 1110 001 */ 9, /* 1110 110 */ 10, /* 1110 111 */ /* line 5, PREFLEN=5, RANGELEN=6, VAL=11..74, 11110+(VAL-11) */ 11, /* 11110 000000 */ 12, /* 11110 000001 */ 73, /* 11110 111110 */ 74, /* 11110 111111 */ /* line 6, PREFLEN=8, RANGELEN=32, VAL=-INF..-257, 11111111+(-257-VAL) */ -257, /* 11111111 00000000 00000000 00000000 00000000 */ -258, /* 11111111 00000000 00000000 00000000 00000001 */ /* line 7, PREFLEN=7, RANGELEN=32, VAL=75..INF, 1111110+(VAL-75) */ 75, /* 1111110 00000000 00000000 00000000 00000000 */ 76, /* 1111110 00000000 00000000 00000000 00000001 */ /* line 8, PREFLEN=6, VAL=OOB, 111110 */ /*OOB*/ /* 111110 */ }; const byte test_input_C[] = { /* 1111 1110 0000 0000 1111 1110 0000 0001 */ 0xfe, 0x00, 0xfe, 0x01, /* 1111 1110 1111 1110 1111 1110 1111 1111 */ 0xfe, 0xfe, 0xfe, 0xff, /* 0101 1011 1000 0111 0001 1110 1101 1101 */ 0x5b, 0x87, 0x1e, 0xdd, /* 1111 1100 0000 0111 1000 0001 1111 0111 */ 0xfc, 0x07, 0x81, 0xf7, /* 1101 1110 1111 1111 1111 1100 0000 0000 */ 0xde, 0xff, 0xfc, 0x00, /* 0000 0000 0000 0000 0000 0011 1111 1100 */ 0x00, 0x00, 0x03, 0xfc, /* 0000 0000 0000 0000 0000 0000 0000 0111 */ 0x00, 0x00, 0x00, 0x07, /* 1111 0000 0000 0000 0000 0000 0000 0000 */ 0xf0, 0x00, 0x00, 0x00, /* 0000 0111 1110 0000 0000 0000 0000 0000 */ 0x07, 0xe0, 0x00, 0x00, /* 0000 0000 0001 1111 10 */ 0x00, 0x1f, 0x80, }; /* test code for Table B.4 - Standard Huffman table D */ const int32_t test_output_D[] = { /* line 0, PREFLEN=1, RANGELEN=0, VAL=1, 0 */ 1, /* 0 */ /* line 1, PREFLEN=2, RANGELEN=0, VAL=2, 10 */ 2, /* 10 */ /* line 2, PREFLEN=3, RANGELEN=0, VAL=3, 110 */ 3, /* 110 */ /* line 3, PREFLEN=4, RANGELEN=3, VAL=4..11, 1110+(VAL-4) */ 4, /* 1110 000 */ 5, /* 1110 001 */ 10, /* 1110 110 */ 11, /* 1110 111 */ /* line 4, PREFLEN=5, RANGELEN=6, VAL=12..75, 11110+(VAL-12) */ 12, /* 11110 000000 */ 13, /* 11110 000001 */ 74, /* 11110 111110 */ 75, /* 11110 111111 */ /* line 5, PREFLEN=5, RANGELEN=32, VAL=76..INF, 11111+(VAL-76) */ 76, /* 11111 00000000 00000000 00000000 00000000 */ 77, /* 11111 00000000 00000000 00000000 00000001 */ }; const byte test_input_D[] = { /* 0101 1011 1000 0111 0001 1110 1101 1101 */ 0x5b, 0x87, 0x1e, 0xdd, /* 1111 1100 0000 0111 1000 0001 1111 0111 */ 0xfc, 0x07, 0x81, 0xf7, /* 1101 1110 1111 1111 1110 0000 0000 0000 */ 0xde, 0xff, 0xe0, 0x00, /* 0000 0000 0000 0000 0001 1111 0000 0000 */ 0x00, 0x00, 0x1f, 0x00, /* 0000 0000 0000 0000 0000 0001 */ 0x00, 0x00, 0x01, }; /* test code for Table B.5 - Standard Huffman table E */ const int32_t test_output_E[] = { /* line 0, PREFLEN=7, RANGELEN=8, VAL=-255..0, 1111110+(VAL+255) */ -255, /* 1111110 00000000 */ -254, /* 1111110 00000001 */ -1, /* 1111110 11111110 */ 0, /* 1111110 11111111 */ /* line 1, PREFLEN=1, RANGELEN=0, VAL=1, 0 */ 1, /* 0 */ /* line 2, PREFLEN=2, RANGELEN=0, VAL=2, 10 */ 2, /* 10 */ /* line 3, PREFLEN=3, RANGELEN=0, VAL=3, 110 */ 3, /* 110 */ /* line 4, PREFLEN=4, RANGELEN=3, VAL=4..11, 1110+(VAL-4) */ 4, /* 1110 000 */ 5, /* 1110 001 */ 10, /* 1110 110 */ 11, /* 1110 111 */ /* line 5, PREFLEN=5, RANGELEN=6, VAL=12..75, 11110+(VAL-12) */ 12, /* 11110 000000 */ 13, /* 11110 000001 */ 74, /* 11110 111110 */ 75, /* 11110 111111 */ /* line 6, PREFLEN=7, RANGELEN=32, VAL=-INF..-256, 1111111+(-256-VAL) */ -256, /* 1111111 00000000 00000000 00000000 00000000 */ -257, /* 1111111 00000000 00000000 00000000 00000001 */ /* line 6, PREFLEN=6, RANGELEN=32, VAL=76..INF, 111110+(VAL-76) */ 76, /* 111110 00000000 00000000 00000000 00000000 */ 77, /* 111110 00000000 00000000 00000000 00000001 */ }; const byte test_input_E[] = { /* 1111 1100 0000 0001 1111 1000 0000 0111 */ 0xfc, 0x01, 0xf8, 0x07, /* 1111 0111 1111 0111 1110 1111 1111 0101 */ 0xf7, 0xf7, 0xef, 0xf5, /* 1011 1000 0111 0001 1110 1101 1101 1111 */ 0xb8, 0x71, 0xed, 0xdf, /* 1100 0000 0111 1000 0001 1111 0111 1101 */ 0xc0, 0x78, 0x1f, 0x7d, /* 1110 1111 1111 1111 1000 0000 0000 0000 */ 0xef, 0xff, 0x80, 0x00, /* 0000 0000 0000 0000 0111 1111 0000 0000 */ 0x00, 0x00, 0x7f, 0x00, /* 0000 0000 0000 0000 0000 0001 1111 1000 */ 0x00, 0x00, 0x01, 0xf8, /* 0000 0000 0000 0000 0000 0000 0000 0011 */ 0x00, 0x00, 0x00, 0x03, /* 1110 0000 0000 0000 0000 0000 0000 0000 */ 0xe0, 0x00, 0x00, 0x00, /* 0001 */ 0x10, }; /* test code for Table B.6 - Standard Huffman table F */ const int32_t test_output_F[] = { /* line 0, PREFLEN=5, RANGELEN=10, VAL=-2048..-1025, 11100+(VAL+2048) */ -2048, /* 11100 00000000 00 */ -2047, /* 11100 00000000 01 */ -1026, /* 11100 11111111 10 */ -1025, /* 11100 11111111 11 */ /* line 1, PREFLEN=4, RANGELEN=9, VAL=-1024..-513, 1000+(VAL+1024) */ -1024, /* 1000 00000000 0 */ -1023, /* 1000 00000000 1 */ -514, /* 1000 11111111 0 */ -513, /* 1000 11111111 1 */ /* line 2, PREFLEN=4, RANGELEN=8, VAL=-512..-257, 1001+(VAL+512) */ -512, /* 1001 00000000 */ -511, /* 1001 00000001 */ -258, /* 1001 11111110 */ -257, /* 1001 11111111 */ /* line 3, PREFLEN=4, RANGELEN=7, VAL=-256..-129, 1010+(VAL+256) */ -256, /* 1010 0000000 */ -255, /* 1010 0000001 */ -130, /* 1010 1111110 */ -129, /* 1010 1111111 */ /* line 4, PREFLEN=5, RANGELEN=6, VAL=-128..-65, 11101+(VAL+128) */ -128, /* 11101 000000 */ -127, /* 11101 000001 */ -66, /* 11101 111110 */ -65, /* 11101 111111 */ /* line 5, PREFLEN=5, RANGELEN=5, VAL=-64..-33, 11110+(VAL+64) */ -64, /* 11110 00000 */ -63, /* 11110 00001 */ -34, /* 11110 11110 */ -33, /* 11110 11111 */ /* line 6, PREFLEN=4, RANGELEN=5, VAL=-32..-1, 1011+(VAL+32) */ -32, /* 1011 00000 */ -31, /* 1011 00001 */ -2, /* 1011 11110 */ -1, /* 1011 11111 */ /* line 7, PREFLEN=2, RANGELEN=7, VAL=0..127, 00+VAL */ 0, /* 00 0000000 */ 1, /* 00 0000001 */ 126, /* 00 1111110 */ 127, /* 00 1111111 */ /* line 8, PREFLEN=3, RANGELEN=7, VAL=128..255, 010+(VAL-128) */ 128, /* 010 0000000 */ 129, /* 010 0000001 */ 254, /* 010 1111110 */ 255, /* 010 1111111 */ /* line 9, PREFLEN=3, RANGELEN=8, VAL=256..511, 011+(VAL-256) */ 256, /* 011 00000000 */ 257, /* 011 00000001 */ 510, /* 011 11111110 */ 511, /* 011 11111111 */ /* line 10, PREFLEN=4, RANGELEN=9, VAL=512..1023, 1100+(VAL-512) */ 512, /* 1100 00000000 0 */ 513, /* 1100 00000000 1 */ 1022, /* 1100 11111111 0 */ 1023, /* 1100 11111111 1 */ /* line 11, PREFLEN=4, RANGELEN=10, VAL=1024..2047, 1101+(VAL-1024) */ 1024, /* 1101 00000000 00 */ 1025, /* 1101 00000000 01 */ 2046, /* 1101 11111111 10 */ 2047, /* 1101 11111111 11 */ /* line 12, PREFLEN=6, RANGELEN=32, VAL=-INF..-2049, 111110+(-2049-VAL) */ -2049, /* 111110 00000000 00000000 00000000 00000000 */ -2050, /* 111110 00000000 00000000 00000000 00000001 */ /* line 13, PREFLEN=6, RANGELEN=32, VAL=2048..INF, 111111+(VAL-2048) */ 2048, /* 111111 00000000 00000000 00000000 00000000 */ 2049, /* 111111 00000000 00000000 00000000 00000001 */ }; const byte test_input_F[] = { /* 1110 0000 0000 0001 1100 0000 0000 0111 */ 0xe0, 0x01, 0xc0, 0x07, /* 1001 1111 1111 0111 0011 1111 1111 1000 */ 0x9f, 0xf7, 0x3f, 0xf8, /* 0000 0000 0100 0000 0000 0110 0011 1111 */ 0x00, 0x40, 0x06, 0x3f, /* 1101 0001 1111 1111 1001 0000 0000 1001 */ 0xd1, 0xff, 0x90, 0x09, /* 0000 0001 1001 1111 1110 1001 1111 1111 */ 0x01, 0x9f, 0xe9, 0xff, /* 1010 0000 0001 0100 0000 0110 1011 1111 */ 0xa0, 0x14, 0x06, 0xbf, /* 0101 0111 1111 1110 1000 0001 1101 0000 */ 0x57, 0xfe, 0x81, 0xd0, /* 0111 1011 1111 0111 0111 1111 1111 0000 */ 0x7b, 0xf7, 0x7f, 0xf0, /* 0011 1100 0001 1111 0111 1011 1101 1111 */ 0x3c, 0x1f, 0x7b, 0xdf, /* 1011 0000 0101 1000 0110 1111 1101 0111 */ 0xb0, 0x58, 0x6f, 0xd7, /* 1111 0000 0000 0000 0000 0100 1111 1100 */ 0xf0, 0x00, 0x04, 0xfc, /* 0111 1111 0100 0000 0001 0000 0001 0101 */ 0x7f, 0x40, 0x10, 0x15, /* 1111 1001 0111 1111 0110 0000 0000 1100 */ 0xf9, 0x7f, 0x60, 0x0c, /* 0000 0101 1111 1111 0011 1111 1111 1100 */ 0x05, 0xff, 0x3f, 0xfc, /* 0000 0000 0110 0000 0000 0111 0011 1111 */ 0x00, 0x60, 0x07, 0x3f, /* 1101 1001 1111 1111 1101 0000 0000 0011 */ 0xd9, 0xff, 0xd0, 0x03, /* 0100 0000 0001 1101 1111 1111 1011 0111 */ 0x40, 0x1d, 0xff, 0xb7, /* 1111 1111 1111 1000 0000 0000 0000 0000 */ 0xff, 0xf8, 0x00, 0x00, /* 0000 0000 0000 0011 1110 0000 0000 0000 */ 0x00, 0x03, 0xe0, 0x00, /* 0000 0000 0000 0000 0001 1111 1100 0000 */ 0x00, 0x00, 0x1f, 0xc0, /* 0000 0000 0000 0000 0000 0000 0011 1111 */ 0x00, 0x00, 0x00, 0x3f, /* 0000 0000 0000 0000 0000 0000 0000 0001 */ 0x00, 0x00, 0x00, 0x01, }; /* test code for Table B.7 - Standard Huffman table G */ const int32_t test_output_G[] = { /* line 0, PREFLEN=4, RANGELEN=9, VAL=-1024..-513, 1000+(VAL+1024) */ -1024, /* 1000 00000000 0 */ -1023, /* 1000 00000000 1 */ -514, /* 1000 11111111 0 */ -513, /* 1000 11111111 1 */ /* line 1, PREFLEN=3, RANGELEN=8, VAL=-512..-257, 000+(VAL+512) */ -512, /* 000 00000000 */ -511, /* 000 00000001 */ -258, /* 000 11111110 */ -257, /* 000 11111111 */ /* line 2, PREFLEN=4, RANGELEN=7, VAL=-256..-129, 1001+(VAL+256) */ -256, /* 1001 0000000 */ -255, /* 1001 0000001 */ -130, /* 1001 1111110 */ -129, /* 1001 1111111 */ /* line 3, PREFLEN=5, RANGELEN=6, VAL=-128..-65, 11010+(VAL+128) */ -128, /* 11010 000000 */ -127, /* 11010 000001 */ -66, /* 11010 111110 */ -65, /* 11010 111111 */ /* line 4, PREFLEN=5, RANGELEN=5, VAL=-64..-33, 11011+(VAL+64) */ -64, /* 11011 00000 */ -63, /* 11011 00001 */ -34, /* 11011 11110 */ -33, /* 11011 11111 */ /* line 5, PREFLEN=4, RANGELEN=5, VAL=-32..-1, 1010+(VAL+32) */ -32, /* 1010 00000 */ -31, /* 1010 00001 */ -2, /* 1010 11110 */ -1, /* 1010 11111 */ /* line 6, PREFLEN=4, RANGELEN=5, VAL=0..31, 1011+VAL */ 0, /* 1011 00000 */ 1, /* 1011 00001 */ 30, /* 1011 11110 */ 31, /* 1011 11111 */ /* line 7, PREFLEN=5, RANGELEN=5, VAL=32..63, 11100+(VAL-32) */ 32, /* 11100 00000 */ 33, /* 11100 00001 */ 62, /* 11100 11110 */ 63, /* 11100 11111 */ /* line 8, PREFLEN=5, RANGELEN=6, VAL=64..127, 11101+(VAL-64) */ 64, /* 11101 000000 */ 65, /* 11101 000001 */ 126, /* 11101 111110 */ 127, /* 11101 111111 */ /* line 9, PREFLEN=4, RANGELEN=7, VAL=128..255, 1100+(VAL-128) */ 128, /* 1100 0000000 */ 129, /* 1100 0000001 */ 254, /* 1100 1111110 */ 255, /* 1100 1111111 */ /* line 10, PREFLEN=3, RANGELEN=8, VAL=256..511, 001+(VAL-256) */ 256, /* 001 00000000 */ 257, /* 001 00000001 */ 510, /* 001 11111110 */ 511, /* 001 11111111 */ /* line 11, PREFLEN=3, RANGELEN=9, VAL=512..1023, 010+(VAL-512) */ 512, /* 010 00000000 0 */ 513, /* 010 00000000 1 */ 1022, /* 010 11111111 0 */ 1023, /* 010 11111111 1 */ /* line 12, PREFLEN=3, RANGELEN=10, VAL=1024..2047, 011+(VAL-1024) */ 1024, /* 011 00000000 00 */ 1025, /* 011 00000000 01 */ 2046, /* 011 11111111 10 */ 2047, /* 011 11111111 11 */ /* line 13, PREFLEN=5, RANGELEN=32, VAL=-INF..-1025, 11110+(-1025-VAL) */ -1025, /* 11110 00000000 00000000 00000000 00000000 */ -1026, /* 11110 00000000 00000000 00000000 00000001 */ /* line 14, PREFLEN=5, RANGELEN=32, VAL=2048..INF, 11111+(VAL-2048) */ 2048, /* 11111 00000000 00000000 00000000 00000000 */ 2049, /* 11111 00000000 00000000 00000000 00000001 */ }; const byte test_input_G[] = { /* 1000 0000 0000 0100 0000 0000 0110 0011 */ 0x80, 0x04, 0x00, 0x63, /* 1111 1101 0001 1111 1111 0000 0000 0000 */ 0xfd, 0x1f, 0xf0, 0x00, /* 0000 0000 0100 0111 1111 0000 1111 1111 */ 0x00, 0x47, 0xf0, 0xff, /* 1001 0000 0001 0010 0000 0110 0111 1111 */ 0x90, 0x12, 0x06, 0x7f, /* 0100 1111 1111 1101 0000 0001 1010 0000 */ 0x4f, 0xfd, 0x01, 0xa0, /* 0111 0101 1111 0110 1011 1111 1101 1000 */ 0x75, 0xf6, 0xbf, 0xd8, /* 0011 0110 0001 1101 1111 1011 0111 1111 */ 0x36, 0x1d, 0xfb, 0x7f, /* 1010 0000 0101 0000 0110 1011 1101 0101 */ 0xa0, 0x50, 0x6b, 0xd5, /* 1111 1011 0000 0101 1000 0110 1111 1101 */ 0xfb, 0x05, 0x86, 0xfd, /* 0111 1111 1110 0000 0011 1000 0001 1110 */ 0x7f, 0xe0, 0x38, 0x1e, /* 0111 1011 1001 1111 1110 1000 0001 1101 */ 0x7b, 0x9f, 0xe8, 0x1d, /* 0000 0111 1011 1111 0111 0111 1111 1100 */ 0x07, 0xbf, 0x77, 0xfc, /* 0000 0001 1000 0000 0111 0011 1111 0110 */ 0x01, 0x80, 0x73, 0xf6, /* 0111 1111 0010 0000 0000 0100 0000 0100 */ 0x7f, 0x20, 0x04, 0x04, /* 1111 1111 0001 1111 1111 0100 0000 0000 */ 0xff, 0x1f, 0xf4, 0x00, /* 0100 0000 0001 0101 1111 1110 0101 1111 */ 0x40, 0x15, 0xfe, 0x5f, /* 1111 0110 0000 0000 0011 0000 0000 0101 */ 0xf6, 0x00, 0x30, 0x05, /* 1111 1111 1100 1111 1111 1111 1111 0000 */ 0xff, 0xcf, 0xff, 0xf0, /* 0000 0000 0000 0000 0000 0000 0000 0111 */ 0x00, 0x00, 0x00, 0x07, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ 0x80, 0x00, 0x00, 0x00, /* 0111 1110 0000 0000 0000 0000 0000 0000 */ 0x7e, 0x00, 0x00, 0x00, /* 0000 0001 1111 0000 0000 0000 0000 0000 */ 0x01, 0xf0, 0x00, 0x00, /* 0000 0000 0001 */ 0x00, 0x10, }; /* test code for Table B.8 - Standard Huffman table H */ const int32_t test_output_H[] = { /* line 0, PREFLEN=8, RANGELEN=3, VAL=-15..-8, 11111100+(VAL+15) */ -15, /* 11111100 000 */ -14, /* 11111100 001 */ -9, /* 11111100 110 */ -8, /* 11111100 111 */ /* line 1, PREFLEN=9, RANGELEN=1, VAL=-7..-6, 111111100+(VAL+7) */ -7, /* 111111100 0 */ -6, /* 111111100 1 */ /* line 2, PREFLEN=8, RANGELEN=1, VAL=-5..-4, 11111101+(VAL+5) */ -5, /* 11111101 0 */ -4, /* 11111101 1 */ /* line 3, PREFLEN=9, RANGELEN=0, VAL=-3, 111111101 */ -3, /* 111111101 */ /* line 4, PREFLEN=7, RANGELEN=0, VAL=-2, 1111100 */ -2, /* 1111100 */ /* line 5, PREFLEN=4, RANGELEN=0, VAL=-1, 1010 */ -1, /* 1010 */ /* line 6, PREFLEN=2, RANGELEN=1, VAL=0..1, 00+VAL */ 0, /* 00 0 */ 1, /* 00 1 */ /* line 7, PREFLEN=5, RANGELEN=0, VAL=2, 11010 */ 2, /* 11010 */ /* line 8, PREFLEN=6, RANGELEN=0, VAL=3, 111010 */ 3, /* 111010 */ /* line 9, PREFLEN=3, RANGELEN=4, VAL=4..19, 100+(VAL-4) */ 4, /* 100 0000 */ 5, /* 100 0001 */ 18, /* 100 1110 */ 19, /* 100 1111 */ /* line 10, PREFLEN=6, RANGELEN=1, VAL=20..21, 111011+(VAL-20) */ 20, /* 111011 0 */ 21, /* 111011 1 */ /* line 11, PREFLEN=4, RANGELEN=4, VAL=22..37, 1011+(VAL-22) */ 22, /* 1011 0000 */ 23, /* 1011 0001 */ 36, /* 1011 1110 */ 37, /* 1011 1111 */ /* line 12, PREFLEN=4, RANGELEN=5, VAL=38..69, 1100+(VAL-38) */ 38, /* 1100 00000 */ 39, /* 1100 00001 */ 68, /* 1100 11110 */ 69, /* 1100 11111 */ /* line 13, PREFLEN=5, RANGELEN=6, VAL=70..133, 11011+(VAL-70) */ 70, /* 11011 000000 */ 71, /* 11011 000001 */ 132, /* 11011 111110 */ 133, /* 11011 111111 */ /* line 14, PREFLEN=5, RANGELEN=7, VAL=134..261, 11100+(VAL-134) */ 134, /* 11100 0000000 */ 135, /* 11100 0000001 */ 260, /* 11100 1111110 */ 261, /* 11100 1111111 */ /* line 15, PREFLEN=6, RANGELEN=7, VAL=262..389, 111100+(VAL-262) */ 262, /* 111100 0000000 */ 263, /* 111100 0000001 */ 388, /* 111100 1111110 */ 389, /* 111100 1111111 */ /* line 16, PREFLEN=7, RANGELEN=8, VAL=390..645, 1111101+(VAL-390) */ 390, /* 1111101 00000000 */ 391, /* 1111101 00000001 */ 644, /* 1111101 11111110 */ 645, /* 1111101 11111111 */ /* line 17, PREFLEN=6, RANGELEN=10, VAL=646..1669, 111101+(VAL-646) */ 646, /* 111101 00000000 00 */ 647, /* 111101 00000000 01 */ 1668, /* 111101 11111111 10 */ 1669, /* 111101 11111111 11 */ /* line 18, PREFLEN=9, RANGELEN=32, VAL=-INF..-16, 111111110+(-16-VAL) */ -16, /* 111111110 00000000 00000000 00000000 00000000 */ -17, /* 111111110 00000000 00000000 00000000 00000001 */ /* line 19, PREFLEN=9, RANGELEN=32, VAL=1670..INF, 111111111+(VAL-1670) */ 1670, /* 111111111 00000000 00000000 00000000 00000000 */ 1671, /* 111111111 00000000 00000000 00000000 00000001 */ /* line 20, PREFLEN=2, VAL=OOB, 01 */ /*OOB*/ /* 01 */ }; const byte test_input_H[] = { /* 1111 1100 0001 1111 1000 0111 1111 0011 */ 0xfc, 0x1f, 0x87, 0xf3, /* 0111 1110 0111 1111 1110 0011 1111 1001 */ 0x7e, 0x7f, 0xe3, 0xf9, /* 1111 1101 0111 1110 1111 1111 1011 1111 */ 0xfd, 0x7e, 0xff, 0xbf, /* 0010 1000 0001 1101 0111 0101 0000 0010 */ 0x28, 0x1d, 0x75, 0x02, /* 0000 1100 1110 1001 1111 1101 1011 1011 */ 0x0c, 0xe9, 0xfd, 0xbb, /* 1101 1000 0101 1000 1101 1111 0101 1111 */ 0xd8, 0x58, 0xdf, 0x5f, /* 1110 0000 0011 0000 0011 1001 1110 1100 */ 0xe0, 0x30, 0x39, 0xec, /* 1111 1110 1100 0000 1101 1000 0011 1011 */ 0xfe, 0xc0, 0xd8, 0x3b, /* 1111 1011 0111 1111 1111 0000 0000 0111 */ 0xfb, 0x7f, 0xf0, 0x07, /* 0000 0000 1111 0011 1111 0111 0011 1111 */ 0x00, 0xf3, 0xf7, 0x3f, /* 1111 1000 0000 0011 1100 0000 0011 1110 */ 0xf8, 0x03, 0xc0, 0x3e, /* 0111 1110 1111 0011 1111 1111 1101 0000 */ 0x7e, 0xf3, 0xff, 0xd0, /* 0000 1111 1010 0000 0011 1111 0111 1111 */ 0x0f, 0xa0, 0x3f, 0x7f, /* 1011 1110 1111 1111 1111 1010 0000 0000 */ 0xbe, 0xff, 0xfa, 0x00, /* 0111 1010 0000 0000 1111 1011 1111 1111 */ 0x7a, 0x00, 0xfb, 0xff, /* 0111 1011 1111 1111 1111 1111 1000 0000 */ 0x7b, 0xff, 0xff, 0x80, /* 0000 0000 0000 0000 0000 0000 0011 1111 */ 0x00, 0x00, 0x00, 0x3f, /* 1100 0000 0000 0000 0000 0000 0000 0000 */ 0xc0, 0x00, 0x00, 0x00, /* 0011 1111 1111 0000 0000 0000 0000 0000 */ 0x3f, 0xf0, 0x00, 0x00, /* 0000 0000 0000 1111 1111 1000 0000 0000 */ 0x00, 0x0f, 0xf8, 0x00, /* 0000 0000 0000 0000 0000 101 */ 0x00, 0x00, 0x0a, }; /* test code for Table B.9 - Standard Huffman table I */ const int32_t test_output_I[] = { /* line 0, PREFLEN=8, RANGELEN=4, VAL=-31..-16, 11111100+(VAL+31) */ -31, /* 11111100 0000 */ -30, /* 11111100 0001 */ -17, /* 11111100 1110 */ -16, /* 11111100 1111 */ /* line 1, PREFLEN=9, RANGELEN=2, VAL=-15..-12, 111111100+(VAL+15) */ -15, /* 111111100 00 */ -14, /* 111111100 01 */ -13, /* 111111100 10 */ -12, /* 111111100 11 */ /* line 2, PREFLEN=8, RANGELEN=2, VAL=-11..-8, 11111101+(VAL+11) */ -11, /* 11111101 00 */ -10, /* 11111101 01 */ -9, /* 11111101 10 */ -8, /* 11111101 11 */ /* line 3, PREFLEN=9, RANGELEN=1, VAL=-7..-6, 111111101+(VAL+7) */ -7, /* 111111101 0 */ -6, /* 111111101 1 */ /* line 4, PREFLEN=7, RANGELEN=1, VAL=-5..-4, 1111100+(VAL+5) */ -5, /* 1111100 0 */ -4, /* 1111100 1 */ /* line 5, PREFLEN=4, RANGELEN=1, VAL=-3..-2, 1010+(VAL+3) */ -3, /* 1010 0 */ -2, /* 1010 1 */ /* line 6, PREFLEN=3, RANGELEN=1, VAL=-1..0, 010+(VAL+1) */ -1, /* 010 0 */ 0, /* 010 1 */ /* line 7, PREFLEN=3, RANGELEN=1, VAL=1..2, 011+(VAL-1) */ 1, /* 011 0 */ 2, /* 011 1 */ /* line 8, PREFLEN=5, RANGELEN=1, VAL=3..4, 11010+(VAL-3) */ 3, /* 11010 0 */ 4, /* 11010 1 */ /* line 9, PREFLEN=6, RANGELEN=1, VAL=5..6, 111010+(VAL-5) */ 5, /* 111010 0 */ 6, /* 111010 1 */ /* line 10, PREFLEN=3, RANGELEN=5, VAL=7..38, 100+(VAL-7) */ 7, /* 100 00000 */ 8, /* 100 00001 */ 37, /* 100 11110 */ 38, /* 100 11111 */ /* line 11, PREFLEN=6, RANGELEN=2, VAL=39..42, 111011+(VAL-39) */ 39, /* 111011 00 */ 40, /* 111011 01 */ 41, /* 111011 10 */ 42, /* 111011 11 */ /* line 12, PREFLEN=4, RANGELEN=5, VAL=43..74, 1011+(VAL-43) */ 43, /* 1011 00000 */ 44, /* 1011 00001 */ 73, /* 1011 11110 */ 74, /* 1011 11111 */ /* line 13, PREFLEN=4, RANGELEN=6, VAL=75..138, 1100+(VAL-75) */ 75, /* 1100 000000 */ 76, /* 1100 000001 */ 137, /* 1100 111110 */ 138, /* 1100 111111 */ /* line 14, PREFLEN=5, RANGELEN=7, VAL=139..266, 11011+(VAL-139) */ 139, /* 11011 0000000 */ 140, /* 11011 0000001 */ 265, /* 11011 1111110 */ 266, /* 11011 1111111 */ /* line 15, PREFLEN=5, RANGELEN=8, VAL=267..522, 11100+(VAL-267) */ 267, /* 11100 00000000 */ 268, /* 11100 00000001 */ 521, /* 11100 11111110 */ 522, /* 11100 11111111 */ /* line 16, PREFLEN=6, RANGELEN=8, VAL=523..778, 111100+(VAL-523) */ 523, /* 111100 00000000 */ 524, /* 111100 00000001 */ 777, /* 111100 11111110 */ 778, /* 111100 11111111 */ /* line 17, PREFLEN=7, RANGELEN=9, VAL=779..1290, 1111101+(VAL-779) */ 779, /* 1111101 00000000 0 */ 780, /* 1111101 00000000 1 */ 1289, /* 1111101 11111111 0 */ 1290, /* 1111101 11111111 1 */ /* line 18, PREFLEN=6, RANGELEN=11, VAL=1291..3338, 111101+(VAL-1291) */ 1291, /* 111101 00000000 000 */ 1292, /* 111101 00000000 001 */ 3337, /* 111101 11111111 110 */ 3338, /* 111101 11111111 111 */ /* line 19, PREFLEN=9, RANGELEN=32, VAL=-INF..-32, 111111110+(-32-VAL) */ -32, /* 111111110 00000000 00000000 00000000 00000000 */ -33, /* 111111110 00000000 00000000 00000000 00000001 */ /* line 20, PREFLEN=9, RANGELEN=32, VAL=3339..INF, 111111111+(VAL-3339) */ 3339, /* 111111111 00000000 00000000 00000000 00000000 */ 3340, /* 111111111 00000000 00000000 00000000 00000001 */ /* line 21, PREFLEN=2, VAL=OOB, 00 */ /*OOB*/ /* 00 */ }; const byte test_input_I[] = { /* 1111 1100 0000 1111 1100 0001 1111 1100 */ 0xfc, 0x0f, 0xc1, 0xfc, /* 1110 1111 1100 1111 1111 1110 0001 1111 */ 0xef, 0xcf, 0xfe, 0x1f, /* 1100 0111 1111 1001 0111 1111 0011 1111 */ 0xc7, 0xf9, 0x7f, 0x3f, /* 1101 0011 1111 0101 1111 1101 1011 1111 */ 0xd3, 0xf5, 0xfd, 0xbf, /* 0111 1111 1110 1011 1111 1011 1111 1000 */ 0x7f, 0xeb, 0xfb, 0xf8, /* 1111 1001 1010 0101 0101 0001 0101 1001 */ 0xf9, 0xa5, 0x51, 0x59, /* 1111 0100 1101 0111 1010 0111 0101 1000 */ 0xf4, 0xd7, 0xa7, 0x58, /* 0000 1000 0001 1001 1110 1001 1111 1110 */ 0x08, 0x19, 0xe9, 0xfe, /* 1100 1110 1101 1110 1110 1110 1111 1011 */ 0xce, 0xde, 0xee, 0xfb, /* 0000 0101 1000 0110 1111 1101 0111 1111 */ 0x05, 0x86, 0xfd, 0x7f, /* 1100 0000 0011 0000 0001 1100 1111 1011 */ 0xc0, 0x30, 0x1c, 0xfb, /* 0011 1111 1101 1000 0000 1101 1000 0001 */ 0x3f, 0xd8, 0x0d, 0x81, /* 1101 1111 1110 1101 1111 1111 1110 0000 */ 0xdf, 0xed, 0xff, 0xe0, /* 0000 0111 0000 0000 0111 1001 1111 1101 */ 0x07, 0x00, 0x79, 0xfd, /* 1100 1111 1111 1111 0000 0000 0011 1100 */ 0xcf, 0xff, 0x00, 0x3c, /* 0000 0001 1111 0011 1111 1011 1100 1111 */ 0x01, 0xf3, 0xfb, 0xcf, /* 1111 1111 1010 0000 0000 1111 1010 0000 */ 0xff, 0xa0, 0x0f, 0xa0, /* 0001 1111 1011 1111 1110 1111 1011 1111 */ 0x1f, 0xbf, 0xef, 0xbf, /* 1111 1111 0100 0000 0000 0111 1010 0000 */ 0xff, 0x40, 0x07, 0xa0, /* 0000 0111 1101 1111 1111 1101 1110 1111 */ 0x07, 0xdf, 0xfd, 0xef, /* 1111 1111 1111 1111 0000 0000 0000 0000 */ 0xff, 0xff, 0x00, 0x00, /* 0000 0000 0000 0000 0111 1111 1000 0000 */ 0x00, 0x00, 0x7f, 0x80, /* 0000 0000 0000 0000 0000 0000 0111 1111 */ 0x00, 0x00, 0x00, 0x7f, /* 1110 0000 0000 0000 0000 0000 0000 0000 */ 0xe0, 0x00, 0x00, 0x00, /* 0001 1111 1111 0000 0000 0000 0000 0000 */ 0x1f, 0xf0, 0x00, 0x00, /* 0000 0000 0001 00 */ 0x00, 0x10, }; /* test code for Table B.10 - Standard Huffman table J */ const int32_t test_output_J[] = { /* line 0, PREFLEN=7, RANGELEN=4, VAL=-21..-6, 1111010+(VAL+21) */ -21, /* 1111010 0000 */ -20, /* 1111010 0001 */ -7, /* 1111010 1110 */ -6, /* 1111010 1111 */ /* line 1, PREFLEN=8, RANGELEN=0, VAL=-5, 11111100 */ -5, /* 11111100 */ /* line 2, PREFLEN=7, RANGELEN=0, VAL=-5, 1111011 */ -4, /* 1111011 */ /* line 3, PREFLEN=5, RANGELEN=0, VAL=-3, 11000 */ -3, /* 11000 */ /* line 4, PREFLEN=2, RANGELEN=2, VAL=-2..1, 00+(VAL+2) */ -2, /* 00 00 */ -1, /* 00 01 */ 0, /* 00 10 */ 1, /* 00 11 */ /* line 5, PREFLEN=5, RANGELEN=0, VAL=2, 11001 */ 2, /* 11001 */ /* line 6, PREFLEN=6, RANGELEN=0, VAL=3, 110110 */ 3, /* 110110 */ /* line 7, PREFLEN=7, RANGELEN=0, VAL=4, 1111100 */ 4, /* 1111100 */ /* line 8, PREFLEN=8, RANGELEN=0, VAL=5, 11111101 */ 5, /* 11111101 */ /* line 9, PREFLEN=2, RANGELEN=6, VAL=6..69, 01+(VAL-6) */ 6, /* 01 000000 */ 7, /* 01 000001 */ 68, /* 01 111110 */ 69, /* 01 111111 */ /* line 8, PREFLEN=5, RANGELEN=5, VAL=70..101, 11010+(VAL-70) */ 70, /* 11010 00000 */ 71, /* 11010 00001 */ 100, /* 11010 11110 */ 101, /* 11010 11111 */ /* line 9, PREFLEN=6, RANGELEN=5, VAL=102..133, 110111+(VAL-102) */ 102, /* 110111 00000 */ 103, /* 110111 00001 */ 132, /* 110111 11110 */ 133, /* 110111 11111 */ /* line 10, PREFLEN=6, RANGELEN=6, VAL=134..197, 111000+(VAL-134) */ 134, /* 111000 000000 */ 135, /* 111000 000001 */ 196, /* 111000 111110 */ 197, /* 111000 111111 */ /* line 11, PREFLEN=6, RANGELEN=7, VAL=198..325, 111001+(VAL-198) */ 198, /* 111001 0000000 */ 199, /* 111001 0000001 */ 324, /* 111001 1111110 */ 325, /* 111001 1111111 */ /* line 12, PREFLEN=6, RANGELEN=8, VAL=326..581, 111010+(VAL-326) */ 326, /* 111010 00000000 */ 327, /* 111010 00000001 */ 580, /* 111010 11111110 */ 581, /* 111010 11111111 */ /* line 13, PREFLEN=6, RANGELEN=9, VAL=582..1093, 111011+(VAL-582) */ 582, /* 111011 00000000 0 */ 583, /* 111011 00000000 1 */ 1092, /* 111011 11111111 0 */ 1093, /* 111011 11111111 1 */ /* line 14, PREFLEN=6, RANGELEN=10, VAL=1094..2117, 111100+(VAL-1094) */ 1094, /* 111100 00000000 00 */ 1095, /* 111100 00000000 01 */ 2116, /* 111100 11111111 10 */ 2117, /* 111100 11111111 11 */ /* line 15, PREFLEN=7, RANGELEN=11, VAL=2118..4165, 1111101+(VAL-2118) */ 2118, /* 1111101 00000000 000 */ 2119, /* 1111101 00000000 001 */ 4164, /* 1111101 11111111 110 */ 4165, /* 1111101 11111111 111 */ /* line 16, PREFLEN=8, RANGELEN=32, VAL=-INF..-22, 11111110+(-22-VAL) */ -22, /* 11111110 00000000 00000000 00000000 00000000 */ -23, /* 11111110 00000000 00000000 00000000 00000001 */ /* line 17, PREFLEN=8, RANGELEN=32, VAL=4166..INF, 11111111+(VAL-4166) */ 4166, /* 11111111 00000000 00000000 00000000 00000000 */ 4167, /* 11111111 00000000 00000000 00000000 00000001 */ /* line 8, PREFLEN=2, VAL=OOB, 10 */ /*OOB*/ /* 10 */ }; const byte test_input_J[] = { /* 1111 0100 0001 1110 1000 0111 1101 0111 */ 0xf4, 0x1e, 0x87, 0xd7, /* 0111 1010 1111 1111 1100 1111 0111 1000 */ 0x7a, 0xff, 0xcf, 0x78, /* 0000 0001 0010 0011 1100 1110 1101 1111 */ 0x01, 0x23, 0xce, 0xdf, /* 0011 1111 0101 0000 0001 0000 0101 1111 */ 0x3f, 0x50, 0x10, 0x5f, /* 1001 1111 1111 0100 0000 1101 0000 0111 */ 0x9f, 0xf4, 0x0d, 0x07, /* 0101 1110 1101 0111 1111 0111 0000 0110 */ 0x5e, 0xd7, 0xf7, 0x06, /* 1110 0001 1101 1111 1101 1011 1111 1111 */ 0xe1, 0xdf, 0xdb, 0xff, /* 1000 0000 0011 1000 0000 0111 1000 1111 */ 0x80, 0x38, 0x07, 0x8f, /* 1011 1000 1111 1111 1001 0000 0001 1100 */ 0xb8, 0xff, 0x90, 0x1c, /* 1000 0001 1110 0111 1111 0111 0011 1111 */ 0x81, 0xe7, 0xf7, 0x3f, /* 1111 1010 0000 0000 1110 1000 0000 0111 */ 0xfa, 0x00, 0xe8, 0x07, /* 1010 1111 1110 1110 1011 1111 1111 1011 */ 0xaf, 0xee, 0xbf, 0xfb, /* 0000 0000 0111 0110 0000 0001 1110 1111 */ 0x00, 0x76, 0x01, 0xef, /* 1111 1101 1101 1111 1111 1111 1100 0000 */ 0xfd, 0xdf, 0xff, 0xc0, /* 0000 0011 1100 0000 0000 0111 1100 1111 */ 0x03, 0xc0, 0x07, 0xcf, /* 1111 1011 1100 1111 1111 1111 1110 1000 */ 0xfb, 0xcf, 0xff, 0xe8, /* 0000 0000 1111 1010 0000 0000 0111 1110 */ 0x00, 0xfa, 0x00, 0x7e, /* 1111 1111 1110 1111 1011 1111 1111 1111 */ 0xff, 0xef, 0xbf, 0xff, /* 1111 1000 0000 0000 0000 0000 0000 0000 */ 0xf8, 0x00, 0x00, 0x00, /* 0000 0011 1111 1000 0000 0000 0000 0000 */ 0x03, 0xf8, 0x00, 0x00, /* 0000 0000 0000 0111 1111 1100 0000 0000 */ 0x00, 0x07, 0xfc, 0x00, /* 0000 0000 0000 0000 0000 0011 1111 1100 */ 0x00, 0x00, 0x03, 0xfc, /* 0000 0000 0000 0000 0000 0000 0000 0110 */ 0x00, 0x00, 0x00, 0x06, }; /* test code for Table B.11 - Standard Huffman table K */ const int32_t test_output_K[] = { /* line 0, PREFLEN=1, RANGELEN=0, VAL=1, 0 */ 1, /* 0 */ /* line 1, PREFLEN=2, RANGELEN=1, VAL=2..3, 10+(VAL-2) */ 2, /* 10 0 */ 3, /* 10 1 */ /* line 2, PREFLEN=4, RANGELEN=0, VAL=4, 1100 */ 4, /* 1100 */ /* line 3, PREFLEN=4, RANGELEN=1, VAL=5..6, 1101+(VAL-5) */ 5, /* 1101 0 */ 6, /* 1101 1 */ /* line 4, PREFLEN=5, RANGELEN=1, VAL=7..8, 11100+(VAL-7) */ 7, /* 11100 0 */ 8, /* 11100 1 */ /* line 5, PREFLEN=5, RANGELEN=2, VAL=9..12, 11101+(VAL-9) */ 9, /* 11101 00 */ 10, /* 11101 01 */ 11, /* 11101 10 */ 12, /* 11101 11 */ /* line 6, PREFLEN=6, RANGELEN=2, VAL=13..16, 111100+(VAL-13) */ 13, /* 111100 00 */ 14, /* 111100 01 */ 15, /* 111100 10 */ 16, /* 111100 11 */ /* line 7, PREFLEN=7, RANGELEN=2, VAL=17..20, 1111010+(VAL-17) */ 17, /* 1111010 00 */ 18, /* 1111010 01 */ 19, /* 1111010 10 */ 20, /* 1111010 11 */ /* line 8, PREFLEN=7, RANGELEN=3, VAL=21..28, 1111011+(VAL-21) */ 21, /* 1111011 000 */ 22, /* 1111011 001 */ 27, /* 1111011 110 */ 28, /* 1111011 111 */ /* line 9, PREFLEN=7, RANGELEN=4, VAL=29..44, 1111100+(VAL-29) */ 29, /* 1111100 0000 */ 30, /* 1111100 0001 */ 43, /* 1111100 1110 */ 44, /* 1111100 1111 */ /* line 10, PREFLEN=7, RANGELEN=5, VAL=45..76, 1111101+(VAL-45) */ 45, /* 1111101 00000 */ 46, /* 1111101 00001 */ 75, /* 1111101 11110 */ 76, /* 1111101 11111 */ /* line 11, PREFLEN=7, RANGELEN=6, VAL=77..140, 1111110+(VAL-77) */ 77, /* 1111110 000000 */ 78, /* 1111110 000001 */ 139, /* 1111110 111110 */ 140, /* 1111110 111111 */ /* line 12, PREFLEN=7, RANGELEN=32, VAL=141..INF, 1111111+(VAL-141) */ 141, /* 1111111 00000000 00000000 00000000 00000000 */ 142, /* 1111111 00000000 00000000 00000000 00000001 */ }; const byte test_input_K[] = { /* 0100 1011 1001 1010 1101 1111 0001 1100 */ 0x4b, 0x9a, 0xdf, 0x1c, /* 1111 0100 1110 1011 1101 1011 1011 1111 */ 0xf4, 0xeb, 0xdb, 0xbf, /* 1000 0111 1000 1111 1001 0111 1001 1111 */ 0x87, 0x8f, 0x97, 0x9f, /* 1010 0011 1101 0011 1110 1010 1111 0101 */ 0xa3, 0xd3, 0xea, 0xf5, /* 1111 1011 0001 1110 1100 1111 1011 1101 */ 0xfb, 0x1e, 0xcf, 0xbd, /* 1110 1111 1111 1100 0000 1111 1000 0011 */ 0xef, 0xfc, 0x0f, 0x83, /* 1111 0011 1011 1110 0111 1111 1101 0000 */ 0xf3, 0xbe, 0x7f, 0xd0, /* 0111 1101 0000 1111 1101 1111 0111 1101 */ 0x7d, 0x0f, 0xdf, 0x7d, /* 1111 1111 1110 0000 0011 1111 0000 0011 */ 0xff, 0xe0, 0x3f, 0x03, /* 1111 1011 1110 1111 1101 1111 1111 1111 */ 0xfb, 0xef, 0xdf, 0xff, /* 0000 0000 0000 0000 0000 0000 0000 0000 */ 0x00, 0x00, 0x00, 0x00, /* 1111 1110 0000 0000 0000 0000 0000 0000 */ 0xfe, 0x00, 0x00, 0x00, /* 0000 001 */ 0x02, }; /* test code for Table B.12 - Standard Huffman table L */ const int32_t test_output_L[] = { /* line 0, PREFLEN=1, RANGELEN=0, VAL=1, 0 */ 1, /* 0 */ /* line 1, PREFLEN=2, RANGELEN=0, VAL=2, 10 */ 2, /* 10 */ /* line 2, PREFLEN=3, RANGELEN=1, VAL=3..4, 110+(VAL-3) */ 3, /* 110 0 */ 4, /* 110 1 */ /* line 3, PREFLEN=5, RANGELEN=0, VAL=5, 11100 */ 5, /* 11100 */ /* line 4, PREFLEN=5, RANGELEN=1, VAL=6..7, 11101+(VAL-7) */ 6, /* 11101 0 */ 7, /* 11101 1 */ /* line 5, PREFLEN=6, RANGELEN=1, VAL=8..9, 111100+(VAL-8) */ 8, /* 111100 0 */ 9, /* 111100 1 */ /* line 6, PREFLEN=7, RANGELEN=0, VAL=10, 1111010 */ 10, /* 1111010 */ /* line 7, PREFLEN=7, RANGELEN=1, VAL=11..12, 1111011+(VAL-11) */ 11, /* 1111011 0 */ 12, /* 1111011 1 */ /* line 8, PREFLEN=7, RANGELEN=2, VAL=13..16, 1111100+(VAL-13) */ 13, /* 1111100 00 */ 14, /* 1111100 01 */ 15, /* 1111100 10 */ 16, /* 1111100 11 */ /* line 9, PREFLEN=7, RANGELEN=3, VAL=17..24, 1111101+(VAL-17) */ 17, /* 1111101 000 */ 18, /* 1111101 001 */ 23, /* 1111101 110 */ 24, /* 1111101 111 */ /* line 10, PREFLEN=7, RANGELEN=4, VAL=25..40, 1111110+(VAL-25) */ 25, /* 1111110 0000 */ 26, /* 1111110 0001 */ 39, /* 1111110 1110 */ 40, /* 1111110 1111 */ /* line 11, PREFLEN=8, RANGELEN=5, VAL=41..72, 11111110+(VAL-41) */ 41, /* 11111110 00000 */ 42, /* 11111110 00001 */ 71, /* 11111110 11110 */ 72, /* 11111110 11111 */ /* line 12, PREFLEN=8, RANGELEN=32, VAL=73..INF, 11111111+(VAL-73) */ 73, /* 11111111 00000000 00000000 00000000 00000000 */ 74, /* 11111111 00000000 00000000 00000000 00000001 */ }; const byte test_input_L[] = { /* 0101 1001 1011 1100 1110 1011 1011 1111 */ 0x59, 0xbc, 0xeb, 0xbf, /* 0001 1110 0111 1101 0111 1011 0111 1011 */ 0x1e, 0x7d, 0x7b, 0x7b, /* 1111 1100 0011 1110 0011 1111 0010 1111 */ 0xfc, 0x3e, 0x3f, 0x2f, /* 1001 1111 1101 0001 1111 0100 1111 1101 */ 0x9f, 0xd1, 0xf4, 0xfd, /* 1101 1111 0111 1111 1110 0000 1111 1100 */ 0xdf, 0x7f, 0xe0, 0xfc, /* 0011 1111 1011 1011 1111 0111 1111 1111 */ 0x3f, 0xbb, 0xf7, 0xff, /* 0000 0011 1111 1000 0011 1111 1101 1110 */ 0x03, 0xf8, 0x3f, 0xde, /* 1111 1110 1111 1111 1111 1000 0000 0000 */ 0xfe, 0xff, 0xf8, 0x00, /* 0000 0000 0000 0000 0000 0111 1111 1000 */ 0x00, 0x00, 0x07, 0xf8, /* 0000 0000 0000 0000 0000 0000 0000 1 */ 0x00, 0x00, 0x00, 0x08, }; /* test code for Table B.13 - Standard Huffman table M */ const int32_t test_output_M[] = { /* line 0, PREFLEN=1, RANGELEN=0, VAL=1, 0 */ 1, /* 0 */ /* line 1, PREFLEN=3, RANGELEN=0, VAL=2, 100 */ 2, /* 100 */ /* line 2, PREFLEN=3, RANGELEN=0, VAL=3, 1100 */ 3, /* 1100 */ /* line 3, PREFLEN=5, RANGELEN=0, VAL=4, 11100 */ 4, /* 11100 */ /* line 4, PREFLEN=4, RANGELEN=1, VAL=5..6, 1101+(VAL-5) */ 5, /* 1101 0 */ 6, /* 1101 1 */ /* line 5, PREFLEN=3, RANGELEN=3, VAL=7..14, 101+(VAL-7) */ 7, /* 101 000 */ 8, /* 101 001 */ 13, /* 101 110 */ 14, /* 101 111 */ /* line 6, PREFLEN=6, RANGELEN=1, VAL=15..16, 111010+(VAL-15) */ 15, /* 111010 0 */ 16, /* 111010 1 */ /* line 7, PREFLEN=6, RANGELEN=2, VAL=17..20, 111011+(VAL-17) */ 17, /* 111011 00 */ 18, /* 111011 01 */ 19, /* 111011 10 */ 20, /* 111011 11 */ /* line 8, PREFLEN=6, RANGELEN=3, VAL=21..28, 111100+(VAL-21) */ 21, /* 111100 000 */ 22, /* 111100 001 */ 27, /* 111100 110 */ 28, /* 111100 111 */ /* line 9, PREFLEN=6, RANGELEN=4, VAL=29..44, 111101+(VAL-29) */ 29, /* 111101 0000 */ 30, /* 111101 0001 */ 43, /* 111101 1110 */ 44, /* 111101 1111 */ /* line 10, PREFLEN=6, RANGELEN=5, VAL=45..76, 111110+(VAL-45) */ 45, /* 111110 00000 */ 46, /* 111110 00001 */ 75, /* 111110 11110 */ 76, /* 111110 11111 */ /* line 11, PREFLEN=7, RANGELEN=6, VAL=77..140, 1111110+(VAL-77) */ 77, /* 1111110 000000 */ 78, /* 1111110 000001 */ 139, /* 1111110 111110 */ 140, /* 1111110 111111 */ /* line 12, PREFLEN=7, RANGELEN=32, VAL=141..INF, 1111111+(VAL-141) */ 141, /* 1111111 00000000 00000000 00000000 00000000 */ 142, /* 1111111 00000000 00000000 00000000 00000001 */ }; const byte test_input_M[] = { /* 0100 1100 1110 0110 1011 0111 0100 0101 */ 0x4c, 0xe6, 0xb7, 0x45, /* 0011 0111 0101 1111 1101 0011 1010 1111 */ 0x37, 0x5f, 0xd3, 0xaf, /* 0110 0111 0110 1111 0111 0111 0111 1111 */ 0x67, 0x6f, 0x77, 0x7f, /* 1000 0011 1100 0011 1110 0110 1111 0011 */ 0x83, 0xc3, 0xe6, 0xf3, /* 1111 1010 0001 1110 1000 1111 1011 1101 */ 0xfa, 0x1e, 0x8f, 0xbd, /* 1110 1111 1111 1100 0000 1111 1000 0011 */ 0xef, 0xfc, 0x0f, 0x83, /* 1111 0111 1011 1110 1111 1111 1110 0000 */ 0xf7, 0xbe, 0xff, 0xe0, /* 0011 1111 0000 0011 1111 1011 1110 1111 */ 0x3f, 0x03, 0xfb, 0xef, /* 1101 1111 1111 1111 0000 0000 0000 0000 */ 0xdf, 0xff, 0x00, 0x00, /* 0000 0000 0000 0000 1111 1110 0000 0000 */ 0x00, 0x00, 0xfe, 0x00, /* 0000 0000 0000 0000 0000 001 */ 0x00, 0x00, 0x02, }; /* test code for Table B.14 - Standard Huffman table N */ const int32_t test_output_N[] = { /* line 0, PREFLEN=3, RANGELEN=0, VAL=-2, 100 */ -2, /* 100 */ /* line 1, PREFLEN=3, RANGELEN=0, VAL=-1, 101 */ -1, /* 101 */ /* line 2, PREFLEN=1, RANGELEN=0, VAL=1, 0 */ 0, /* 0 */ /* line 3, PREFLEN=3, RANGELEN=0, VAL=1, 110 */ 1, /* 110 */ /* line 4, PREFLEN=3, RANGELEN=0, VAL=2, 111 */ 2, /* 111 */ }; const byte test_input_N[] = { /* 1001 0101 1011 1 */ 0x95, 0xb8, }; /* test code for Table B.15 - Standard Huffman table O */ const int32_t test_output_O[] = { /* line 0, PREFLEN=7, RANGELEN=4, VAL=-24..-9, 1111100+(VAL+24) */ -24, /* 1111100 0000 */ -23, /* 1111100 0001 */ -10, /* 1111100 1110 */ -9, /* 1111100 1111 */ /* line 1, PREFLEN=6, RANGELEN=2, VAL=-8..-5, 111100+(VAL+8) */ -8, /* 111100 00 */ -7, /* 111100 01 */ -6, /* 111100 10 */ -5, /* 111100 11 */ /* line 2, PREFLEN=5, RANGELEN=1, VAL=-4..-3, 11100+(VAL+4) */ -4, /* 11100 0 */ -3, /* 11100 1 */ /* line 3, PREFLEN=4, RANGELEN=0, VAL=-2, 1100 */ -2, /* 1100 */ /* line 4, PREFLEN=3, RANGELEN=0, VAL=-1, 100 */ -1, /* 100 */ /* line 5, PREFLEN=1, RANGELEN=0, VAL=1, 0 */ 0, /* 0 */ /* line 6, PREFLEN=3, RANGELEN=0, VAL=1, 101 */ 1, /* 101 */ /* line 7, PREFLEN=4, RANGELEN=0, VAL=2, 1101 */ 2, /* 1101 */ /* line 8, PREFLEN=5, RANGELEN=1, VAL=3..4, 11101+(VAL-3) */ 3, /* 11101 0 */ 4, /* 11101 1 */ /* line 9, PREFLEN=6, RANGELEN=2, VAL=5..8, 111101+(VAL-5) */ 5, /* 111101 00 */ 6, /* 111101 01 */ 7, /* 111101 10 */ 8, /* 111101 11 */ /* line 10, PREFLEN=7, RANGELEN=4, VAL=9..24, 1111101+(VAL-9) */ 9, /* 1111101 0000 */ 10, /* 1111101 0001 */ 23, /* 1111101 1110 */ 24, /* 1111101 1111 */ /* line 11, PREFLEN=7, RANGELEN=32, VAL=-INF..-25, 1111110+(-25-VAL) */ -25, /* 1111110 00000000 00000000 00000000 00000000 */ -26, /* 1111110 00000000 00000000 00000000 00000001 */ /* line 12, PREFLEN=7, RANGELEN=32, VAL=25..INF, 1111111+(VAL-25) */ 25, /* 1111111 00000000 00000000 00000000 00000000 */ 26, /* 1111111 00000000 00000000 00000000 00000001 */ }; const byte test_input_O[] = { /* 1111 1000 0001 1111 0000 0111 1110 0111 */ 0xf8, 0x1f, 0x07, 0xe7, /* 0111 1100 1111 1111 0000 1111 0001 1111 */ 0x7c, 0xff, 0x0f, 0x1f, /* 0010 1111 0011 1110 0011 1001 1100 1000 */ 0x2f, 0x3e, 0x39, 0xc8, /* 1011 1011 1101 0111 0111 1110 1001 1110 */ 0xbb, 0xd7, 0x7e, 0x9e, /* 1011 1110 1101 1110 1111 1111 0100 0011 */ 0xbe, 0xde, 0xff, 0x43, /* 1110 1000 1111 1101 1110 1111 1011 1111 */ 0xe8, 0xfd, 0xef, 0xbf, /* 1111 1000 0000 0000 0000 0000 0000 0000 */ 0xf8, 0x00, 0x00, 0x00, /* 0000 0011 1111 0000 0000 0000 0000 0000 */ 0x03, 0xf0, 0x00, 0x00, /* 0000 0000 0000 1111 1111 0000 0000 0000 */ 0x00, 0x0f, 0xf0, 0x00, /* 0000 0000 0000 0000 0000 1111 1110 0000 */ 0x00, 0x00, 0x0f, 0xe0, /* 0000 0000 0000 0000 0000 0000 001 */ 0x00, 0x00, 0x00, 0x20, }; typedef struct test_huffmancodes { const char *name; const Jbig2HuffmanParams *params; const byte *input; const size_t input_len; const int32_t *output; const size_t output_len; } test_huffmancodes_t; #define countof(x) (sizeof((x)) / sizeof((x)[0])) #define DEF_TEST_HUFFMANCODES(x) { \ #x, \ &jbig2_huffman_params_##x, \ test_input_##x, countof(test_input_##x), \ test_output_##x, countof(test_output_##x), \ } test_huffmancodes_t tests[] = { DEF_TEST_HUFFMANCODES(A), DEF_TEST_HUFFMANCODES(B), DEF_TEST_HUFFMANCODES(C), DEF_TEST_HUFFMANCODES(D), DEF_TEST_HUFFMANCODES(E), DEF_TEST_HUFFMANCODES(F), DEF_TEST_HUFFMANCODES(G), DEF_TEST_HUFFMANCODES(H), DEF_TEST_HUFFMANCODES(I), DEF_TEST_HUFFMANCODES(J), DEF_TEST_HUFFMANCODES(K), DEF_TEST_HUFFMANCODES(L), DEF_TEST_HUFFMANCODES(M), DEF_TEST_HUFFMANCODES(N), DEF_TEST_HUFFMANCODES(O), }; typedef struct test_stream { Jbig2WordStream ws; test_huffmancodes_t *h; } test_stream_t; static int test_get_word2(Jbig2WordStream *self, size_t offset, uint32_t *word) { test_stream_t *st = (test_stream_t *) self; uint32_t val = 0; int ret = 0; if (st == NULL || st->h == NULL || word == NULL) return -1; if (offset >= st->h->input_len) return 0; if (offset < st->h->input_len) { val |= (st->h->input[offset] << 24); ret++; } if (offset + 1 < st->h->input_len) { val |= (st->h->input[offset + 1] << 16); ret++; } if (offset + 2 < st->h->input_len) { val |= (st->h->input[offset + 2] << 8); ret++; } if (offset + 3 < st->h->input_len) { val |= st->h->input[offset + 3]; ret++; } *word = val; return ret; } static int test2() { Jbig2Ctx *ctx; int success = 0; int i; ctx = jbig2_ctx_new(NULL, 0, NULL, NULL, NULL); if (ctx == NULL) { fprintf(stderr, "Failed to allocate jbig2 context\n"); return 0; } for (i = 0; i < countof(tests); i++) { Jbig2HuffmanTable *table; Jbig2HuffmanState *hs; test_stream_t st; int32_t code; bool oob; int j; st.ws.get_next_word = test_get_word2; st.h = &tests[i]; printf("testing Standard Huffman table %s: ", st.h->name); table = jbig2_build_huffman_table(ctx, st.h->params); if (table == NULL) { fprintf(stderr, "jbig2_build_huffman_table() returned NULL!\n"); jbig2_ctx_free(ctx); return 0; } /* jbig2_dump_huffman_table(table); */ hs = jbig2_huffman_new(ctx, &st.ws); if (hs == NULL) { fprintf(stderr, "jbig2_huffman_new() returned NULL!\n"); jbig2_release_huffman_table(ctx, table); jbig2_ctx_free(ctx); return 0; } for (j = 0; j < st.h->output_len; j++) { printf("%d...", st.h->output[j]); code = jbig2_huffman_get(hs, table, &oob); if (code == st.h->output[j] && !oob) { printf("ok, "); } else { int need_comma = 0; printf("NG("); if (code != st.h->output[j]) { printf("%d", code); need_comma = 1; } if (oob) { if (need_comma) printf(","); printf("OOB"); } printf("), "); } } if (st.h->params->HTOOB) { printf("OOB..."); code = jbig2_huffman_get(hs, table, &oob); if (oob) { printf("ok"); } else { printf("NG(%d)", code); } } printf("\n"); jbig2_huffman_free(ctx, hs); jbig2_release_huffman_table(ctx, table); } jbig2_ctx_free(ctx); if (i == countof(tests)) success = 1; return success; } int main(int argc, char **argv) { return test1() && test2() ? 0 : 1; } #endif jbig2dec-0.18/jbig2_huffman.h000066400000000000000000000074431362055302000157710ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_HUFFMAN_H #define _JBIG2_HUFFMAN_H /* Huffman coder interface */ typedef struct _Jbig2HuffmanEntry Jbig2HuffmanEntry; typedef struct _Jbig2HuffmanState Jbig2HuffmanState; typedef struct _Jbig2HuffmanTable Jbig2HuffmanTable; typedef struct _Jbig2HuffmanParams Jbig2HuffmanParams; struct _Jbig2HuffmanEntry { union { int32_t RANGELOW; Jbig2HuffmanTable *ext_table; } u; byte PREFLEN; byte RANGELEN; byte flags; }; struct _Jbig2HuffmanTable { int log_table_size; Jbig2HuffmanEntry *entries; }; typedef struct _Jbig2HuffmanLine Jbig2HuffmanLine; struct _Jbig2HuffmanLine { int PREFLEN; int RANGELEN; int RANGELOW; }; struct _Jbig2HuffmanParams { bool HTOOB; int n_lines; const Jbig2HuffmanLine *lines; }; Jbig2HuffmanState *jbig2_huffman_new(Jbig2Ctx *ctx, Jbig2WordStream *ws); void jbig2_huffman_free(Jbig2Ctx *ctx, Jbig2HuffmanState *hs); int jbig2_huffman_skip(Jbig2HuffmanState *hs); int jbig2_huffman_advance(Jbig2HuffmanState *hs, size_t advance); uint32_t jbig2_huffman_offset(Jbig2HuffmanState *hs); int32_t jbig2_huffman_get(Jbig2HuffmanState *hs, const Jbig2HuffmanTable *table, bool *oob); int32_t jbig2_huffman_get_bits(Jbig2HuffmanState *hs, const int bits, int *err); #ifdef JBIG2_DEBUG void jbig2_dump_huffman_state(Jbig2HuffmanState *hs); void jbig2_dump_huffman_binary(Jbig2HuffmanState *hs); #endif Jbig2HuffmanTable *jbig2_build_huffman_table(Jbig2Ctx *ctx, const Jbig2HuffmanParams *params); void jbig2_release_huffman_table(Jbig2Ctx *ctx, Jbig2HuffmanTable *table); /* standard Huffman templates defined by the specification */ extern const Jbig2HuffmanParams jbig2_huffman_params_A; /* Table B.1 */ extern const Jbig2HuffmanParams jbig2_huffman_params_B; /* Table B.2 */ extern const Jbig2HuffmanParams jbig2_huffman_params_C; /* Table B.3 */ extern const Jbig2HuffmanParams jbig2_huffman_params_D; /* Table B.4 */ extern const Jbig2HuffmanParams jbig2_huffman_params_E; /* Table B.5 */ extern const Jbig2HuffmanParams jbig2_huffman_params_F; /* Table B.6 */ extern const Jbig2HuffmanParams jbig2_huffman_params_G; /* Table B.7 */ extern const Jbig2HuffmanParams jbig2_huffman_params_H; /* Table B.8 */ extern const Jbig2HuffmanParams jbig2_huffman_params_I; /* Table B.9 */ extern const Jbig2HuffmanParams jbig2_huffman_params_J; /* Table B.10 */ extern const Jbig2HuffmanParams jbig2_huffman_params_K; /* Table B.11 */ extern const Jbig2HuffmanParams jbig2_huffman_params_L; /* Table B.12 */ extern const Jbig2HuffmanParams jbig2_huffman_params_M; /* Table B.13 */ extern const Jbig2HuffmanParams jbig2_huffman_params_N; /* Table B.14 */ extern const Jbig2HuffmanParams jbig2_huffman_params_O; /* Table B.15 */ /* Routines to handle "code table segment (53)" */ /* Parse a code table segment, store Jbig2HuffmanParams in segment->result */ int jbig2_table(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data); /* free Jbig2HuffmanParams allocated by jbig2_huffman_table() */ void jbig2_table_free(Jbig2Ctx *ctx, Jbig2HuffmanParams *params); /* find a user supplied table used by 'segment' and by 'index' */ const Jbig2HuffmanParams *jbig2_find_table(Jbig2Ctx *ctx, Jbig2Segment *segment, int index); #endif /* _JBIG2_HUFFMAN_H */ jbig2dec-0.18/jbig2_hufftab.c000066400000000000000000000174521362055302000157600ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* predefined Huffman table definitions -- See Annex B of the JBIG2 specification */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_huffman.h" #include "jbig2_hufftab.h" #define JBIG2_COUNTOF(x) (sizeof((x)) / sizeof((x)[0])) /* Table B.1 */ static const Jbig2HuffmanLine jbig2_huffman_lines_A[] = { {1, 4, 0}, {2, 8, 16}, {3, 16, 272}, {0, 32, -1}, /* low */ {3, 32, 65808} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_A = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_A), jbig2_huffman_lines_A }; /* Table B.2 */ static const Jbig2HuffmanLine jbig2_huffman_lines_B[] = { {1, 0, 0}, {2, 0, 1}, {3, 0, 2}, {4, 3, 3}, {5, 6, 11}, {0, 32, -1}, /* low */ {6, 32, 75}, /* high */ {6, 0, 0} /* OOB */ }; const Jbig2HuffmanParams jbig2_huffman_params_B = { TRUE, JBIG2_COUNTOF(jbig2_huffman_lines_B), jbig2_huffman_lines_B }; /* Table B.3 */ static const Jbig2HuffmanLine jbig2_huffman_lines_C[] = { {8, 8, -256}, {1, 0, 0}, {2, 0, 1}, {3, 0, 2}, {4, 3, 3}, {5, 6, 11}, {8, 32, -257}, /* low */ {7, 32, 75}, /* high */ {6, 0, 0} /* OOB */ }; const Jbig2HuffmanParams jbig2_huffman_params_C = { TRUE, JBIG2_COUNTOF(jbig2_huffman_lines_C), jbig2_huffman_lines_C }; /* Table B.4 */ static const Jbig2HuffmanLine jbig2_huffman_lines_D[] = { {1, 0, 1}, {2, 0, 2}, {3, 0, 3}, {4, 3, 4}, {5, 6, 12}, {0, 32, -1}, /* low */ {5, 32, 76}, /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_D = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_D), jbig2_huffman_lines_D }; /* Table B.5 */ static const Jbig2HuffmanLine jbig2_huffman_lines_E[] = { {7, 8, -255}, {1, 0, 1}, {2, 0, 2}, {3, 0, 3}, {4, 3, 4}, {5, 6, 12}, {7, 32, -256}, /* low */ {6, 32, 76} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_E = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_E), jbig2_huffman_lines_E }; /* Table B.6 */ static const Jbig2HuffmanLine jbig2_huffman_lines_F[] = { {5, 10, -2048}, {4, 9, -1024}, {4, 8, -512}, {4, 7, -256}, {5, 6, -128}, {5, 5, -64}, {4, 5, -32}, {2, 7, 0}, {3, 7, 128}, {3, 8, 256}, {4, 9, 512}, {4, 10, 1024}, {6, 32, -2049}, /* low */ {6, 32, 2048} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_F = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_F), jbig2_huffman_lines_F }; /* Table B.7 */ static const Jbig2HuffmanLine jbig2_huffman_lines_G[] = { {4, 9, -1024}, {3, 8, -512}, {4, 7, -256}, {5, 6, -128}, {5, 5, -64}, {4, 5, -32}, {4, 5, 0}, {5, 5, 32}, {5, 6, 64}, {4, 7, 128}, {3, 8, 256}, {3, 9, 512}, {3, 10, 1024}, {5, 32, -1025}, /* low */ {5, 32, 2048} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_G = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_G), jbig2_huffman_lines_G }; /* Table B.8 */ static const Jbig2HuffmanLine jbig2_huffman_lines_H[] = { {8, 3, -15}, {9, 1, -7}, {8, 1, -5}, {9, 0, -3}, {7, 0, -2}, {4, 0, -1}, {2, 1, 0}, {5, 0, 2}, {6, 0, 3}, {3, 4, 4}, {6, 1, 20}, {4, 4, 22}, {4, 5, 38}, {5, 6, 70}, {5, 7, 134}, {6, 7, 262}, {7, 8, 390}, {6, 10, 646}, {9, 32, -16}, /* low */ {9, 32, 1670}, /* high */ {2, 0, 0} /* OOB */ }; const Jbig2HuffmanParams jbig2_huffman_params_H = { TRUE, JBIG2_COUNTOF(jbig2_huffman_lines_H), jbig2_huffman_lines_H }; /* Table B.9 */ static const Jbig2HuffmanLine jbig2_huffman_lines_I[] = { {8, 4, -31}, {9, 2, -15}, {8, 2, -11}, {9, 1, -7}, {7, 1, -5}, {4, 1, -3}, {3, 1, -1}, {3, 1, 1}, {5, 1, 3}, {6, 1, 5}, {3, 5, 7}, {6, 2, 39}, {4, 5, 43}, {4, 6, 75}, {5, 7, 139}, {5, 8, 267}, {6, 8, 523}, {7, 9, 779}, {6, 11, 1291}, {9, 32, -32}, /* low */ {9, 32, 3339}, /* high */ {2, 0, 0} /* OOB */ }; const Jbig2HuffmanParams jbig2_huffman_params_I = { TRUE, JBIG2_COUNTOF(jbig2_huffman_lines_I), jbig2_huffman_lines_I }; /* Table B.10 */ static const Jbig2HuffmanLine jbig2_huffman_lines_J[] = { {7, 4, -21}, {8, 0, -5}, {7, 0, -4}, {5, 0, -3}, {2, 2, -2}, {5, 0, 2}, {6, 0, 3}, {7, 0, 4}, {8, 0, 5}, {2, 6, 6}, {5, 5, 70}, {6, 5, 102}, {6, 6, 134}, {6, 7, 198}, {6, 8, 326}, {6, 9, 582}, {6, 10, 1094}, {7, 11, 2118}, {8, 32, -22}, /* low */ {8, 32, 4166}, /* high */ {2, 0, 0} /* OOB */ }; const Jbig2HuffmanParams jbig2_huffman_params_J = { TRUE, JBIG2_COUNTOF(jbig2_huffman_lines_J), jbig2_huffman_lines_J }; /* Table B.11 */ static const Jbig2HuffmanLine jbig2_huffman_lines_K[] = { {1, 0, 1}, {2, 1, 2}, {4, 0, 4}, {4, 1, 5}, {5, 1, 7}, {5, 2, 9}, {6, 2, 13}, {7, 2, 17}, {7, 3, 21}, {7, 4, 29}, {7, 5, 45}, {7, 6, 77}, {0, 32, -1}, /* low */ {7, 32, 141} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_K = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_K), jbig2_huffman_lines_K }; /* Table B.12 */ static const Jbig2HuffmanLine jbig2_huffman_lines_L[] = { {1, 0, 1}, {2, 0, 2}, {3, 1, 3}, {5, 0, 5}, {5, 1, 6}, {6, 1, 8}, {7, 0, 10}, {7, 1, 11}, {7, 2, 13}, {7, 3, 17}, {7, 4, 25}, {8, 5, 41}, {8, 32, 73}, {0, 32, -1}, /* low */ {0, 32, 0} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_L = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_L), jbig2_huffman_lines_L }; /* Table B.13 */ static const Jbig2HuffmanLine jbig2_huffman_lines_M[] = { {1, 0, 1}, {3, 0, 2}, {4, 0, 3}, {5, 0, 4}, {4, 1, 5}, {3, 3, 7}, {6, 1, 15}, {6, 2, 17}, {6, 3, 21}, {6, 4, 29}, {6, 5, 45}, {7, 6, 77}, {0, 32, -1}, /* low */ {7, 32, 141} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_M = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_M), jbig2_huffman_lines_M }; /* Table B.14 */ static const Jbig2HuffmanLine jbig2_huffman_lines_N[] = { {3, 0, -2}, {3, 0, -1}, {1, 0, 0}, {3, 0, 1}, {3, 0, 2}, {0, 32, -1}, /* low */ {0, 32, 3}, /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_N = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_N), jbig2_huffman_lines_N }; /* Table B.15 */ static const Jbig2HuffmanLine jbig2_huffman_lines_O[] = { {7, 4, -24}, {6, 2, -8}, {5, 1, -4}, {4, 0, -2}, {3, 0, -1}, {1, 0, 0}, {3, 0, 1}, {4, 0, 2}, {5, 1, 3}, {6, 2, 5}, {7, 4, 9}, {7, 32, -25}, /* low */ {7, 32, 25} /* high */ }; const Jbig2HuffmanParams jbig2_huffman_params_O = { FALSE, JBIG2_COUNTOF(jbig2_huffman_lines_O), jbig2_huffman_lines_O }; jbig2dec-0.18/jbig2_hufftab.h000066400000000000000000000031141362055302000157530ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* predefined Huffman table definitions -- See Annex B.5 of the JBIG2 specification */ #ifndef _JBIG2_HUFFTAB_H #define _JBIG2_HUFFTAB_H extern const Jbig2HuffmanParams jbig2_huffman_params_A; extern const Jbig2HuffmanParams jbig2_huffman_params_B; extern const Jbig2HuffmanParams jbig2_huffman_params_C; extern const Jbig2HuffmanParams jbig2_huffman_params_D; extern const Jbig2HuffmanParams jbig2_huffman_params_E; extern const Jbig2HuffmanParams jbig2_huffman_params_F; extern const Jbig2HuffmanParams jbig2_huffman_params_G; extern const Jbig2HuffmanParams jbig2_huffman_params_H; extern const Jbig2HuffmanParams jbig2_huffman_params_I; extern const Jbig2HuffmanParams jbig2_huffman_params_J; extern const Jbig2HuffmanParams jbig2_huffman_params_K; extern const Jbig2HuffmanParams jbig2_huffman_params_L; extern const Jbig2HuffmanParams jbig2_huffman_params_M; extern const Jbig2HuffmanParams jbig2_huffman_params_N; extern const Jbig2HuffmanParams jbig2_huffman_params_O; #endif /* _JBIG2_HUFFTAB_H */ jbig2dec-0.18/jbig2_image.c000066400000000000000000000422351362055302000154200ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include #include /* memcpy() */ #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_image.h" #if !defined (INT32_MAX) #define INT32_MAX 0x7fffffff #endif #if !defined (UINT32_MAX) #define UINT32_MAX 0xffffffffu #endif /* allocate a Jbig2Image structure and its associated bitmap */ Jbig2Image * jbig2_image_new(Jbig2Ctx *ctx, uint32_t width, uint32_t height) { Jbig2Image *image; uint32_t stride; if (width == 0 || height == 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to create zero sized image"); return NULL; } image = jbig2_new(ctx, Jbig2Image, 1); if (image == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate image"); return NULL; } stride = ((width - 1) >> 3) + 1; /* generate a byte-aligned stride */ /* check for integer multiplication overflow */ if (height > (INT32_MAX / stride)) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "integer multiplication overflow (stride=%u, height=%u)", stride, height); jbig2_free(ctx->allocator, image); return NULL; } image->data = jbig2_new(ctx, uint8_t, (size_t) height * stride); if (image->data == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate image data buffer (stride=%u, height=%u)", stride, height); jbig2_free(ctx->allocator, image); return NULL; } image->width = width; image->height = height; image->stride = stride; image->refcount = 1; return image; } /* bump the reference count for an image pointer */ Jbig2Image * jbig2_image_reference(Jbig2Ctx *ctx, Jbig2Image *image) { if (image) image->refcount++; return image; } /* release an image pointer, freeing it it appropriate */ void jbig2_image_release(Jbig2Ctx *ctx, Jbig2Image *image) { if (image == NULL) return; image->refcount--; if (image->refcount == 0) jbig2_image_free(ctx, image); } /* free a Jbig2Image structure and its associated memory */ void jbig2_image_free(Jbig2Ctx *ctx, Jbig2Image *image) { if (image != NULL) { jbig2_free(ctx->allocator, image->data); jbig2_free(ctx->allocator, image); } } /* resize a Jbig2Image */ Jbig2Image * jbig2_image_resize(Jbig2Ctx *ctx, Jbig2Image *image, uint32_t width, uint32_t height, int value) { if (width == image->width) { uint8_t *data; /* check for integer multiplication overflow */ if (image->height > (INT32_MAX / image->stride)) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "integer multiplication overflow during resize (stride=%u, height=%u)", image->stride, height); return NULL; } /* use the same stride, just change the length */ data = jbig2_renew(ctx, image->data, uint8_t, (size_t) height * image->stride); if (data == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to reallocate image"); return NULL; } image->data = data; if (height > image->height) { const uint8_t fill = value ? 0xFF : 0x00; memset(image->data + (size_t) image->height * image->stride, fill, ((size_t) height - image->height) * image->stride); } image->height = height; } else { Jbig2Image *newimage; int code; /* Unoptimized implementation, but it works. */ newimage = jbig2_image_new(ctx, width, height); if (newimage == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to allocate resized image"); return NULL; } jbig2_image_clear(ctx, newimage, value); code = jbig2_image_compose(ctx, newimage, image, 0, 0, JBIG2_COMPOSE_REPLACE); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to compose image buffers when resizing"); jbig2_image_release(ctx, newimage); return NULL; } /* if refcount > 1 the original image, its pointer must be kept, so simply replaces its innards, and throw away the empty new image shell. */ jbig2_free(ctx->allocator, image->data); image->width = newimage->width; image->height = newimage->height; image->stride = newimage->stride; image->data = newimage->data; jbig2_free(ctx->allocator, newimage); } return image; } static inline void template_image_compose_opt(const uint8_t * JBIG2_RESTRICT ss, uint8_t * JBIG2_RESTRICT dd, int early, int late, uint8_t leftmask, uint8_t rightmask, uint32_t bytewidth_, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride, Jbig2ComposeOp op) { int i; uint32_t j; int bytewidth = (int)bytewidth_; if (bytewidth == 1) { for (j = 0; j < h; j++) { /* Only 1 byte! */ uint8_t v = (((early ? 0 : ss[0]<<8) | (late ? 0 : ss[1]))>>shift); if (op == JBIG2_COMPOSE_OR) *dd |= v & leftmask; else if (op == JBIG2_COMPOSE_AND) *dd &= (v & leftmask) | ~leftmask; else if (op == JBIG2_COMPOSE_XOR) *dd ^= v & leftmask; else if (op == JBIG2_COMPOSE_XNOR) *dd ^= (~v) & leftmask; else /* Replace */ *dd = (v & leftmask) | (*dd & ~leftmask); dd += dstride; ss += sstride; } return; } bytewidth -= 2; if (shift == 0) { ss++; for (j = 0; j < h; j++) { /* Left byte */ const uint8_t * JBIG2_RESTRICT s = ss; uint8_t * JBIG2_RESTRICT d = dd; if (op == JBIG2_COMPOSE_OR) *d++ |= *s++ & leftmask; else if (op == JBIG2_COMPOSE_AND) *d++ &= (*s++ & leftmask) | ~leftmask; else if (op == JBIG2_COMPOSE_XOR) *d++ ^= *s++ & leftmask; else if (op == JBIG2_COMPOSE_XNOR) *d++ ^= (~*s++) & leftmask; else /* Replace */ *d = (*s++ & leftmask) | (*d & ~leftmask), d++; /* Central run */ for (i = bytewidth; i != 0; i--) { if (op == JBIG2_COMPOSE_OR) *d++ |= *s++; else if (op == JBIG2_COMPOSE_AND) *d++ &= *s++; else if (op == JBIG2_COMPOSE_XOR) *d++ ^= *s++; else if (op == JBIG2_COMPOSE_XNOR) *d++ ^= ~*s++; else /* Replace */ *d++ = *s++; } /* Right byte */ if (op == JBIG2_COMPOSE_OR) *d |= *s & rightmask; else if (op == JBIG2_COMPOSE_AND) *d &= (*s & rightmask) | ~rightmask; else if (op == JBIG2_COMPOSE_XOR) *d ^= *s & rightmask; else if (op == JBIG2_COMPOSE_XNOR) *d ^= (~*s) & rightmask; else /* Replace */ *d = (*s & rightmask) | (*d & ~rightmask); dd += dstride; ss += sstride; } } else { for (j = 0; j < h; j++) { /* Left byte */ const uint8_t * JBIG2_RESTRICT s = ss; uint8_t * JBIG2_RESTRICT d = dd; uint8_t s0, s1, v; s0 = early ? 0 : *s; s++; s1 = *s++; v = ((s0<<8) | s1)>>shift; if (op == JBIG2_COMPOSE_OR) *d++ |= v & leftmask; else if (op == JBIG2_COMPOSE_AND) *d++ &= (v & leftmask) | ~leftmask; else if (op == JBIG2_COMPOSE_XOR) *d++ ^= v & leftmask; else if (op == JBIG2_COMPOSE_XNOR) *d++ ^= (~v) & leftmask; else /* Replace */ *d = (v & leftmask) | (*d & ~leftmask), d++; /* Central run */ for (i = bytewidth; i > 0; i--) { s0 = s1; s1 = *s++; v = ((s0<<8) | s1)>>shift; if (op == JBIG2_COMPOSE_OR) *d++ |= v; else if (op == JBIG2_COMPOSE_AND) *d++ &= v; else if (op == JBIG2_COMPOSE_XOR) *d++ ^= v; else if (op == JBIG2_COMPOSE_XNOR) *d++ ^= ~v; else /* Replace */ *d++ = v; } /* Right byte */ s0 = s1; s1 = (late ? 0 : *s); v = (((s0<<8) | s1)>>shift); if (op == JBIG2_COMPOSE_OR) *d |= v & rightmask; else if (op == JBIG2_COMPOSE_AND) *d &= (v & rightmask) | ~rightmask; else if (op == JBIG2_COMPOSE_XOR) *d ^= v & rightmask; else if (op == JBIG2_COMPOSE_XNOR) *d ^= ~v & rightmask; else /* Replace */ *d = (v & rightmask) | (*d & ~rightmask); dd += dstride; ss += sstride; } } } static void jbig2_image_compose_opt_OR(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride) { if (early || late) template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_OR); else template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_OR); } static void jbig2_image_compose_opt_AND(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride) { if (early || late) template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_AND); else template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_AND); } static void jbig2_image_compose_opt_XOR(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride) { if (early || late) template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XOR); else template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XOR); } static void jbig2_image_compose_opt_XNOR(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride) { if (early || late) template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XNOR); else template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_XNOR); } static void jbig2_image_compose_opt_REPLACE(const uint8_t *s, uint8_t *d, int early, int late, uint8_t mask, uint8_t rightmask, uint32_t bytewidth, uint32_t h, uint32_t shift, uint32_t dstride, uint32_t sstride) { if (early || late) template_image_compose_opt(s, d, early, late, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_REPLACE); else template_image_compose_opt(s, d, 0, 0, mask, rightmask, bytewidth, h, shift, dstride, sstride, JBIG2_COMPOSE_REPLACE); } /* composite one jbig2_image onto another */ int jbig2_image_compose(Jbig2Ctx *ctx, Jbig2Image *dst, Jbig2Image *src, int x, int y, Jbig2ComposeOp op) { uint32_t w, h; uint32_t shift; uint32_t leftbyte; uint8_t *ss; uint8_t *dd; uint8_t leftmask, rightmask; int early = x >= 0; int late; uint32_t bytewidth; uint32_t syoffset = 0; if (src == NULL) return 0; if ((UINT32_MAX - src->width < (x > 0 ? x : -x)) || (UINT32_MAX - src->height < (y > 0 ? y : -y))) { #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "overflow in compose_image"); #endif return 0; } /* This code takes a src image and combines it onto dst at offset (x,y), with operation op. */ /* Data is packed msb first within a byte, so with bits numbered: 01234567. * Second byte is: 89abcdef. So to combine into a run, we use: * (s[0]<<8) | s[1] == 0123456789abcdef. * To read from src into dst at offset 3, we need to read: * read: 0123456789abcdef... * write: 0123456798abcdef... * In general, to read from src and write into dst at offset x, we need to shift * down by (x&7) bits to allow for bit alignment. So shift = x&7. * So the 'central' part of our runs will see us doing: * *d++ op= ((s[0]<<8)|s[1])>>shift; * with special cases on the left and right edges of the run to mask. * With the left hand edge, we have to be careful not to 'underread' the start of * the src image; this is what the early flag is about. Similarly we have to be * careful not to read off the right hand edge; this is what the late flag is for. */ /* clip */ w = src->width; h = src->height; shift = (x & 7); ss = src->data - early; if (x < 0) { if (w < (uint32_t) -x) w = 0; else w += x; ss += (-x-1)>>3; x = 0; } if (y < 0) { if (h < (uint32_t) -y) h = 0; else h += y; syoffset = -y * src->stride; y = 0; } if ((uint32_t)x + w > dst->width) { if (dst->width < (uint32_t)x) w = 0; else w = dst->width - x; } if ((uint32_t)y + h > dst->height) { if (dst->height < (uint32_t)y) h = 0; else h = dst->height - y; } #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "compositing %dx%d at (%d, %d) after clipping", w, h, x, y); #endif /* check for zero clipping region */ if ((w <= 0) || (h <= 0)) { #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "zero clipping region"); #endif return 0; } leftbyte = (uint32_t) x >> 3; dd = dst->data + y * dst->stride + leftbyte; bytewidth = (((uint32_t) x + w - 1) >> 3) - leftbyte + 1; leftmask = 255>>(x&7); rightmask = (((x+w)&7) == 0) ? 255 : ~(255>>((x+w)&7)); if (bytewidth == 1) leftmask &= rightmask; late = (ss + bytewidth >= src->data + ((src->width+7)>>3)); ss += syoffset; switch(op) { case JBIG2_COMPOSE_OR: jbig2_image_compose_opt_OR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride); break; case JBIG2_COMPOSE_AND: jbig2_image_compose_opt_AND(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride); break; case JBIG2_COMPOSE_XOR: jbig2_image_compose_opt_XOR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride); break; case JBIG2_COMPOSE_XNOR: jbig2_image_compose_opt_XNOR(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride); break; case JBIG2_COMPOSE_REPLACE: jbig2_image_compose_opt_REPLACE(ss, dd, early, late, leftmask, rightmask, bytewidth, h, shift, dst->stride, src->stride); break; } return 0; } /* initialize an image bitmap to a constant value */ void jbig2_image_clear(Jbig2Ctx *ctx, Jbig2Image *image, int value) { const uint8_t fill = value ? 0xFF : 0x00; memset(image->data, fill, image->stride * image->height); } /* look up a pixel value in an image. returns 0 outside the image frame for the convenience of the template code */ int jbig2_image_get_pixel(Jbig2Image *image, int x, int y) { const int w = image->width; const int h = image->height; const int byte = (x >> 3) + y * image->stride; const int bit = 7 - (x & 7); if ((x < 0) || (x >= w)) return 0; if ((y < 0) || (y >= h)) return 0; return ((image->data[byte] >> bit) & 1); } /* set an individual pixel value in an image */ void jbig2_image_set_pixel(Jbig2Image *image, int x, int y, bool value) { const int w = image->width; const int h = image->height; int scratch, mask; int bit, byte; if ((x < 0) || (x >= w)) return; if ((y < 0) || (y >= h)) return; byte = (x >> 3) + y * image->stride; bit = 7 - (x & 7); mask = (1 << bit) ^ 0xff; scratch = image->data[byte] & mask; image->data[byte] = scratch | (value << bit); } jbig2dec-0.18/jbig2_image.h000066400000000000000000000027711362055302000154260ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_IMAGE_H #define _JBIG2_IMAGE_H typedef enum { JBIG2_COMPOSE_OR = 0, JBIG2_COMPOSE_AND = 1, JBIG2_COMPOSE_XOR = 2, JBIG2_COMPOSE_XNOR = 3, JBIG2_COMPOSE_REPLACE = 4 } Jbig2ComposeOp; Jbig2Image *jbig2_image_new(Jbig2Ctx *ctx, uint32_t width, uint32_t height); void jbig2_image_release(Jbig2Ctx *ctx, Jbig2Image *image); Jbig2Image *jbig2_image_reference(Jbig2Ctx *ctx, Jbig2Image *image); void jbig2_image_free(Jbig2Ctx *ctx, Jbig2Image *image); void jbig2_image_clear(Jbig2Ctx *ctx, Jbig2Image *image, int value); Jbig2Image *jbig2_image_resize(Jbig2Ctx *ctx, Jbig2Image *image, uint32_t width, uint32_t height, int value); int jbig2_image_compose(Jbig2Ctx *ctx, Jbig2Image *dst, Jbig2Image *src, int x, int y, Jbig2ComposeOp op); int jbig2_image_get_pixel(Jbig2Image *image, int x, int y); void jbig2_image_set_pixel(Jbig2Image *image, int x, int y, bool value); #endif /* _JBIG2_IMAGE_H */ jbig2dec-0.18/jbig2_image_pbm.c000066400000000000000000000103111362055302000162440ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_image.h" #include "jbig2_image_rw.h" /* take an image structure and write it to a file in pbm format */ int jbig2_image_write_pbm_file(Jbig2Image *image, char *filename) { FILE *out; int code; if ((out = fopen(filename, "wb")) == NULL) { fprintf(stderr, "unable to open '%s' for writing", filename); return 1; } code = jbig2_image_write_pbm(image, out); fclose(out); return (code); } /* write out an image struct as a pbm stream to an open file pointer */ int jbig2_image_write_pbm(Jbig2Image *image, FILE *out) { /* pbm header */ fprintf(out, "P4\n%d %d\n", image->width, image->height); /* pbm format pads to a byte boundary, so we can just write out the whole data buffer NB: this assumes minimal stride for the width */ fwrite(image->data, 1, image->height * image->stride, out); /* success */ return 0; } /* take an image from a file in pbm format */ Jbig2Image * jbig2_image_read_pbm_file(Jbig2Ctx *ctx, char *filename) { FILE *in; Jbig2Image *image; if ((in = fopen(filename, "rb")) == NULL) { fprintf(stderr, "unable to open '%s' for reading\n", filename); return NULL; } image = jbig2_image_read_pbm(ctx, in); fclose(in); return (image); } /* FIXME: should handle multi-image files */ Jbig2Image * jbig2_image_read_pbm(Jbig2Ctx *ctx, FILE *in) { int i, dim[2]; int done; Jbig2Image *image; int c; char buf[32]; /* look for 'P4' magic */ while ((c = fgetc(in)) != 'P') { if (feof(in)) return NULL; } if ((c = fgetc(in)) != '4') { fprintf(stderr, "not a binary pbm file.\n"); return NULL; } /* read size. we must find two decimal numbers representing the image dimensions. 'done' will index whether we're looking for the width or the height and 'i' will be our array index for copying strings into our buffer */ done = 0; i = 0; while (done < 2) { c = fgetc(in); /* skip whitespace */ if (c == ' ' || c == '\t' || c == '\r' || c == '\n') continue; /* skip comments */ if (c == '#') { while ((c = fgetc(in)) != '\n'); continue; } /* report unexpected eof */ if (c == EOF) { fprintf(stderr, "end-of-file parsing pbm header\n"); return NULL; } if (isdigit(c)) { buf[i++] = c; while (isdigit(c = fgetc(in))) { if (i >= 32) { fprintf(stderr, "pbm parsing error\n"); return NULL; } buf[i++] = c; } buf[i] = '\0'; if (sscanf(buf, "%d", &dim[done]) != 1) { fprintf(stderr, "failed to read pbm image dimensions\n"); return NULL; } i = 0; done++; } } /* allocate image structure */ image = jbig2_image_new(ctx, dim[0], dim[1]); if (image == NULL) { fprintf(stderr, "failed to allocate %dx%d image for pbm file\n", dim[0], dim[1]); return NULL; } /* the pbm data is byte-aligned, so we can do a simple block read */ (void)fread(image->data, 1, image->height * image->stride, in); if (feof(in)) { fprintf(stderr, "unexpected end of pbm file.\n"); jbig2_image_release(ctx, image); return NULL; } /* success */ return image; } jbig2dec-0.18/jbig2_image_png.c000066400000000000000000000074161362055302000162660ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include #include #ifndef OLD_LIB_PNG # if PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR < 2 # define OLD_LIB_PNG 1 # else # define OLD_LIB_PNG 0 # endif #endif #if OLD_LIB_PNG #include #endif #define CVT_PTR(ptr) (ptr) #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_image.h" /* take an image structure and write it out in png format */ static void jbig2_png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { png_size_t check; #if OLD_LIB_PNG png_FILE_p f = (png_FILE_p) png_ptr->io_ptr; #else png_FILE_p f = (png_FILE_p) png_get_io_ptr(png_ptr); #endif check = fwrite(data, 1, length, f); if (check != length) { png_error(png_ptr, "write error"); } } static void jbig2_png_flush(png_structp png_ptr) { #if OLD_LIB_PNG png_FILE_p f = (png_FILE_p) png_ptr->io_ptr; #else png_FILE_p f = (png_FILE_p) png_get_io_ptr(png_ptr); #endif if (f != NULL) fflush(f); } /* write out an image struct in png format to an open file pointer */ int jbig2_image_write_png(Jbig2Image *image, FILE *out) { uint32_t i; png_structp png; png_infop info; png_bytep rowpointer; png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png == NULL) { fprintf(stderr, "unable to create png structure\n"); return 2; } info = png_create_info_struct(png); if (info == NULL) { fprintf(stderr, "unable to create png info structure\n"); png_destroy_write_struct(&png, (png_infopp) NULL); return 3; } /* set/check error handling */ if (setjmp(png_jmpbuf(png))) { /* we've returned here after an internal error */ fprintf(stderr, "internal error in libpng saving file\n"); png_destroy_write_struct(&png, &info); return 4; } /* png_init_io() doesn't work linking dynamically to libpng on win32 one has to either link statically or use callbacks because of runtime variations */ /* png_init_io(png, out); */ png_set_write_fn(png, (png_voidp) out, jbig2_png_write_data, jbig2_png_flush); /* now we fill out the info structure with our format data */ png_set_IHDR(png, info, image->width, image->height, 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png, info); /* png natively treats 0 as black. This will convert for us */ png_set_invert_mono(png); /* write out each row in turn */ rowpointer = (png_bytep) image->data; for (i = 0; i < image->height; i++) { png_write_row(png, rowpointer); rowpointer += image->stride; } /* finish and clean up */ png_write_end(png, info); png_destroy_write_struct(&png, &info); return 0; } int jbig2_image_write_png_file(Jbig2Image *image, char *filename) { FILE *out; int code; if ((out = fopen(filename, "wb")) == NULL) { fprintf(stderr, "unable to open '%s' for writing\n", filename); return 1; } code = jbig2_image_write_png(image, out); fclose(out); return (code); } jbig2dec-0.18/jbig2_image_rw.h000066400000000000000000000022211362055302000161240ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_IMAGE_RW_H #define _JBIG2_IMAGE_RW_H /* routines for dumping the image data in various formats */ #include int jbig2_image_write_pbm_file(Jbig2Image *image, char *filename); int jbig2_image_write_pbm(Jbig2Image *image, FILE *out); Jbig2Image *jbig2_image_read_pbm_file(Jbig2Ctx *ctx, char *filename); Jbig2Image *jbig2_image_read_pbm(Jbig2Ctx *ctx, FILE *in); #ifdef HAVE_LIBPNG int jbig2_image_write_png_file(Jbig2Image *image, char *filename); int jbig2_image_write_png(Jbig2Image *image, FILE *out); #endif #endif /* _JBIG2_IMAGE_RW_H */ jbig2dec-0.18/jbig2_mmr.c000066400000000000000000000644471362055302000151420ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* An implementation of MMR decoding. This is based on the implementation in Fitz, which in turn is based on the one in Ghostscript. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include #include #include #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_generic.h" #include "jbig2_image.h" #include "jbig2_mmr.h" #include "jbig2_segment.h" #if !defined (UINT32_MAX) #define UINT32_MAX 0xffffffff #endif typedef struct { uint32_t width; uint32_t height; const byte *data; size_t size; uint32_t data_index; uint32_t bit_index; uint32_t word; } Jbig2MmrCtx; #define MINUS1 UINT32_MAX #define ERROR -1 #define ZEROES -2 #define UNCOMPRESSED -3 static void jbig2_decode_mmr_init(Jbig2MmrCtx *mmr, int width, int height, const byte *data, size_t size) { size_t i; uint32_t word = 0; mmr->width = width; mmr->height = height; mmr->data = data; mmr->size = size; mmr->data_index = 0; mmr->bit_index = 0; for (i = 0; i < size && i < 4; i++) word |= (data[i] << ((3 - i) << 3)); mmr->word = word; } static void jbig2_decode_mmr_consume(Jbig2MmrCtx *mmr, int n_bits) { mmr->word <<= n_bits; mmr->bit_index += n_bits; while (mmr->bit_index >= 8) { mmr->bit_index -= 8; if (mmr->data_index + 4 < mmr->size) mmr->word |= (mmr->data[mmr->data_index + 4] << mmr->bit_index); mmr->data_index++; } } /* the first 2^(initialbits) entries map bit patterns to decodes let's say initial_bits is 8 for the sake of example and that the code is 1001 that means that entries 0x90 .. 0x9f have the entry { val, 4 } because those are all the bytes that start with the code and the 4 is the length of the code ... if (n_bits > initial_bits) ... anyway, in that case, it basically points to a mini table the n_bits is the maximum length of all codes beginning with that byte so 2^(n_bits - initial_bits) is the size of the mini-table peter came up with this, and it makes sense */ typedef struct { short val; short n_bits; } mmr_table_node; /* white decode table (runlength huffman codes) */ const mmr_table_node jbig2_mmr_white_decode[] = { {256, 12}, {272, 12}, {29, 8}, {30, 8}, {45, 8}, {46, 8}, {22, 7}, {22, 7}, {23, 7}, {23, 7}, {47, 8}, {48, 8}, {13, 6}, {13, 6}, {13, 6}, {13, 6}, {20, 7}, {20, 7}, {33, 8}, {34, 8}, {35, 8}, {36, 8}, {37, 8}, {38, 8}, {19, 7}, {19, 7}, {31, 8}, {32, 8}, {1, 6}, {1, 6}, {1, 6}, {1, 6}, {12, 6}, {12, 6}, {12, 6}, {12, 6}, {53, 8}, {54, 8}, {26, 7}, {26, 7}, {39, 8}, {40, 8}, {41, 8}, {42, 8}, {43, 8}, {44, 8}, {21, 7}, {21, 7}, {28, 7}, {28, 7}, {61, 8}, {62, 8}, {63, 8}, {0, 8}, {320, 8}, {384, 8}, {10, 5}, {10, 5}, {10, 5}, {10, 5}, {10, 5}, {10, 5}, {10, 5}, {10, 5}, {11, 5}, {11, 5}, {11, 5}, {11, 5}, {11, 5}, {11, 5}, {11, 5}, {11, 5}, {27, 7}, {27, 7}, {59, 8}, {60, 8}, {288, 9}, {290, 9}, {18, 7}, {18, 7}, {24, 7}, {24, 7}, {49, 8}, {50, 8}, {51, 8}, {52, 8}, {25, 7}, {25, 7}, {55, 8}, {56, 8}, {57, 8}, {58, 8}, {192, 6}, {192, 6}, {192, 6}, {192, 6}, {1664, 6}, {1664, 6}, {1664, 6}, {1664, 6}, {448, 8}, {512, 8}, {292, 9}, {640, 8}, {576, 8}, {294, 9}, {296, 9}, {298, 9}, {300, 9}, {302, 9}, {256, 7}, {256, 7}, {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, 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}, {128, 5}, {128, 5}, {128, 5}, {128, 5}, {128, 5}, {128, 5}, {128, 5}, {128, 5}, {8, 5}, {8, 5}, {8, 5}, {8, 5}, {8, 5}, {8, 5}, {8, 5}, {8, 5}, {9, 5}, {9, 5}, {9, 5}, {9, 5}, {9, 5}, {9, 5}, {9, 5}, {9, 5}, {16, 6}, {16, 6}, {16, 6}, {16, 6}, {17, 6}, {17, 6}, {17, 6}, {17, 6}, {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, 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}, {14, 6}, {14, 6}, {14, 6}, {14, 6}, {15, 6}, {15, 6}, {15, 6}, {15, 6}, {64, 5}, {64, 5}, {64, 5}, {64, 5}, {64, 5}, {64, 5}, {64, 5}, {64, 5}, {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, 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}, {-2, 3}, {-2, 3}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-3, 4}, {1792, 3}, {1792, 3}, {1984, 4}, {2048, 4}, {2112, 4}, {2176, 4}, {2240, 4}, {2304, 4}, {1856, 3}, {1856, 3}, {1920, 3}, {1920, 3}, {2368, 4}, {2432, 4}, {2496, 4}, {2560, 4}, {1472, 1}, {1536, 1}, {1600, 1}, {1728, 1}, {704, 1}, {768, 1}, {832, 1}, {896, 1}, {960, 1}, {1024, 1}, {1088, 1}, {1152, 1}, {1216, 1}, {1280, 1}, {1344, 1}, {1408, 1} }; /* black decode table (runlength huffman codes) */ const mmr_table_node jbig2_mmr_black_decode[] = { {128, 12}, {160, 13}, {224, 12}, {256, 12}, {10, 7}, {11, 7}, {288, 12}, {12, 7}, {9, 6}, {9, 6}, {8, 6}, {8, 6}, {7, 5}, {7, 5}, {7, 5}, {7, 5}, {6, 4}, {6, 4}, {6, 4}, {6, 4}, {6, 4}, {6, 4}, {6, 4}, {6, 4}, {5, 4}, {5, 4}, {5, 4}, {5, 4}, {5, 4}, {5, 4}, {5, 4}, {5, 4}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 3}, {1, 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}, {3, 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}, {3, 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}, {3, 2}, {3, 2}, {3, 2}, {3, 2}, {3, 2}, {3, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {2, 2}, {-2, 4}, {-2, 4}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0}, {-3, 5}, {1792, 4}, {1792, 4}, {1984, 5}, {2048, 5}, {2112, 5}, {2176, 5}, {2240, 5}, {2304, 5}, {1856, 4}, {1856, 4}, {1920, 4}, {1920, 4}, {2368, 5}, {2432, 5}, {2496, 5}, {2560, 5}, {18, 3}, {18, 3}, {18, 3}, {18, 3}, {18, 3}, {18, 3}, {18, 3}, {18, 3}, {52, 5}, {52, 5}, {640, 6}, {704, 6}, {768, 6}, {832, 6}, {55, 5}, {55, 5}, {56, 5}, {56, 5}, {1280, 6}, {1344, 6}, {1408, 6}, {1472, 6}, {59, 5}, {59, 5}, {60, 5}, {60, 5}, {1536, 6}, {1600, 6}, {24, 4}, {24, 4}, {24, 4}, {24, 4}, {25, 4}, {25, 4}, {25, 4}, {25, 4}, {1664, 6}, {1728, 6}, {320, 5}, {320, 5}, {384, 5}, {384, 5}, {448, 5}, {448, 5}, {512, 6}, {576, 6}, {53, 5}, {53, 5}, {54, 5}, {54, 5}, {896, 6}, {960, 6}, {1024, 6}, {1088, 6}, {1152, 6}, {1216, 6}, {64, 3}, {64, 3}, {64, 3}, {64, 3}, {64, 3}, {64, 3}, {64, 3}, {64, 3}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {13, 1}, {23, 4}, {23, 4}, {50, 5}, {51, 5}, {44, 5}, {45, 5}, {46, 5}, {47, 5}, {57, 5}, {58, 5}, {61, 5}, {256, 5}, {16, 3}, {16, 3}, {16, 3}, {16, 3}, {17, 3}, {17, 3}, {17, 3}, {17, 3}, {48, 5}, {49, 5}, {62, 5}, {63, 5}, {30, 5}, {31, 5}, {32, 5}, {33, 5}, {40, 5}, {41, 5}, {22, 4}, {22, 4}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {14, 1}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {15, 2}, {128, 5}, {192, 5}, {26, 5}, {27, 5}, {28, 5}, {29, 5}, {19, 4}, {19, 4}, {20, 4}, {20, 4}, {34, 5}, {35, 5}, {36, 5}, {37, 5}, {38, 5}, {39, 5}, {21, 4}, {21, 4}, {42, 5}, {43, 5}, {0, 3}, {0, 3}, {0, 3}, {0, 3} }; #define getbit(buf, x) ( ( buf[x >> 3] >> ( 7 - (x & 7) ) ) & 1 ) static uint32_t jbig2_find_changing_element(const byte *line, uint32_t x, uint32_t w) { int a; uint8_t all8; uint16_t all16; uint32_t all32; if (line == NULL) return w; if (x == MINUS1) { a = 0; x = 0; } else if (x < w) { a = getbit(line, x); x++; } else { return x; } /* We will be looking for a uint8 or uint16 or uint32 that has at least one bit different from , so prepare some useful values for comparison. */ all8 = (a) ? 0xff : 0; all16 = (a) ? 0xffff : 0; all32 = (a) ? 0xffffffff : 0; /* Check individual bits up to next 8-bit boundary. [Would it be worth looking at top 4 bits, then at 2 bits then at 1 bit, instead of iterating over all 8 bits? */ if ( ((uint8_t*) line)[ x / 8] == all8) { /* Don't bother checking individual bits if the enclosing uint8 equals all8 - just move to the next byte. */ x = x / 8 * 8 + 8; if (x >= w) { x = w; goto end; } } else { for(;;) { if (x == w) { goto end; } if (x % 8 == 0) { break; } if (getbit(line, x) != a) { goto end; } x += 1; } } assert(x % 8 == 0); /* Check next uint8 if we are not on 16-bit boundary. */ if (x % 16) { if (w - x < 8) { goto check1; } if ( ((uint8_t*) line)[ x / 8] != all8) { goto check1; } x += 8; /* This will make x a multiple of 16. */ } assert(x % 16 == 0); /* Check next uint16 if we are not on 32-bit boundary. */ if (x % 32) { if (w - x < 16) { goto check8; } if ( ((uint16_t*) line)[ x / 16] != all16) { goto check8_no_eof; } x += 16; /* This will make x a multiple of 32. */ } /* We are now on a 32-bit boundary. Check uint32's until we reach last sub-32-bit region. */ assert(x % 32 == 0); for(;;) { if (w - x < 32) { /* We could still look at the uint32 here - if it equals all32, we know there is no match before so could do {x = w; goto end;}. But for now we simply fall into the epilogue checking, which will look at the next uint16, then uint8, then last 8 bits. */ goto check16; } if (((uint32_t*) line)[x/32] != all32) { goto check16_no_eof; } x += 32; } /* Check next uint16. */ check16: assert(x % 16 == 0); if (w - x < 16) { goto check8; } check16_no_eof: assert(w - x >= 16); if ( ((uint16_t*) line)[x/16] != all16) { goto check8_no_eof; } x += 16; /* Check next uint8. */ check8: assert(x % 8 == 0); if (w - x < 8) { goto check1; } check8_no_eof: assert(w - x >= 8); if ( ((uint8_t*) line)[x/8] != all8) { goto check1; } x += 8; /* Check up to the next 8 bits. */ check1: assert(x % 8 == 0); if ( ((uint8_t*) line)[ x / 8] == all8) { x = w; goto end; } { for(;;) { if (x == w) { goto end; } if (getbit(line, x) != a) { goto end; } x += 1; } } end: return x; } static uint32_t jbig2_find_changing_element_of_color(const byte *line, uint32_t x, uint32_t w, int color) { if (line == NULL) return w; x = jbig2_find_changing_element(line, x, w); if (x < w && getbit(line, x) != color) x = jbig2_find_changing_element(line, x, w); return x; } static const byte lm[8] = { 0xFF, 0x7F, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01 }; static const byte rm[8] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE }; static void jbig2_set_bits(byte *line, uint32_t x0, uint32_t x1) { uint32_t a0, a1, b0, b1, a; a0 = x0 >> 3; a1 = x1 >> 3; b0 = x0 & 7; b1 = x1 & 7; if (a0 == a1) { line[a0] |= lm[b0] & rm[b1]; } else { line[a0] |= lm[b0]; for (a = a0 + 1; a < a1; a++) line[a] = 0xFF; if (b1) line[a1] |= rm[b1]; } } static int jbig2_decode_get_code(Jbig2MmrCtx *mmr, const mmr_table_node *table, int initial_bits) { uint32_t word = mmr->word; int table_ix = word >> (32 - initial_bits); int val = table[table_ix].val; int n_bits = table[table_ix].n_bits; if (n_bits > initial_bits) { int mask = (1 << (32 - initial_bits)) - 1; table_ix = val + ((word & mask) >> (32 - n_bits)); val = table[table_ix].val; n_bits = initial_bits + table[table_ix].n_bits; } jbig2_decode_mmr_consume(mmr, n_bits); return val; } static int jbig2_decode_get_run(Jbig2Ctx *ctx, Jbig2MmrCtx *mmr, const mmr_table_node *table, int initial_bits) { int result = 0; int val; do { val = jbig2_decode_get_code(mmr, table, initial_bits); if (val == ERROR) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "invalid code detected in MMR-coded data"); else if (val == UNCOMPRESSED) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "uncompressed code in MMR-coded data"); else if (val == ZEROES) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "zeroes code in MMR-coded data"); result += val; } while (val >= 64); return result; } static int jbig2_decode_mmr_line(Jbig2Ctx *ctx, Jbig2MmrCtx *mmr, const byte *ref, byte *dst, int *eofb) { uint32_t a0 = MINUS1; uint32_t a1, a2, b1, b2; int c = 0; /* 0 is white, black is 1 */ while (1) { uint32_t word = mmr->word; /* printf ("%08x\n", word); */ if (a0 != MINUS1 && a0 >= mmr->width) break; if ((word >> (32 - 3)) == 1) { int white_run, black_run; jbig2_decode_mmr_consume(mmr, 3); if (a0 == MINUS1) a0 = 0; if (c == 0) { white_run = jbig2_decode_get_run(ctx, mmr, jbig2_mmr_white_decode, 8); if (white_run < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to decode white H run"); black_run = jbig2_decode_get_run(ctx, mmr, jbig2_mmr_black_decode, 7); if (black_run < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to decode black H run"); /* printf ("H %d %d\n", white_run, black_run); */ a1 = a0 + white_run; a2 = a1 + black_run; if (a1 > mmr->width) a1 = mmr->width; if (a2 > mmr->width) a2 = mmr->width; if (a2 < a1) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative black H run"); a2 = a1; } if (a1 < mmr->width) jbig2_set_bits(dst, a1, a2); a0 = a2; } else { black_run = jbig2_decode_get_run(ctx, mmr, jbig2_mmr_black_decode, 7); if (black_run < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to decode black H run"); white_run = jbig2_decode_get_run(ctx, mmr, jbig2_mmr_white_decode, 8); if (white_run < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to decode white H run"); /* printf ("H %d %d\n", black_run, white_run); */ a1 = a0 + black_run; a2 = a1 + white_run; if (a1 > mmr->width) a1 = mmr->width; if (a2 > mmr->width) a2 = mmr->width; if (a1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative white H run"); a1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, a1); a0 = a2; } } else if ((word >> (32 - 4)) == 1) { /* printf ("P\n"); */ jbig2_decode_mmr_consume(mmr, 4); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); b2 = jbig2_find_changing_element(ref, b1, mmr->width); if (c) { if (b2 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative P run"); b2 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b2); } a0 = b2; } else if ((word >> (32 - 1)) == 1) { /* printf ("V(0)\n"); */ jbig2_decode_mmr_consume(mmr, 1); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); if (c) { if (b1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative V(0) run"); b1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b1); } a0 = b1; c = !c; } else if ((word >> (32 - 3)) == 3) { /* printf ("VR(1)\n"); */ jbig2_decode_mmr_consume(mmr, 3); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); if (b1 + 1 <= mmr->width) b1 += 1; if (c) { if (b1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative VR(1) run"); b1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b1); } a0 = b1; c = !c; } else if ((word >> (32 - 6)) == 3) { /* printf ("VR(2)\n"); */ jbig2_decode_mmr_consume(mmr, 6); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); if (b1 + 2 <= mmr->width) b1 += 2; if (c) { if (b1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative VR(2) run"); b1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b1); } a0 = b1; c = !c; } else if ((word >> (32 - 7)) == 3) { /* printf ("VR(3)\n"); */ jbig2_decode_mmr_consume(mmr, 7); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); if (b1 + 3 <= mmr->width) b1 += 3; if (c) { if (b1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative VR(3) run"); b1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b1); } a0 = b1; c = !c; } else if ((word >> (32 - 3)) == 2) { /* printf ("VL(1)\n"); */ jbig2_decode_mmr_consume(mmr, 3); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); if (b1 >= 1) b1 -= 1; if (c) { if (b1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative VL(1) run"); b1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b1); } a0 = b1; c = !c; } else if ((word >> (32 - 6)) == 2) { /* printf ("VL(2)\n"); */ jbig2_decode_mmr_consume(mmr, 6); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); if (b1 >= 2) b1 -= 2; if (c) { if (b1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative VL(2) run"); b1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b1); } a0 = b1; c = !c; } else if ((word >> (32 - 7)) == 2) { /* printf ("VL(3)\n"); */ jbig2_decode_mmr_consume(mmr, 7); b1 = jbig2_find_changing_element_of_color(ref, a0, mmr->width, !c); if (b1 >= 3) b1 -= 3; if (c) { if (b1 < a0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "ignoring negative VL(3) run"); b1 = a0; } if (a0 < mmr->width) jbig2_set_bits(dst, a0, b1); } a0 = b1; c = !c; } else if ((word >> (32 - 24)) == 0x1001) { /* printf ("EOFB\n"); */ jbig2_decode_mmr_consume(mmr, 24); *eofb = 1; break; } else break; } return 0; } int jbig2_decode_generic_mmr(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, const byte *data, size_t size, Jbig2Image *image) { Jbig2MmrCtx mmr; const uint32_t rowstride = image->stride; byte *dst = image->data; byte *ref = NULL; uint32_t y; int code = 0; int eofb = 0; jbig2_decode_mmr_init(&mmr, image->width, image->height, data, size); for (y = 0; !eofb && y < image->height; y++) { memset(dst, 0, rowstride); code = jbig2_decode_mmr_line(ctx, &mmr, ref, dst, &eofb); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode mmr line"); ref = dst; dst += rowstride; } if (eofb && y < image->height) { memset(dst, 0, rowstride * (image->height - y)); } return code; } /** * jbig2_decode_halftone_mmr: decode mmr region inside of halftones * * @ctx: jbig2 decoder context * @params: parameters for decoding * @data: pointer to text region data to be decoded * @size: length of text region data * @image: return of decoded image * @consumed_bytes: return of consumed bytes from @data * * MMR decoding that consumes EOFB and returns consumed bytes (@consumed_bytes) * * returns: 0 **/ int jbig2_decode_halftone_mmr(Jbig2Ctx *ctx, const Jbig2GenericRegionParams *params, const byte *data, size_t size, Jbig2Image *image, size_t *consumed_bytes) { Jbig2MmrCtx mmr; const uint32_t rowstride = image->stride; byte *dst = image->data; byte *ref = NULL; uint32_t y; int code = 0; const uint32_t EOFB = 0x001001; int eofb = 0; jbig2_decode_mmr_init(&mmr, image->width, image->height, data, size); for (y = 0; !eofb && y < image->height; y++) { memset(dst, 0, rowstride); code = jbig2_decode_mmr_line(ctx, &mmr, ref, dst, &eofb); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to decode halftone mmr line"); ref = dst; dst += rowstride; } if (eofb && y < image->height) { memset(dst, 0, rowstride * (image->height - y)); } /* test for EOFB (see section 6.2.6) */ if (mmr.word >> 8 == EOFB) { jbig2_decode_mmr_consume(&mmr, 24); } *consumed_bytes += mmr.data_index + (mmr.bit_index >> 3) + (mmr.bit_index > 0 ? 1 : 0); return code; } jbig2dec-0.18/jbig2_mmr.h000066400000000000000000000017331362055302000151340ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_MMR_H #define _JBIG2_MMR_H int jbig2_decode_generic_mmr(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2GenericRegionParams *params, const byte *data, size_t size, Jbig2Image *image); int jbig2_decode_halftone_mmr(Jbig2Ctx *ctx, const Jbig2GenericRegionParams *params, const byte *data, size_t size, Jbig2Image *image, size_t *consumed_bytes); #endif /* _JBIG2_MMR_H */ jbig2dec-0.18/jbig2_page.c000066400000000000000000000315741362055302000152560ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #ifdef OUTPUT_PBM #include #endif #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_image.h" #include "jbig2_page.h" #include "jbig2_segment.h" #if !defined (INT32_MAX) #define INT32_MAX 0x7fffffff #endif #if !defined (UINT32_MAX) #define UINT32_MAX 0xffffffff #endif /* dump the page struct info */ static void dump_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, Jbig2Page *page) { if (page->x_resolution == 0) { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (unknown res)", page->number, page->width, page->height); } else if (page->x_resolution == page->y_resolution) { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (%d ppm)", page->number, page->width, page->height, page->x_resolution); } else { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "page %d image is %dx%d (%dx%d ppm)", page->number, page->width, page->height, page->x_resolution, page->y_resolution); } if (page->striped) { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "\tmaximum stripe size: %d", page->stripe_size); } } /** * jbig2_page_info: parse page info segment * * Parse the page info segment data and fill out a corresponding * Jbig2Page struct and ready it for subsequent rendered data, * including allocating an image buffer for the page (or the first stripe) **/ int jbig2_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { Jbig2Page *page, *pages; /* a new page info segment implies the previous page is finished */ page = &(ctx->pages[ctx->current_page]); if (page->number != 0 && (page->state == JBIG2_PAGE_NEW || page->state == JBIG2_PAGE_FREE)) { page->state = JBIG2_PAGE_COMPLETE; jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unexpected page info segment, marking previous page finished"); } /* find a free page */ { int index, j; index = ctx->current_page; while (ctx->pages[index].state != JBIG2_PAGE_FREE) { index++; if (index >= ctx->max_page_index) { /* grow the list */ pages = jbig2_renew(ctx, ctx->pages, Jbig2Page, (ctx->max_page_index <<= 2)); if (pages == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to reallocate pages"); } ctx->pages = pages; for (j = index; j < ctx->max_page_index; j++) { ctx->pages[j].state = JBIG2_PAGE_FREE; ctx->pages[j].number = 0; ctx->pages[j].image = NULL; } } } page = &(ctx->pages[index]); ctx->current_page = index; page->state = JBIG2_PAGE_NEW; page->number = segment->page_association; } /* FIXME: would be nice if we tried to work around this */ if (segment->data_length < 19) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); } /* 7.4.8.x */ page->width = jbig2_get_uint32(segment_data); page->height = jbig2_get_uint32(segment_data + 4); page->x_resolution = jbig2_get_uint32(segment_data + 8); page->y_resolution = jbig2_get_uint32(segment_data + 12); page->flags = segment_data[16]; /* Check for T.88 amendment 3 */ if (page->flags & 0x80) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "page segment indicates use of color segments (NYI)"); /* 7.4.8.6 */ { int16_t striping = jbig2_get_int16(segment_data + 17); if (striping & 0x8000) { page->striped = TRUE; page->stripe_size = striping & 0x7FFF; } else { page->striped = FALSE; page->stripe_size = 0; /* would page->height be better? */ } } if (page->height == 0xFFFFFFFF && page->striped == FALSE) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "height is unspecified but page is not marked as striped, assuming striped with maximum strip size"); page->striped = TRUE; page->stripe_size = 0x7FFF; } page->end_row = 0; if (segment->data_length > 19) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "extra data in segment"); } dump_page_info(ctx, segment, page); /* allocate an appropriate page image buffer */ /* 7.4.8.2 */ if (page->height == 0xFFFFFFFF) { page->image = jbig2_image_new(ctx, page->width, page->stripe_size); } else { page->image = jbig2_image_new(ctx, page->width, page->height); } if (page->image == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate buffer for page image"); } else { /* 8.2 (3) fill the page with the default pixel value */ jbig2_image_clear(ctx, page->image, (page->flags & 4)); jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "allocated %dx%d page image (%d bytes)", page->image->width, page->image->height, page->image->stride * page->image->height); } return 0; } /** * jbig2_end_of_stripe: parse and implement an end of stripe segment **/ int jbig2_end_of_stripe(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { Jbig2Page *page = &ctx->pages[ctx->current_page]; uint32_t end_row; if (segment->data_length < 4) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); end_row = jbig2_get_uint32(segment_data); if (end_row < page->end_row) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "end of stripe segment with non-positive end row advance (new end row %d vs current end row %d)", end_row, page->end_row); } else { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of stripe: advancing end row from %u to %u", page->end_row, end_row); } page->end_row = end_row; return 0; } /** * jbig2_complete_page: complete a page image * * called upon seeing an 'end of page' segment, this routine * marks a page as completed so it can be returned. * compositing will have already happened in the previous * segment handlers. **/ int jbig2_complete_page(Jbig2Ctx *ctx) { int code; /* check for unfinished segments */ if (ctx->segment_index != ctx->n_segments) { Jbig2Segment *segment = ctx->segments[ctx->segment_index]; /* Some versions of Xerox Workcentre generate PDF files with the segment data length field of the last segment set to -1. Try to cope with this here. */ if ((segment->data_length & 0xffffffff) == 0xffffffff) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "file has an invalid segment data length; trying to decode using the available data"); segment->data_length = ctx->buf_wr_ix - ctx->buf_rd_ix; code = jbig2_parse_segment(ctx, segment, ctx->buf + ctx->buf_rd_ix); ctx->buf_rd_ix += segment->data_length; ctx->segment_index++; if (code < 0) { return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to parse segment"); } } } /* ensure image exists before marking page as complete */ if (ctx->pages[ctx->current_page].image == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "page has no image, cannot be completed"); } ctx->pages[ctx->current_page].state = JBIG2_PAGE_COMPLETE; return 0; } /** * jbig2_end_of_page: parse and implement an end of page segment **/ int jbig2_end_of_page(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { uint32_t page_number = ctx->pages[ctx->current_page].number; int code; if (segment->page_association != page_number) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "end of page marker for page %d doesn't match current page number %d", segment->page_association, page_number); } jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of page %d", page_number); code = jbig2_complete_page(ctx); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to complete page"); #ifdef OUTPUT_PBM code = jbig2_image_write_pbm(ctx->pages[ctx->current_page].image, stdout); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to write page image"); #endif return 0; } /** * jbig2_add_page_result: composite a decoding result onto a page * * this is called to add the results of segment decode (when it * is an image) to a page image buffer **/ int jbig2_page_add_result(Jbig2Ctx *ctx, Jbig2Page *page, Jbig2Image *image, uint32_t x, uint32_t y, Jbig2ComposeOp op) { int code; if (x > INT32_MAX || y > INT32_MAX) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "unsupported image coordinates"); /* ensure image exists first */ if (page->image == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "page info possibly missing, no image defined"); /* grow the page to accommodate a new stripe if necessary */ if (page->striped && page->height == 0xFFFFFFFF) { uint32_t new_height; if (y > UINT32_MAX - image->height) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "adding image at coordinate would grow page out of bounds"); new_height = y + image->height; if (page->image->height < new_height) { Jbig2Image *resized_image = NULL; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "growing page buffer to %u rows to accommodate new stripe", new_height); resized_image = jbig2_image_resize(ctx, page->image, page->image->width, new_height, page->flags & 4); if (resized_image == NULL) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "unable to resize image to accommodate new stripe"); } page->image = resized_image; } } code = jbig2_image_compose(ctx, page->image, image, x, y, op); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to compose image with page"); return 0; } /** * jbig2_get_page: return the next available page image buffer * * the client can call this at any time to check if any pages * have been decoded. If so, it returns the first available * one. The client should then call jbig2_release_page() when * it no longer needs to refer to the image buffer. * * since this is a public routine for the library clients, we * return an image structure pointer, even though the function * name refers to a page; the page structure is private. **/ Jbig2Image * jbig2_page_out(Jbig2Ctx *ctx) { int index; /* search for a completed page */ for (index = 0; index < ctx->max_page_index; index++) { if (ctx->pages[index].state == JBIG2_PAGE_COMPLETE) { Jbig2Image *img = ctx->pages[index].image; uint32_t page_number = ctx->pages[index].number; if (img == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "page %d returned with no associated image", page_number); continue; } ctx->pages[index].state = JBIG2_PAGE_RETURNED; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "page %d returned to the client", page_number); return jbig2_image_reference(ctx, img); } } /* no pages available */ return NULL; } /** * jbig2_release_page: tell the library a page can be freed **/ void jbig2_release_page(Jbig2Ctx *ctx, Jbig2Image *image) { int index; if (image == NULL) return; /* find the matching page struct and mark it released */ for (index = 0; index < ctx->max_page_index; index++) { if (ctx->pages[index].image == image) { jbig2_image_release(ctx, image); ctx->pages[index].state = JBIG2_PAGE_RELEASED; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, -1, "page %d released by the client", ctx->pages[index].number); return; } } /* no matching pages */ jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to release unknown page"); } jbig2dec-0.18/jbig2_page.h000066400000000000000000000031771362055302000152610ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_PAGE_H #define _JBIG2_PAGE_H /* the page structure handles decoded page results. it's allocated by a 'page info' segment and marked complete by an 'end of page' segment. */ typedef enum { JBIG2_PAGE_FREE, JBIG2_PAGE_NEW, JBIG2_PAGE_COMPLETE, JBIG2_PAGE_RETURNED, JBIG2_PAGE_RELEASED } Jbig2PageState; struct _Jbig2Page { Jbig2PageState state; uint32_t number; uint32_t height, width; /* in pixels */ uint32_t x_resolution, y_resolution; /* in pixels per meter */ uint16_t stripe_size; bool striped; uint32_t end_row; uint8_t flags; Jbig2Image *image; }; int jbig2_page_info(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data); int jbig2_end_of_stripe(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data); int jbig2_end_of_page(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data); int jbig2_page_add_result(Jbig2Ctx *ctx, Jbig2Page *page, Jbig2Image *src, uint32_t x, uint32_t y, Jbig2ComposeOp op); #endif /* _JBIG2_PAGE_H */ jbig2dec-0.18/jbig2_priv.h000066400000000000000000000101441362055302000153150ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_PRIV_H #define _JBIG2_PRIV_H /* To enable Memento predefine MEMENTO while building by setting CFLAGS=-DMEMENTO. */ /* If we are being compiled as part of a larger project that includes * Memento, that project should define JBIG_EXTERNAL_MEMENTO_H to point * to the include file to use. */ #ifdef JBIG_EXTERNAL_MEMENTO_H #include JBIG_EXTERNAL_MEMENTO_H #else #include "memento.h" #endif /* library internals */ typedef uint8_t byte; #define bool int #ifdef __cplusplus #define template template_C #define new new_C #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifndef NULL #define NULL ((void*)0) #endif typedef struct _Jbig2Page Jbig2Page; typedef struct _Jbig2Segment Jbig2Segment; typedef enum { JBIG2_FILE_HEADER, JBIG2_FILE_SEQUENTIAL_HEADER, JBIG2_FILE_SEQUENTIAL_BODY, JBIG2_FILE_RANDOM_HEADERS, JBIG2_FILE_RANDOM_BODIES, JBIG2_FILE_EOF } Jbig2FileState; struct _Jbig2Ctx { Jbig2Allocator *allocator; Jbig2Options options; const Jbig2Ctx *global_ctx; Jbig2ErrorCallback error_callback; void *error_callback_data; byte *buf; size_t buf_size; unsigned int buf_rd_ix; unsigned int buf_wr_ix; Jbig2FileState state; uint8_t file_header_flags; uint32_t n_pages; int n_segments_max; Jbig2Segment **segments; int n_segments; /* index of last segment header parsed */ int segment_index; /* index of last segment body parsed */ /* list of decoded pages, including the one in progress, currently stored as a contiguous, 0-indexed array. */ int current_page; int max_page_index; Jbig2Page *pages; }; uint32_t jbig2_get_uint32(const byte *bptr); int32_t jbig2_get_int32(const byte *buf); uint16_t jbig2_get_uint16(const byte *bptr); int16_t jbig2_get_int16(const byte *buf); /* dynamic memory management */ void *jbig2_alloc(Jbig2Allocator *allocator, size_t size, size_t num); void jbig2_free(Jbig2Allocator *allocator, void *p); void *jbig2_realloc(Jbig2Allocator *allocator, void *p, size_t size, size_t num); #define jbig2_new(ctx, t, size) ((t *)jbig2_alloc(ctx->allocator, size, sizeof(t))) #define jbig2_renew(ctx, p, t, size) ((t *)jbig2_realloc(ctx->allocator, (p), size, sizeof(t))) int jbig2_error(Jbig2Ctx *ctx, Jbig2Severity severity, int32_t seg_idx, const char *fmt, ...) #ifdef __GNUC__ __attribute__ ((format (__printf__, 4, 5))) #endif ; /* The word stream design is a compromise between simplicity and trying to amortize the number of method calls. Each ::get_next_word invocation pulls 4 bytes from the stream, packed big-endian into a 32 bit word. The offset argument is provided as a convenience. It begins at 0 and increments by 4 for each successive invocation. */ typedef struct _Jbig2WordStream Jbig2WordStream; struct _Jbig2WordStream { int (*get_next_word)(Jbig2WordStream *self, size_t offset, uint32_t *word); }; Jbig2WordStream *jbig2_word_stream_buf_new(Jbig2Ctx *ctx, const byte *data, size_t size); void jbig2_word_stream_buf_free(Jbig2Ctx *ctx, Jbig2WordStream *ws); /* restrict is standard in C99, but not in all C++ compilers. */ #if defined (__STDC_VERSION_) && (__STDC_VERSION__ >= 199901L) /* C99 */ #define JBIG2_RESTRICT restrict #elif defined(_MSC_VER) && (_MSC_VER >= 1600) /* MSVC 10 or newer */ #define JBIG2_RESTRICT __restrict #elif defined(__GNUC__) && (__GNUC__ >= 3) /* GCC 3 or newer */ #define JBIG2_RESTRICT __restrict #else /* Unknown or ancient */ #define JBIG2_RESTRICT #endif #endif /* _JBIG2_PRIV_H */ jbig2dec-0.18/jbig2_refinement.c000066400000000000000000000553751362055302000165030ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /** * Generic Refinement region handlers. **/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include /* memcpy(), memset() */ #include #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_generic.h" #include "jbig2_image.h" #include "jbig2_page.h" #include "jbig2_refinement.h" #include "jbig2_segment.h" #define pixel_outside_field(x, y) \ ((y) < -128 || (y) > 0 || (x) < -128 || ((y) < 0 && (x) > 127) || ((y) == 0 && (x) >= 0)) #define refpixel_outside_field(x, y) \ ((y) < -128 || (y) > 127 || (x) < -128 || (x) > 127) static int jbig2_decode_refinement_template0_unopt(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2RefinementRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GR_stats) { const int GRW = image->width; const int GRH = image->height; Jbig2Image *ref = params->GRREFERENCE; const int dx = params->GRREFERENCEDX; const int dy = params->GRREFERENCEDY; uint32_t CONTEXT; int x, y; int bit; if (pixel_outside_field(params->grat[0], params->grat[1]) || refpixel_outside_field(params->grat[2], params->grat[3])) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "adaptive template pixel is out of field"); for (y = 0; y < GRH; y++) { for (x = 0; x < GRW; x++) { CONTEXT = 0; CONTEXT |= jbig2_image_get_pixel(image, x - 1, y + 0) << 0; CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 1; CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 2; CONTEXT |= jbig2_image_get_pixel(image, x + params->grat[0], y + params->grat[1]) << 3; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 1) << 4; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 1) << 5; CONTEXT |= jbig2_image_get_pixel(ref, x - dx - 1, y - dy + 1) << 6; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 0) << 7; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 0) << 8; CONTEXT |= jbig2_image_get_pixel(ref, x - dx - 1, y - dy + 0) << 9; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy - 1) << 10; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy - 1) << 11; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + params->grat[2], y - dy + params->grat[3]) << 12; bit = jbig2_arith_decode(as, &GR_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling refinement template0"); jbig2_image_set_pixel(image, x, y, bit); } } #ifdef JBIG2_DEBUG_DUMP { static count = 0; char name[32]; int code; snprintf(name, 32, "refin-%d.pbm", count); code = jbig2_image_write_pbm_file(ref, name); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed write refinement input"); snprintf(name, 32, "refout-%d.pbm", count); code = jbig2_image_write_pbm_file(image, name); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed write refinement output"); count++; } #endif return 0; } static int jbig2_decode_refinement_template1_unopt(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2RefinementRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GR_stats) { const int GRW = image->width; const int GRH = image->height; Jbig2Image *ref = params->GRREFERENCE; const int dx = params->GRREFERENCEDX; const int dy = params->GRREFERENCEDY; uint32_t CONTEXT; int x, y; int bit; for (y = 0; y < GRH; y++) { for (x = 0; x < GRW; x++) { CONTEXT = 0; CONTEXT |= jbig2_image_get_pixel(image, x - 1, y + 0) << 0; CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 1; CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 2; CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 3; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 1) << 4; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 1) << 5; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 0) << 6; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 0) << 7; CONTEXT |= jbig2_image_get_pixel(ref, x - dx - 1, y - dy + 0) << 8; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy - 1) << 9; bit = jbig2_arith_decode(as, &GR_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling refinement template0"); jbig2_image_set_pixel(image, x, y, bit); } } #ifdef JBIG2_DEBUG_DUMP { static count = 0; char name[32]; snprintf(name, 32, "refin-%d.pbm", count); code = jbig2_image_write_pbm_file(ref, name); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to write refinement input"); snprintf(name, 32, "refout-%d.pbm", count); code = jbig2_image_write_pbm_file(image, name); if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to write refinement output"); count++; } #endif return 0; } #if 0 /* currently not used */ static int jbig2_decode_refinement_template1(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2RefinementRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GR_stats) { const int GRW = image->width; const int GRH = image->height; const int stride = image->stride; const int refstride = params->reference->stride; const int dy = params->DY; byte *grreg_line = (byte *) image->data; byte *grref_line = (byte *) params->reference->data; int x, y; for (y = 0; y < GRH; y++) { const int padded_width = (GRW + 7) & -8; uint32_t CONTEXT; uint32_t refline_m1; /* previous line of the reference bitmap */ uint32_t refline_0; /* current line of the reference bitmap */ uint32_t refline_1; /* next line of the reference bitmap */ uint32_t line_m1; /* previous line of the decoded bitmap */ line_m1 = (y >= 1) ? grreg_line[-stride] : 0; refline_m1 = ((y - dy) >= 1) ? grref_line[(-1 - dy) * stride] << 2 : 0; refline_0 = (((y - dy) > 0) && ((y - dy) < GRH)) ? grref_line[(0 - dy) * stride] << 4 : 0; refline_1 = (y < GRH - 1) ? grref_line[(+1 - dy) * stride] << 7 : 0; CONTEXT = ((line_m1 >> 5) & 0x00e) | ((refline_1 >> 5) & 0x030) | ((refline_0 >> 5) & 0x1c0) | ((refline_m1 >> 5) & 0x200); for (x = 0; x < padded_width; x += 8) { byte result = 0; int x_minor; const int minor_width = GRW - x > 8 ? 8 : GRW - x; if (y >= 1) { line_m1 = (line_m1 << 8) | (x + 8 < GRW ? grreg_line[-stride + (x >> 3) + 1] : 0); refline_m1 = (refline_m1 << 8) | (x + 8 < GRW ? grref_line[-refstride + (x >> 3) + 1] << 2 : 0); } refline_0 = (refline_0 << 8) | (x + 8 < GRW ? grref_line[(x >> 3) + 1] << 4 : 0); if (y < GRH - 1) refline_1 = (refline_1 << 8) | (x + 8 < GRW ? grref_line[+refstride + (x >> 3) + 1] << 7 : 0); else refline_1 = 0; /* this is the speed critical inner-loop */ for (x_minor = 0; x_minor < minor_width; x_minor++) { int bit; bit = jbig2_arith_decode(as, &GR_stats[CONTEXT]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling refinement template1"); result |= bit << (7 - x_minor); CONTEXT = ((CONTEXT & 0x0d6) << 1) | bit | ((line_m1 >> (9 - x_minor)) & 0x002) | ((refline_1 >> (9 - x_minor)) & 0x010) | ((refline_0 >> (9 - x_minor)) & 0x040) | ((refline_m1 >> (9 - x_minor)) & 0x200); } grreg_line[x >> 3] = result; } grreg_line += stride; grref_line += refstride; } return 0; } #endif typedef uint32_t(*ContextBuilder)(const Jbig2RefinementRegionParams *, Jbig2Image *, int, int); static int implicit_value(const Jbig2RefinementRegionParams *params, Jbig2Image *image, int x, int y) { Jbig2Image *ref = params->GRREFERENCE; int i = x - params->GRREFERENCEDX; int j = y - params->GRREFERENCEDY; int m = jbig2_image_get_pixel(ref, i, j); return ((jbig2_image_get_pixel(ref, i - 1, j - 1) == m) && (jbig2_image_get_pixel(ref, i, j - 1) == m) && (jbig2_image_get_pixel(ref, i + 1, j - 1) == m) && (jbig2_image_get_pixel(ref, i - 1, j) == m) && (jbig2_image_get_pixel(ref, i + 1, j) == m) && (jbig2_image_get_pixel(ref, i - 1, j + 1) == m) && (jbig2_image_get_pixel(ref, i, j + 1) == m) && (jbig2_image_get_pixel(ref, i + 1, j + 1) == m) )? m : -1; } static uint32_t mkctx0(const Jbig2RefinementRegionParams *params, Jbig2Image *image, int x, int y) { Jbig2Image *ref = params->GRREFERENCE; const int dx = params->GRREFERENCEDX; const int dy = params->GRREFERENCEDY; uint32_t CONTEXT; CONTEXT = jbig2_image_get_pixel(image, x - 1, y + 0); CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 1; CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 2; CONTEXT |= jbig2_image_get_pixel(image, x + params->grat[0], y + params->grat[1]) << 3; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 1) << 4; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 1) << 5; CONTEXT |= jbig2_image_get_pixel(ref, x - dx - 1, y - dy + 1) << 6; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 0) << 7; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 0) << 8; CONTEXT |= jbig2_image_get_pixel(ref, x - dx - 1, y - dy + 0) << 9; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy - 1) << 10; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy - 1) << 11; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + params->grat[2], y - dy + params->grat[3]) << 12; return CONTEXT; } static uint32_t mkctx1(const Jbig2RefinementRegionParams *params, Jbig2Image *image, int x, int y) { Jbig2Image *ref = params->GRREFERENCE; const int dx = params->GRREFERENCEDX; const int dy = params->GRREFERENCEDY; uint32_t CONTEXT; CONTEXT = jbig2_image_get_pixel(image, x - 1, y + 0); CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 1; CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 2; CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 3; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 1) << 4; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 1) << 5; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 1, y - dy + 0) << 6; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy + 0) << 7; CONTEXT |= jbig2_image_get_pixel(ref, x - dx - 1, y - dy + 0) << 8; CONTEXT |= jbig2_image_get_pixel(ref, x - dx + 0, y - dy - 1) << 9; return CONTEXT; } static int jbig2_decode_refinement_TPGRON(Jbig2Ctx *ctx, const Jbig2RefinementRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GR_stats) { const int GRW = image->width; const int GRH = image->height; int x, y, iv, LTP = 0; uint32_t start_context = (params->GRTEMPLATE ? 0x40 : 0x100); ContextBuilder mkctx = (params->GRTEMPLATE ? mkctx1 : mkctx0); if (params->GRTEMPLATE == 0 && (pixel_outside_field(params->grat[0], params->grat[1]) || refpixel_outside_field(params->grat[2], params->grat[3]))) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "adaptive template pixel is out of field"); for (y = 0; y < GRH; y++) { int bit = jbig2_arith_decode(as, &GR_stats[start_context]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode arithmetic code when handling refinement TPGRON1"); LTP ^= bit; if (!LTP) { for (x = 0; x < GRW; x++) { bit = jbig2_arith_decode(as, &GR_stats[mkctx(params, image, x, y)]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode arithmetic code when handling refinement TPGRON1"); jbig2_image_set_pixel(image, x, y, bit); } } else { for (x = 0; x < GRW; x++) { iv = implicit_value(params, image, x, y); if (iv < 0) { int bit = jbig2_arith_decode(as, &GR_stats[mkctx(params, image, x, y)]); if (bit < 0) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode arithmetic code when handling refinement TPGRON1"); jbig2_image_set_pixel(image, x, y, bit); } else jbig2_image_set_pixel(image, x, y, iv); } } } return 0; } /** * jbig2_decode_refinement_region: Decode a generic refinement region. * @ctx: The context for allocation and error reporting. * @segment: A segment reference for error reporting. * @params: Decoding parameter set. * @as: Arithmetic decoder state. * @image: Where to store the decoded image. * @GR_stats: Arithmetic stats. * * Decodes a generic refinement region, according to section 6.3. * an already allocated Jbig2Image object in @image for the result. * * Because this API is based on an arithmetic decoding state, it is * not suitable for MMR decoding. * * Return code: 0 on success. **/ int jbig2_decode_refinement_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2RefinementRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GR_stats) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "decoding generic refinement region with offset %d,%x, GRTEMPLATE=%d, TPGRON=%d", params->GRREFERENCEDX, params->GRREFERENCEDY, params->GRTEMPLATE, params->TPGRON); if (params->TPGRON) return jbig2_decode_refinement_TPGRON(ctx, params, as, image, GR_stats); if (params->GRTEMPLATE) return jbig2_decode_refinement_template1_unopt(ctx, segment, params, as, image, GR_stats); else return jbig2_decode_refinement_template0_unopt(ctx, segment, params, as, image, GR_stats); } /** * Find the first referred-to intermediate region segment * with a non-NULL result for use as a reference image */ static Jbig2Segment * jbig2_region_find_referred(Jbig2Ctx *ctx, Jbig2Segment *segment) { const int nsegments = segment->referred_to_segment_count; Jbig2Segment *rsegment; int index; for (index = 0; index < nsegments; index++) { rsegment = jbig2_find_segment(ctx, segment->referred_to_segments[index]); if (rsegment == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to find referred to segment %d", segment->referred_to_segments[index]); continue; } switch (rsegment->flags & 63) { case 4: /* intermediate text region */ case 20: /* intermediate halftone region */ case 36: /* intermediate generic region */ case 40: /* intermediate generic refinement region */ if (rsegment->result) return rsegment; break; default: /* keep looking */ break; } } /* no appropriate reference was found. */ return NULL; } /** * Handler for generic refinement region segments */ int jbig2_refinement_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { Jbig2RefinementRegionParams params; Jbig2RegionSegmentInfo rsi; int offset = 0; byte seg_flags; int code = 0; /* 7.4.7 */ if (segment->data_length < 18) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); jbig2_get_region_segment_info(&rsi, segment_data); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "generic region: %u x %u @ (%u, %u), flags = %02x", rsi.width, rsi.height, rsi.x, rsi.y, rsi.flags); /* 7.4.7.2 */ seg_flags = segment_data[17]; params.GRTEMPLATE = seg_flags & 0x01; params.TPGRON = seg_flags & 0x02 ? 1 : 0; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "segment flags = %02x %s%s", seg_flags, params.GRTEMPLATE ? " GRTEMPLATE" : "", params.TPGRON ? " TPGRON" : ""); if (seg_flags & 0xFC) jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "reserved segment flag bits are non-zero"); offset += 18; /* 7.4.7.3 */ if (!params.GRTEMPLATE) { if (segment->data_length < 22) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); params.grat[0] = segment_data[offset + 0]; params.grat[1] = segment_data[offset + 1]; params.grat[2] = segment_data[offset + 2]; params.grat[3] = segment_data[offset + 3]; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "grat1: (%d, %d) grat2: (%d, %d)", params.grat[0], params.grat[1], params.grat[2], params.grat[3]); offset += 4; } /* 7.4.7.4 - set up the reference image */ if (segment->referred_to_segment_count) { Jbig2Segment *ref; ref = jbig2_region_find_referred(ctx, segment); if (ref == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to find reference bitmap"); if (ref->result == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "reference bitmap has no decoded image"); /* the reference bitmap is the result of a previous intermediate region segment; the reference selection rules say to use the first one available, and not to reuse any intermediate result, so we simply take another reference to it and free the original to keep track of this. */ params.GRREFERENCE = jbig2_image_reference(ctx, (Jbig2Image *) ref->result); jbig2_image_release(ctx, (Jbig2Image *) ref->result); ref->result = NULL; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "found reference bitmap in segment %d", ref->number); } else { /* the reference is just (a subset of) the page buffer */ if (ctx->pages[ctx->current_page].image == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "reference page bitmap has no decoded image"); params.GRREFERENCE = jbig2_image_reference(ctx, ctx->pages[ctx->current_page].image); /* TODO: subset the image if appropriate */ } /* 7.4.7.5 */ params.GRREFERENCEDX = 0; params.GRREFERENCEDY = 0; { Jbig2WordStream *ws = NULL; Jbig2ArithState *as = NULL; Jbig2ArithCx *GR_stats = NULL; int stats_size; Jbig2Image *image = NULL; image = jbig2_image_new(ctx, rsi.width, rsi.height); if (image == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate refinement image"); goto cleanup; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "allocated %d x %d image buffer for region decode results", rsi.width, rsi.height); stats_size = params.GRTEMPLATE ? 1 << 10 : 1 << 13; GR_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GR_stats == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate arithmetic decoder state for generic refinement regions"); goto cleanup; } memset(GR_stats, 0, stats_size); ws = jbig2_word_stream_buf_new(ctx, segment_data + offset, segment->data_length - offset); if (ws == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate word stream when handling refinement region"); goto cleanup; } as = jbig2_arith_new(ctx, ws); if (as == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding state when handling refinement region"); goto cleanup; } code = jbig2_decode_refinement_region(ctx, segment, ¶ms, as, image, GR_stats); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode refinement region"); goto cleanup; } if ((segment->flags & 63) == 40) { /* intermediate region. save the result for later */ segment->result = jbig2_image_reference(ctx, image); } else { /* immediate region. composite onto the page */ jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "composing %dx%d decoded refinement region onto page at (%d, %d)", rsi.width, rsi.height, rsi.x, rsi.y); code = jbig2_page_add_result(ctx, &ctx->pages[ctx->current_page], image, rsi.x, rsi.y, rsi.op); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to add refinement region to page"); goto cleanup; } } cleanup: jbig2_image_release(ctx, image); jbig2_image_release(ctx, params.GRREFERENCE); jbig2_free(ctx->allocator, as); jbig2_word_stream_buf_free(ctx, ws); jbig2_free(ctx->allocator, GR_stats); } return code; } jbig2dec-0.18/jbig2_refinement.h000066400000000000000000000023551362055302000164760ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_REFINEMENT_H #define _JBIG2_REFINEMENT_H /* 6.3 Table 6 */ typedef struct { /* GRW */ /* GRH */ bool GRTEMPLATE; Jbig2Image *GRREFERENCE; int32_t GRREFERENCEDX, GRREFERENCEDY; bool TPGRON; int8_t grat[4]; } Jbig2RefinementRegionParams; int jbig2_decode_refinement_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2RefinementRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats); /* 7.4 */ int jbig2_refinement_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data); #endif /* _JBIG2_REFINEMENT_H */ jbig2dec-0.18/jbig2_segment.c000066400000000000000000000337011362055302000157760ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include /* size_t */ #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_arith_int.h" #include "jbig2_arith_iaid.h" #include "jbig2_generic.h" #include "jbig2_image.h" #include "jbig2_halftone.h" #include "jbig2_huffman.h" #include "jbig2_page.h" #include "jbig2_refinement.h" #include "jbig2_segment.h" #include "jbig2_symbol_dict.h" #include "jbig2_text.h" #if !defined (UINT32_MAX) #define UINT32_MAX 0xffffffff #endif Jbig2Segment * jbig2_parse_segment_header(Jbig2Ctx *ctx, uint8_t *buf, size_t buf_size, size_t *p_header_size) { Jbig2Segment *result; uint8_t rtscarf; uint32_t rtscarf_long; uint32_t *referred_to_segments; uint32_t referred_to_segment_count; uint32_t referred_to_segment_size; uint32_t pa_size; uint32_t offset; /* minimum possible size of a jbig2 segment header */ if (buf_size < 11) return NULL; result = jbig2_new(ctx, Jbig2Segment, 1); if (result == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate segment"); return NULL; } /* 7.2.2 */ result->number = jbig2_get_uint32(buf); /* 7.2.3 */ result->flags = buf[4]; /* 7.2.4 referred-to segments */ rtscarf = buf[5]; if ((rtscarf & 0xe0) == 0xe0) { rtscarf_long = jbig2_get_uint32(buf + 5); referred_to_segment_count = rtscarf_long & 0x1fffffff; offset = 5 + 4 + (referred_to_segment_count + 1) / 8; } else { referred_to_segment_count = (rtscarf >> 5); offset = 5 + 1; } result->referred_to_segment_count = referred_to_segment_count; /* we now have enough information to compute the full header length */ referred_to_segment_size = result->number <= 256 ? 1 : result->number <= 65536 ? 2 : 4; /* 7.2.5 */ pa_size = result->flags & 0x40 ? 4 : 1; /* 7.2.6 */ if (offset + referred_to_segment_count * referred_to_segment_size + pa_size + 4 > buf_size) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, result->number, "insufficient data to parse segment header"); jbig2_free(ctx->allocator, result); return NULL; } /* 7.2.5 */ if (referred_to_segment_count) { uint32_t i; referred_to_segments = jbig2_new(ctx, uint32_t, referred_to_segment_count * referred_to_segment_size); if (referred_to_segments == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, result->number, "failed to allocate referred to segments"); return NULL; } for (i = 0; i < referred_to_segment_count; i++) { referred_to_segments[i] = (referred_to_segment_size == 1) ? buf[offset] : (referred_to_segment_size == 2) ? jbig2_get_uint16(buf + offset) : jbig2_get_uint32(buf + offset); offset += referred_to_segment_size; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, result->number, "segment %d refers to segment %d", result->number, referred_to_segments[i]); } result->referred_to_segments = referred_to_segments; } else { /* no referred-to segments */ result->referred_to_segments = NULL; } /* 7.2.6 */ if (pa_size == 4) { result->page_association = jbig2_get_uint32(buf + offset); offset += 4; } else { result->page_association = buf[offset++]; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, result->number, "segment %d is associated with page %d", result->number, result->page_association); /* 7.2.7 */ result->rows = UINT32_MAX; result->data_length = jbig2_get_uint32(buf + offset); *p_header_size = offset + 4; /* no body parsing results yet */ result->result = NULL; return result; } void jbig2_free_segment(Jbig2Ctx *ctx, Jbig2Segment *segment) { if (segment == NULL) return; jbig2_free(ctx->allocator, segment->referred_to_segments); /* todo: we need either some separate fields or a more complex result object rather than this brittle special casing */ switch (segment->flags & 63) { case 0: /* symbol dictionary */ if (segment->result != NULL) jbig2_sd_release(ctx, (Jbig2SymbolDict *) segment->result); break; case 4: /* intermediate text region */ case 40: /* intermediate refinement region */ if (segment->result != NULL) jbig2_image_release(ctx, (Jbig2Image *) segment->result); break; case 16: /* pattern dictionary */ if (segment->result != NULL) jbig2_hd_release(ctx, (Jbig2PatternDict *) segment->result); break; case 53: /* user-supplied huffman table */ if (segment->result != NULL) jbig2_table_free(ctx, (Jbig2HuffmanParams *) segment->result); break; default: /* anything else is probably an undefined pointer */ break; } jbig2_free(ctx->allocator, segment); } /* find a segment by number */ Jbig2Segment * jbig2_find_segment(Jbig2Ctx *ctx, uint32_t number) { int index, index_max = ctx->segment_index - 1; const Jbig2Ctx *global_ctx = ctx->global_ctx; /* FIXME: binary search would be better */ for (index = index_max; index >= 0; index--) if (ctx->segments[index]->number == number) return (ctx->segments[index]); if (global_ctx) for (index = global_ctx->segment_index - 1; index >= 0; index--) if (global_ctx->segments[index]->number == number) return (global_ctx->segments[index]); /* didn't find a match */ return NULL; } /* parse the generic portion of a region segment data header */ void jbig2_get_region_segment_info(Jbig2RegionSegmentInfo *info, const uint8_t *segment_data) { /* 7.4.1 */ info->width = jbig2_get_uint32(segment_data); info->height = jbig2_get_uint32(segment_data + 4); info->x = jbig2_get_uint32(segment_data + 8); info->y = jbig2_get_uint32(segment_data + 12); info->flags = segment_data[16]; info->op = (Jbig2ComposeOp)(info->flags & 0x7); } /* dispatch code for extension segment parsing */ static int jbig2_parse_extension_segment(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { uint32_t type; bool reserved; bool necessary; if (segment->data_length < 4) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); type = jbig2_get_uint32(segment_data); reserved = type & 0x20000000; /* Not implemented since this bit is only needed by encoders. dependent = type & 0x40000000; */ necessary = type & 0x80000000; if (necessary && !reserved) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "extension segment is marked 'necessary' but not 'reserved' contrary to spec"); } switch (type) { case 0x20000000: jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "ignoring ASCII comment"); break; case 0x20000002: jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "ignoring UCS-2 comment"); break; default: if (necessary) { return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unhandled necessary extension segment type 0x%08x", type); } else { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unhandled non-necessary extension segment, skipping"); } } return 0; } /* dispatch code for profile segment parsing */ static int jbig2_parse_profile_segment(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { uint32_t profiles; uint32_t i; uint32_t profile; int index; const char *requirements; const char *generic_region; const char *refinement_region; const char *halftone_region; const char *numerical_data; if (segment->data_length < 4) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Segment too short"); index = 0; profiles = jbig2_get_uint32(&segment_data[index]); index += 4; for (i = 0; i < profiles; i++) { if (segment->data_length - index < 4) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short to store profile"); profile = jbig2_get_uint32(&segment_data[index]); index += 4; switch (profile) { case 0x00000001: requirements = "All JBIG2 capabilities"; generic_region = "No restriction"; refinement_region = "No restriction"; halftone_region = "No restriction"; numerical_data = "No restriction"; break; case 0x00000002: requirements = "Maximum compression"; generic_region = "Arithmetic only; any template used"; refinement_region = "No restriction"; halftone_region = "No restriction"; numerical_data = "Arithmetic only"; break; case 0x00000003: requirements = "Medium complexity and medium compression"; generic_region = "Arithmetic only; only 10-pixel and 13-pixel templates"; refinement_region = "10-pixel template only"; halftone_region = "No skip mask used"; numerical_data = "Arithmetic only"; break; case 0x00000004: requirements = "Low complexity with progressive lossless capability"; generic_region = "MMR only"; refinement_region = "10-pixel template only"; halftone_region = "No skip mask used"; numerical_data = "Huffman only"; break; case 0x00000005: requirements = "Low complexity"; generic_region = "MMR only"; refinement_region = "Not available"; halftone_region = "No skip mask used"; numerical_data = "Huffman only"; break; default: requirements = "Unknown"; generic_region = "Unknown"; refinement_region = "Unknown"; halftone_region = "Unknown"; numerical_data = "Unknown"; break; } jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "Supported profile: 0x%08x", profile); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, " Requirements: %s", requirements); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, " Generic region coding: %s", generic_region); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, " Refinement region coding: %s", refinement_region); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, " Halftone region coding: %s", halftone_region); jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, " Numerical data: %s", numerical_data); } return 0; } /* general segment parsing dispatch */ int jbig2_parse_segment(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data) { jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "segment %d, flags=%x, type=%d, data_length=%ld", segment->number, segment->flags, segment->flags & 63, (long) segment->data_length); switch (segment->flags & 63) { case 0: return jbig2_symbol_dictionary(ctx, segment, segment_data); case 4: /* intermediate text region */ case 6: /* immediate text region */ case 7: /* immediate lossless text region */ return jbig2_text_region(ctx, segment, segment_data); case 16: return jbig2_pattern_dictionary(ctx, segment, segment_data); case 20: /* intermediate halftone region */ case 22: /* immediate halftone region */ case 23: /* immediate lossless halftone region */ return jbig2_halftone_region(ctx, segment, segment_data); case 36: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unhandled segment type 'intermediate generic region' (NYI)"); case 38: /* immediate generic region */ case 39: /* immediate lossless generic region */ return jbig2_immediate_generic_region(ctx, segment, segment_data); case 40: /* intermediate generic refinement region */ case 42: /* immediate generic refinement region */ case 43: /* immediate lossless generic refinement region */ return jbig2_refinement_region(ctx, segment, segment_data); case 48: return jbig2_page_info(ctx, segment, segment_data); case 49: return jbig2_end_of_page(ctx, segment, segment_data); case 50: return jbig2_end_of_stripe(ctx, segment, segment_data); case 51: ctx->state = JBIG2_FILE_EOF; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "end of file"); break; case 52: return jbig2_parse_profile_segment(ctx, segment, segment_data); case 53: /* user-supplied huffman table */ return jbig2_table(ctx, segment, segment_data); case 54: return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unhandled segment type 'color palette' (NYI)"); case 62: return jbig2_parse_extension_segment(ctx, segment, segment_data); default: jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unknown segment type %d", segment->flags & 63); } return 0; } jbig2dec-0.18/jbig2_segment.h000066400000000000000000000030231362055302000157750ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_SEGMENT_H #define _JBIG2_SEGMENT_H /* segment header routines */ struct _Jbig2Segment { uint32_t number; uint8_t flags; uint32_t page_association; size_t data_length; int referred_to_segment_count; uint32_t *referred_to_segments; uint32_t rows; void *result; }; Jbig2Segment *jbig2_parse_segment_header(Jbig2Ctx *ctx, uint8_t *buf, size_t buf_size, size_t *p_header_size); int jbig2_parse_segment(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data); void jbig2_free_segment(Jbig2Ctx *ctx, Jbig2Segment *segment); Jbig2Segment *jbig2_find_segment(Jbig2Ctx *ctx, uint32_t number); /* region segment info */ typedef struct { uint32_t width; uint32_t height; uint32_t x; uint32_t y; Jbig2ComposeOp op; uint8_t flags; } Jbig2RegionSegmentInfo; void jbig2_get_region_segment_info(Jbig2RegionSegmentInfo *info, const uint8_t *segment_data); #endif /* _JBIG2_SEGMENT_H */ jbig2dec-0.18/jbig2_symbol_dict.c000066400000000000000000001313401362055302000166420ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* symbol dictionary segment decode and support */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include /* memset() */ #if defined(OUTPUT_PBM) || defined(DUMP_SYMDICT) #include #endif #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_arith_int.h" #include "jbig2_arith_iaid.h" #include "jbig2_generic.h" #include "jbig2_huffman.h" #include "jbig2_image.h" #include "jbig2_mmr.h" #include "jbig2_refinement.h" #include "jbig2_segment.h" #include "jbig2_symbol_dict.h" #include "jbig2_text.h" /* Table 13 */ typedef struct { bool SDHUFF; bool SDREFAGG; uint32_t SDNUMINSYMS; Jbig2SymbolDict *SDINSYMS; uint32_t SDNUMNEWSYMS; uint32_t SDNUMEXSYMS; Jbig2HuffmanTable *SDHUFFDH; Jbig2HuffmanTable *SDHUFFDW; Jbig2HuffmanTable *SDHUFFBMSIZE; Jbig2HuffmanTable *SDHUFFAGGINST; int SDTEMPLATE; int8_t sdat[8]; bool SDRTEMPLATE; int8_t sdrat[4]; } Jbig2SymbolDictParams; /* Utility routines */ #ifdef DUMP_SYMDICT void jbig2_dump_symbol_dict(Jbig2Ctx *ctx, Jbig2Segment *segment) { Jbig2SymbolDict *dict = (Jbig2SymbolDict *) segment->result; int index; char filename[24]; int code; if (dict == NULL) return; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "dumping symbol dictionary as %d individual png files", dict->n_symbols); for (index = 0; index < dict->n_symbols; index++) { snprintf(filename, sizeof(filename), "symbol_%02d-%04d.png", segment->number, index); jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "dumping symbol %d/%d as '%s'", index, dict->n_symbols, filename); #ifdef HAVE_LIBPNG code = jbig2_image_write_png_file(dict->glyphs[index], filename); #else code = jbig2_image_write_pbm_file(dict->glyphs[index], filename); #endif if (code < 0) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to dump symbol %d/%d as '%s'", index, dict->n_symbols, filename); } } #endif /* DUMP_SYMDICT */ /* return a new empty symbol dict */ Jbig2SymbolDict * jbig2_sd_new(Jbig2Ctx *ctx, uint32_t n_symbols) { Jbig2SymbolDict *new_dict = NULL; new_dict = jbig2_new(ctx, Jbig2SymbolDict, 1); if (new_dict != NULL) { new_dict->glyphs = jbig2_new(ctx, Jbig2Image *, n_symbols); new_dict->n_symbols = n_symbols; } else { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate new empty symbol dictionary"); return NULL; } if (new_dict->glyphs != NULL) { memset(new_dict->glyphs, 0, n_symbols * sizeof(Jbig2Image *)); } else if (new_dict->n_symbols > 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate glyphs for new empty symbol dictionary"); jbig2_free(ctx->allocator, new_dict); return NULL; } return new_dict; } /* release the memory associated with a symbol dict */ void jbig2_sd_release(Jbig2Ctx *ctx, Jbig2SymbolDict *dict) { uint32_t i; if (dict == NULL) return; if (dict->glyphs != NULL) for (i = 0; i < dict->n_symbols; i++) jbig2_image_release(ctx, dict->glyphs[i]); jbig2_free(ctx->allocator, dict->glyphs); jbig2_free(ctx->allocator, dict); } /* get a particular glyph by index */ Jbig2Image * jbig2_sd_glyph(Jbig2SymbolDict *dict, unsigned int id) { if (dict == NULL) return NULL; return dict->glyphs[id]; } /* count the number of dictionary segments referred to by the given segment */ uint32_t jbig2_sd_count_referred(Jbig2Ctx *ctx, Jbig2Segment *segment) { int index; Jbig2Segment *rsegment; uint32_t n_dicts = 0; for (index = 0; index < segment->referred_to_segment_count; index++) { rsegment = jbig2_find_segment(ctx, segment->referred_to_segments[index]); if (rsegment && ((rsegment->flags & 63) == 0) && rsegment->result && (((Jbig2SymbolDict *) rsegment->result)->n_symbols > 0) && ((*((Jbig2SymbolDict *) rsegment->result)->glyphs) != NULL)) n_dicts++; } return (n_dicts); } /* return an array of pointers to symbol dictionaries referred to by the given segment */ Jbig2SymbolDict ** jbig2_sd_list_referred(Jbig2Ctx *ctx, Jbig2Segment *segment) { int index; Jbig2Segment *rsegment; Jbig2SymbolDict **dicts; uint32_t n_dicts = jbig2_sd_count_referred(ctx, segment); uint32_t dindex = 0; dicts = jbig2_new(ctx, Jbig2SymbolDict *, n_dicts); if (dicts == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate referred list of symbol dictionaries"); return NULL; } for (index = 0; index < segment->referred_to_segment_count; index++) { rsegment = jbig2_find_segment(ctx, segment->referred_to_segments[index]); if (rsegment && ((rsegment->flags & 63) == 0) && rsegment->result && (((Jbig2SymbolDict *) rsegment->result)->n_symbols > 0) && ((*((Jbig2SymbolDict *) rsegment->result)->glyphs) != NULL)) { /* add this referred to symbol dictionary */ dicts[dindex++] = (Jbig2SymbolDict *) rsegment->result; } } if (dindex != n_dicts) { /* should never happen */ jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "counted %d symbol dictionaries but built a list with %d.", n_dicts, dindex); jbig2_free(ctx->allocator, dicts); return NULL; } return (dicts); } /* generate a new symbol dictionary by concatenating a list of existing dictionaries */ Jbig2SymbolDict * jbig2_sd_cat(Jbig2Ctx *ctx, uint32_t n_dicts, Jbig2SymbolDict **dicts) { uint32_t i, j, k, symbols; Jbig2SymbolDict *new_dict = NULL; /* count the imported symbols and allocate a new array */ symbols = 0; for (i = 0; i < n_dicts; i++) symbols += dicts[i]->n_symbols; /* fill a new array with new references to glyph pointers */ new_dict = jbig2_sd_new(ctx, symbols); if (new_dict != NULL) { k = 0; for (i = 0; i < n_dicts; i++) for (j = 0; j < dicts[i]->n_symbols; j++) new_dict->glyphs[k++] = jbig2_image_reference(ctx, dicts[i]->glyphs[j]); } else { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "failed to allocate new symbol dictionary"); } return new_dict; } /* Decoding routines */ /* 6.5 */ static Jbig2SymbolDict * jbig2_decode_symbol_dict(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2SymbolDictParams *params, const byte *data, size_t size, Jbig2ArithCx *GB_stats, Jbig2ArithCx *GR_stats) { Jbig2SymbolDict *SDNEWSYMS = NULL; Jbig2SymbolDict *SDEXSYMS = NULL; uint32_t HCHEIGHT; uint32_t NSYMSDECODED; uint32_t SYMWIDTH, TOTWIDTH; uint32_t HCFIRSTSYM; uint32_t *SDNEWSYMWIDTHS = NULL; int SBSYMCODELEN = 0; Jbig2WordStream *ws = NULL; Jbig2HuffmanState *hs = NULL; Jbig2ArithState *as = NULL; Jbig2ArithIntCtx *IADH = NULL; Jbig2ArithIntCtx *IADW = NULL; Jbig2ArithIntCtx *IAEX = NULL; Jbig2ArithIntCtx *IAAI = NULL; int code = 0; Jbig2SymbolDict **refagg_dicts = NULL; uint32_t i; Jbig2TextRegionParams tparams; Jbig2Image *image = NULL; Jbig2Image *glyph = NULL; uint32_t emptyruns = 0; memset(&tparams, 0, sizeof(tparams)); /* 6.5.5 (3) */ HCHEIGHT = 0; NSYMSDECODED = 0; ws = jbig2_word_stream_buf_new(ctx, data, size); if (ws == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate word stream when decoding symbol dictionary"); return NULL; } as = jbig2_arith_new(ctx, ws); if (as == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding state when decoding symbol dictionary"); jbig2_word_stream_buf_free(ctx, ws); return NULL; } if (params->SDHUFF) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "huffman coded symbol dictionary"); hs = jbig2_huffman_new(ctx, ws); tparams.SBHUFFRDX = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); /* Table B.15 */ tparams.SBHUFFRDY = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); /* Table B.15 */ tparams.SBHUFFRSIZE = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A); /* Table B.1 */ if (hs == NULL || tparams.SBHUFFRDX == NULL || tparams.SBHUFFRDY == NULL || tparams.SBHUFFRSIZE == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate for symbol bitmap"); goto cleanup; } if (!params->SDREFAGG) { SDNEWSYMWIDTHS = jbig2_new(ctx, uint32_t, params->SDNUMNEWSYMS); if (SDNEWSYMWIDTHS == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate symbol widths (%u)", params->SDNUMNEWSYMS); goto cleanup; } } else { tparams.SBHUFFFS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_F); /* Table B.6 */ tparams.SBHUFFDS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_H); /* Table B.8 */ tparams.SBHUFFDT = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_K); /* Table B.11 */ tparams.SBHUFFRDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); /* Table B.15 */ tparams.SBHUFFRDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); /* Table B.15 */ if (tparams.SBHUFFFS == NULL || tparams.SBHUFFDS == NULL || tparams.SBHUFFDT == NULL || tparams.SBHUFFRDW == NULL || tparams.SBHUFFRDH == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "out of memory creating text region huffman decoder entries"); goto cleanup; } } } else { IADH = jbig2_arith_int_ctx_new(ctx); IADW = jbig2_arith_int_ctx_new(ctx); IAEX = jbig2_arith_int_ctx_new(ctx); IAAI = jbig2_arith_int_ctx_new(ctx); if (IADH == NULL || IADW == NULL || IAEX == NULL || IAAI == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate symbol bitmap"); goto cleanup; } for (SBSYMCODELEN = 0; ((uint64_t) 1 << SBSYMCODELEN) < ((uint64_t) params->SDNUMINSYMS + params->SDNUMNEWSYMS); SBSYMCODELEN++); tparams.IAID = jbig2_arith_iaid_ctx_new(ctx, SBSYMCODELEN); tparams.IARDX = jbig2_arith_int_ctx_new(ctx); tparams.IARDY = jbig2_arith_int_ctx_new(ctx); if (tparams.IAID == NULL || tparams.IARDX == NULL || tparams.IARDY == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region arithmetic decoder contexts"); goto cleanup; } if (params->SDREFAGG) { /* Values from Table 17, section 6.5.8.2 (2) */ tparams.IADT = jbig2_arith_int_ctx_new(ctx); tparams.IAFS = jbig2_arith_int_ctx_new(ctx); tparams.IADS = jbig2_arith_int_ctx_new(ctx); tparams.IAIT = jbig2_arith_int_ctx_new(ctx); /* Table 31 */ tparams.IARI = jbig2_arith_int_ctx_new(ctx); tparams.IARDW = jbig2_arith_int_ctx_new(ctx); tparams.IARDH = jbig2_arith_int_ctx_new(ctx); if (tparams.IADT == NULL || tparams.IAFS == NULL || tparams.IADS == NULL || tparams.IAIT == NULL || tparams.IARI == NULL || tparams.IARDW == NULL || tparams.IARDH == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region arith decoder contexts"); } } } tparams.SBHUFF = params->SDHUFF; tparams.SBREFINE = 1; tparams.SBSTRIPS = 1; tparams.SBDEFPIXEL = 0; tparams.SBCOMBOP = JBIG2_COMPOSE_OR; tparams.TRANSPOSED = 0; tparams.REFCORNER = JBIG2_CORNER_TOPLEFT; tparams.SBDSOFFSET = 0; tparams.SBRTEMPLATE = params->SDRTEMPLATE; SDNEWSYMS = jbig2_sd_new(ctx, params->SDNUMNEWSYMS); if (SDNEWSYMS == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate new symbols (%u)", params->SDNUMNEWSYMS); goto cleanup; } refagg_dicts = jbig2_new(ctx, Jbig2SymbolDict *, 2); if (refagg_dicts == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Out of memory allocating dictionary array"); goto cleanup; } refagg_dicts[0] = jbig2_sd_new(ctx, params->SDNUMINSYMS); if (refagg_dicts[0] == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "out of memory allocating symbol dictionary"); goto cleanup; } for (i = 0; i < params->SDNUMINSYMS; i++) { refagg_dicts[0]->glyphs[i] = jbig2_image_reference(ctx, params->SDINSYMS->glyphs[i]); } refagg_dicts[1] = SDNEWSYMS; /* 6.5.5 (4a) */ while (NSYMSDECODED < params->SDNUMNEWSYMS) { int32_t HCDH, DW; /* 6.5.6 */ if (params->SDHUFF) { HCDH = jbig2_huffman_get(hs, params->SDHUFFDH, &code); } else { code = jbig2_arith_int_decode(ctx, IADH, as, &HCDH); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode height class delta"); goto cleanup; } if (code > 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "OOB decoding height class delta"); goto cleanup; } /* 6.5.5 (4b) */ HCHEIGHT = HCHEIGHT + HCDH; SYMWIDTH = 0; TOTWIDTH = 0; HCFIRSTSYM = NSYMSDECODED; if ((int32_t) HCHEIGHT < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "invalid HCHEIGHT value"); goto cleanup; } #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "HCHEIGHT = %d", HCHEIGHT); #endif jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "decoding height class %d with %d syms decoded", HCHEIGHT, NSYMSDECODED); for (;;) { /* 6.5.7 */ if (params->SDHUFF) { DW = jbig2_huffman_get(hs, params->SDHUFFDW, &code); } else { code = jbig2_arith_int_decode(ctx, IADW, as, &DW); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode DW"); goto cleanup; } /* 6.5.5 (4c.i) */ if (code > 0) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "OOB when decoding DW signals end of height class %d", HCHEIGHT); break; } /* check for broken symbol table */ if (NSYMSDECODED >= params->SDNUMNEWSYMS) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "no OOB signaling end of height class %d, continuing", HCHEIGHT); break; } SYMWIDTH = SYMWIDTH + DW; TOTWIDTH = TOTWIDTH + SYMWIDTH; if ((int32_t) SYMWIDTH < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "invalid SYMWIDTH value (%d) at symbol %d", SYMWIDTH, NSYMSDECODED + 1); goto cleanup; } #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "SYMWIDTH = %d TOTWIDTH = %d", SYMWIDTH, TOTWIDTH); #endif /* 6.5.5 (4c.ii) */ if (!params->SDHUFF || params->SDREFAGG) { #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "SDHUFF = %d; SDREFAGG = %d", params->SDHUFF, params->SDREFAGG); #endif /* 6.5.8 */ if (!params->SDREFAGG) { Jbig2GenericRegionParams region_params; int sdat_bytes; /* Table 16 */ region_params.MMR = 0; region_params.GBTEMPLATE = params->SDTEMPLATE; region_params.TPGDON = 0; region_params.USESKIP = 0; sdat_bytes = params->SDTEMPLATE == 0 ? 8 : 2; memcpy(region_params.gbat, params->sdat, sdat_bytes); image = jbig2_image_new(ctx, SYMWIDTH, HCHEIGHT); if (image == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate image"); goto cleanup; } code = jbig2_decode_generic_region(ctx, segment, ®ion_params, as, image, GB_stats); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode generic region"); goto cleanup; } SDNEWSYMS->glyphs[NSYMSDECODED] = image; image = NULL; } else { /* 6.5.8.2 refinement/aggregate symbol */ uint32_t REFAGGNINST; if (params->SDHUFF) { REFAGGNINST = jbig2_huffman_get(hs, params->SDHUFFAGGINST, &code); } else { code = jbig2_arith_int_decode(ctx, IAAI, as, (int32_t *) &REFAGGNINST); } if (code < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode number of symbols in aggregate glyph"); goto cleanup; } if (code > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB in number of symbols in aggregate glyph"); goto cleanup; } if ((int32_t) REFAGGNINST <= 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "invalid number of symbols in aggregate glyph"); goto cleanup; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "aggregate symbol coding (%d instances)", REFAGGNINST); if (REFAGGNINST > 1) { tparams.SBNUMINSTANCES = REFAGGNINST; image = jbig2_image_new(ctx, SYMWIDTH, HCHEIGHT); if (image == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate symbol image"); goto cleanup; } /* multiple symbols are handled as a text region */ code = jbig2_decode_text_region(ctx, segment, &tparams, (const Jbig2SymbolDict * const *)refagg_dicts, 2, image, data, size, GR_stats, as, ws); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode text region"); goto cleanup; } SDNEWSYMS->glyphs[NSYMSDECODED] = image; image = NULL; } else { /* 6.5.8.2.2 */ /* bool SBHUFF = params->SDHUFF; */ Jbig2RefinementRegionParams rparams; uint32_t ID; int32_t RDX, RDY; int BMSIZE = 0; uint32_t ninsyms = params->SDNUMINSYMS; int code1 = 0; int code2 = 0; int code3 = 0; int code4 = 0; int code5 = 0; /* 6.5.8.2.2 (2, 3, 4, 5) */ if (params->SDHUFF) { ID = jbig2_huffman_get_bits(hs, SBSYMCODELEN, &code1); RDX = jbig2_huffman_get(hs, tparams.SBHUFFRDX, &code2); RDY = jbig2_huffman_get(hs, tparams.SBHUFFRDY, &code3); BMSIZE = jbig2_huffman_get(hs, tparams.SBHUFFRSIZE, &code4); code5 = jbig2_huffman_skip(hs); } else { code1 = jbig2_arith_iaid_decode(ctx, tparams.IAID, as, (int32_t *) &ID); code2 = jbig2_arith_int_decode(ctx, tparams.IARDX, as, &RDX); code3 = jbig2_arith_int_decode(ctx, tparams.IARDY, as, &RDY); } if (code1 < 0 || code2 < 0 || code3 < 0 || code4 < 0 || code5 < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode data"); goto cleanup; } if (code1 > 0 || code2 > 0 || code3 > 0 || code4 > 0 || code5 > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB in single refinement/aggregate coded symbol data"); goto cleanup; } if (ID >= ninsyms + NSYMSDECODED) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "refinement references unknown symbol %d", ID); goto cleanup; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "symbol is a refinement of ID %d with the refinement applied at (%d,%d)", ID, RDX, RDY); image = jbig2_image_new(ctx, SYMWIDTH, HCHEIGHT); if (image == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate symbol image"); goto cleanup; } /* Table 18 */ rparams.GRTEMPLATE = params->SDRTEMPLATE; rparams.GRREFERENCE = (ID < ninsyms) ? params->SDINSYMS->glyphs[ID] : SDNEWSYMS->glyphs[ID - ninsyms]; /* SumatraPDF: fail on missing glyphs */ if (rparams.GRREFERENCE == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "missing glyph %d/%d", ID, ninsyms); goto cleanup; } rparams.GRREFERENCEDX = RDX; rparams.GRREFERENCEDY = RDY; rparams.TPGRON = 0; memcpy(rparams.grat, params->sdrat, 4); code = jbig2_decode_refinement_region(ctx, segment, &rparams, as, image, GR_stats); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode refinement region"); goto cleanup; } SDNEWSYMS->glyphs[NSYMSDECODED] = image; image = NULL; /* 6.5.8.2.2 (7) */ if (params->SDHUFF) { if (BMSIZE == 0) BMSIZE = (size_t) SDNEWSYMS->glyphs[NSYMSDECODED]->height * SDNEWSYMS->glyphs[NSYMSDECODED]->stride; code = jbig2_huffman_advance(hs, BMSIZE); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to advance after huffman decoding in refinement region"); goto cleanup; } } } } #ifdef OUTPUT_PBM { char name[64]; FILE *out; int code; snprintf(name, 64, "sd.%04d.%04d.pbm", segment->number, NSYMSDECODED); out = fopen(name, "wb"); code = jbig2_image_write_pbm(SDNEWSYMS->glyphs[NSYMSDECODED], out); fclose(out); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to write glyph"); goto cleanup; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "writing out glyph as '%s' ...", name); } #endif } /* 6.5.5 (4c.iii) */ if (params->SDHUFF && !params->SDREFAGG) { SDNEWSYMWIDTHS[NSYMSDECODED] = SYMWIDTH; } /* 6.5.5 (4c.iv) */ NSYMSDECODED = NSYMSDECODED + 1; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "decoded symbol %u of %u (%ux%u)", NSYMSDECODED, params->SDNUMNEWSYMS, SYMWIDTH, HCHEIGHT); } /* end height class decode loop */ /* 6.5.5 (4d) */ if (params->SDHUFF && !params->SDREFAGG) { /* 6.5.9 */ size_t BMSIZE; uint32_t j; int x; BMSIZE = jbig2_huffman_get(hs, params->SDHUFFBMSIZE, &code); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "error decoding size of collective bitmap"); goto cleanup; } if (code > 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB obtained when decoding size of collective bitmap"); goto cleanup; } /* skip any bits before the next byte boundary */ code = jbig2_huffman_skip(hs); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to skip to next byte when decoding collective bitmap"); } image = jbig2_image_new(ctx, TOTWIDTH, HCHEIGHT); if (image == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate collective bitmap image"); goto cleanup; } if (BMSIZE == 0) { /* if BMSIZE == 0 bitmap is uncompressed */ const byte *src = data + jbig2_huffman_offset(hs); const int stride = (image->width >> 3) + ((image->width & 7) ? 1 : 0); byte *dst = image->data; /* SumatraPDF: prevent read access violation */ if (size < jbig2_huffman_offset(hs) || (size - jbig2_huffman_offset(hs) < (size_t) image->height * stride) || (size < jbig2_huffman_offset(hs))) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "not enough data for decoding uncompressed (%d/%li)", image->height * stride, (long) (size - jbig2_huffman_offset(hs))); goto cleanup; } BMSIZE = (size_t) image->height * stride; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "reading %dx%d uncompressed bitmap for %d symbols (%li bytes)", image->width, image->height, NSYMSDECODED - HCFIRSTSYM, (long) BMSIZE); for (j = 0; j < image->height; j++) { memcpy(dst, src, stride); dst += image->stride; src += stride; } } else { Jbig2GenericRegionParams rparams; /* SumatraPDF: prevent read access violation */ if (size < jbig2_huffman_offset(hs) || size < BMSIZE || size - jbig2_huffman_offset(hs) < BMSIZE) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "not enough data for decoding (%li/%li)", (long) BMSIZE, (long) (size - jbig2_huffman_offset(hs))); goto cleanup; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "reading %dx%d collective bitmap for %d symbols (%li bytes)", image->width, image->height, NSYMSDECODED - HCFIRSTSYM, (long) BMSIZE); rparams.MMR = 1; code = jbig2_decode_generic_mmr(ctx, segment, &rparams, data + jbig2_huffman_offset(hs), BMSIZE, image); if (code) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode MMR-coded generic region"); goto cleanup; } } /* advance past the data we've just read */ code = jbig2_huffman_advance(hs, BMSIZE); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to advance after huffman decoding MMR bitmap image"); goto cleanup; } /* copy the collective bitmap into the symbol dictionary */ x = 0; for (j = HCFIRSTSYM; j < NSYMSDECODED; j++) { glyph = jbig2_image_new(ctx, SDNEWSYMWIDTHS[j], HCHEIGHT); if (glyph == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to copy the collective bitmap into symbol dictionary"); goto cleanup; } code = jbig2_image_compose(ctx, glyph, image, -x, 0, JBIG2_COMPOSE_REPLACE); if (code) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to compose image into glyph"); goto cleanup; } x += SDNEWSYMWIDTHS[j]; SDNEWSYMS->glyphs[j] = glyph; glyph = NULL; } jbig2_image_release(ctx, image); image = NULL; } } /* end of symbol decode loop */ /* 6.5.10 */ SDEXSYMS = jbig2_sd_new(ctx, params->SDNUMEXSYMS); if (SDEXSYMS == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate symbols exported from symbols dictionary"); goto cleanup; } else { uint32_t i = 0; uint32_t j = 0; uint32_t k; int exflag = 0; uint32_t limit = params->SDNUMINSYMS + params->SDNUMNEWSYMS; uint32_t EXRUNLENGTH; while (i < limit) { if (params->SDHUFF) EXRUNLENGTH = jbig2_huffman_get(hs, tparams.SBHUFFRSIZE, &code); else code = jbig2_arith_int_decode(ctx, IAEX, as, (int32_t *) &EXRUNLENGTH); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode runlength for exported symbols"); /* skip to the cleanup code and return SDEXSYMS = NULL */ jbig2_sd_release(ctx, SDEXSYMS); SDEXSYMS = NULL; break; } if (code > 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB when decoding runlength for exported symbols"); /* skip to the cleanup code and return SDEXSYMS = NULL */ jbig2_sd_release(ctx, SDEXSYMS); SDEXSYMS = NULL; break; } /* prevent infinite list of empty runs, 1000 is just an arbitrary number */ if (EXRUNLENGTH <= 0 && ++emptyruns == 1000) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "runlength too small in export symbol table (%u == 0 i = %u limit = %u)", EXRUNLENGTH, i, limit); /* skip to the cleanup code and return SDEXSYMS = NULL */ jbig2_sd_release(ctx, SDEXSYMS); SDEXSYMS = NULL; break; } else if (EXRUNLENGTH > 0) { emptyruns = 0; } if (EXRUNLENGTH > limit - i) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "exporting more symbols than available (%u > %u), capping", i + EXRUNLENGTH, limit); EXRUNLENGTH = limit - i; } if (exflag && j + EXRUNLENGTH > params->SDNUMEXSYMS) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "exporting more symbols than may be exported (%u > %u), capping", j + EXRUNLENGTH, params->SDNUMEXSYMS); EXRUNLENGTH = params->SDNUMEXSYMS - j; } for (k = 0; k < EXRUNLENGTH; k++) { if (exflag) { Jbig2Image *img; if (i < params->SDNUMINSYMS) { img = params->SDINSYMS->glyphs[i]; } else { img = SDNEWSYMS->glyphs[i - params->SDNUMINSYMS]; } SDEXSYMS->glyphs[j++] = jbig2_image_reference(ctx, img); } i++; } exflag = !exflag; } } cleanup: jbig2_image_release(ctx, glyph); jbig2_image_release(ctx, image); if (refagg_dicts != NULL) { if (refagg_dicts[0] != NULL) jbig2_sd_release(ctx, refagg_dicts[0]); /* skip releasing refagg_dicts[1] as that is the same as SDNEWSYMS */ jbig2_free(ctx->allocator, refagg_dicts); } jbig2_sd_release(ctx, SDNEWSYMS); if (params->SDHUFF) { jbig2_release_huffman_table(ctx, tparams.SBHUFFRSIZE); jbig2_release_huffman_table(ctx, tparams.SBHUFFRDY); jbig2_release_huffman_table(ctx, tparams.SBHUFFRDX); jbig2_release_huffman_table(ctx, tparams.SBHUFFRDH); jbig2_release_huffman_table(ctx, tparams.SBHUFFRDW); jbig2_release_huffman_table(ctx, tparams.SBHUFFDT); jbig2_release_huffman_table(ctx, tparams.SBHUFFDS); jbig2_release_huffman_table(ctx, tparams.SBHUFFFS); if (!params->SDREFAGG) { jbig2_free(ctx->allocator, SDNEWSYMWIDTHS); } jbig2_huffman_free(ctx, hs); } else { jbig2_arith_int_ctx_free(ctx, tparams.IARDY); jbig2_arith_int_ctx_free(ctx, tparams.IARDX); jbig2_arith_int_ctx_free(ctx, tparams.IARDH); jbig2_arith_int_ctx_free(ctx, tparams.IARDW); jbig2_arith_int_ctx_free(ctx, tparams.IARI); jbig2_arith_iaid_ctx_free(ctx, tparams.IAID); jbig2_arith_int_ctx_free(ctx, tparams.IAIT); jbig2_arith_int_ctx_free(ctx, tparams.IADS); jbig2_arith_int_ctx_free(ctx, tparams.IAFS); jbig2_arith_int_ctx_free(ctx, tparams.IADT); jbig2_arith_int_ctx_free(ctx, IAAI); jbig2_arith_int_ctx_free(ctx, IAEX); jbig2_arith_int_ctx_free(ctx, IADW); jbig2_arith_int_ctx_free(ctx, IADH); } jbig2_free(ctx->allocator, as); jbig2_word_stream_buf_free(ctx, ws); return SDEXSYMS; } /* 7.4.2 */ int jbig2_symbol_dictionary(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { Jbig2SymbolDictParams params; uint16_t flags; uint32_t sdat_bytes; uint32_t offset; Jbig2ArithCx *GB_stats = NULL; Jbig2ArithCx *GR_stats = NULL; int table_index = 0; const Jbig2HuffmanParams *huffman_params; params.SDHUFF = 0; if (segment->data_length < 10) goto too_short; /* 7.4.2.1.1 */ flags = jbig2_get_uint16(segment_data); /* zero params to ease cleanup later */ memset(¶ms, 0, sizeof(Jbig2SymbolDictParams)); params.SDHUFF = flags & 1; params.SDREFAGG = (flags >> 1) & 1; params.SDTEMPLATE = (flags >> 10) & 3; params.SDRTEMPLATE = (flags >> 12) & 1; if (params.SDHUFF) { switch ((flags & 0x000c) >> 2) { case 0: /* Table B.4 */ params.SDHUFFDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_D); break; case 1: /* Table B.5 */ params.SDHUFFDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_E); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom DH huffman table not found (%d)", table_index); goto cleanup; } params.SDHUFFDH = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: default: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "symbol dictionary specified invalid huffman table"); } if (params.SDHUFFDH == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate DH huffman table"); goto cleanup; } switch ((flags & 0x0030) >> 4) { case 0: /* Table B.2 */ params.SDHUFFDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_B); break; case 1: /* Table B.3 */ params.SDHUFFDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_C); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom DW huffman table not found (%d)", table_index); goto cleanup; } params.SDHUFFDW = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: default: jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "symbol dictionary specified invalid huffman table"); goto cleanup; /* Jump direct to cleanup to avoid 2 errors being given */ } if (params.SDHUFFDW == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate DW huffman table"); goto cleanup; } if (flags & 0x0040) { /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom BMSIZE huffman table not found (%d)", table_index); goto cleanup; } params.SDHUFFBMSIZE = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; } else { /* Table B.1 */ params.SDHUFFBMSIZE = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A); } if (params.SDHUFFBMSIZE == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate BMSIZE huffman table"); goto cleanup; } if (flags & 0x0080) { /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom REFAGG huffman table not found (%d)", table_index); goto cleanup; } params.SDHUFFAGGINST = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; } else { /* Table B.1 */ params.SDHUFFAGGINST = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A); } if (params.SDHUFFAGGINST == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate REFAGG huffman table"); goto cleanup; } } /* FIXME: there are quite a few of these conditions to check */ /* maybe #ifdef CONFORMANCE and a separate routine */ if (!params.SDHUFF) { if (flags & 0x000c) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "SDHUFF is zero, but contrary to spec SDHUFFDH is not."); goto cleanup; } if (flags & 0x0030) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "SDHUFF is zero, but contrary to spec SDHUFFDW is not."); goto cleanup; } } /* 7.4.2.1.2 */ sdat_bytes = params.SDHUFF ? 0 : params.SDTEMPLATE == 0 ? 8 : 2; memcpy(params.sdat, segment_data + 2, sdat_bytes); offset = 2 + sdat_bytes; /* 7.4.2.1.3 */ if (params.SDREFAGG && !params.SDRTEMPLATE) { if (offset + 4 > segment->data_length) goto too_short; memcpy(params.sdrat, segment_data + offset, 4); offset += 4; } if (offset + 8 > segment->data_length) goto too_short; /* 7.4.2.1.4 */ params.SDNUMEXSYMS = jbig2_get_uint32(segment_data + offset); /* 7.4.2.1.5 */ params.SDNUMNEWSYMS = jbig2_get_uint32(segment_data + offset + 4); offset += 8; jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "symbol dictionary, flags=%04x, %u exported syms, %u new syms", flags, params.SDNUMEXSYMS, params.SDNUMNEWSYMS); /* 7.4.2.2 (2) */ { uint32_t n_dicts = jbig2_sd_count_referred(ctx, segment); Jbig2SymbolDict **dicts = NULL; if (n_dicts > 0) { dicts = jbig2_sd_list_referred(ctx, segment); if (dicts == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate dicts in symbol dictionary"); goto cleanup; } params.SDINSYMS = jbig2_sd_cat(ctx, n_dicts, dicts); if (params.SDINSYMS == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate symbol array in symbol dictionary"); jbig2_free(ctx->allocator, dicts); goto cleanup; } jbig2_free(ctx->allocator, dicts); } if (params.SDINSYMS != NULL) { params.SDNUMINSYMS = params.SDINSYMS->n_symbols; } } /* 7.4.2.2 (3, 4) */ if (flags & 0x0100) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "segment marks bitmap coding context as used (NYI)"); goto cleanup; } else { int stats_size = params.SDTEMPLATE == 0 ? 65536 : params.SDTEMPLATE == 1 ? 8192 : 1024; GB_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GB_stats == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate arithmetic decoder states for generic regions"); goto cleanup; } memset(GB_stats, 0, sizeof (Jbig2ArithCx) * stats_size); stats_size = params.SDRTEMPLATE ? 1 << 10 : 1 << 13; GR_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GR_stats == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate arithmetic decoder states for generic refinement regions"); jbig2_free(ctx->allocator, GB_stats); goto cleanup; } memset(GR_stats, 0, sizeof (Jbig2ArithCx) * stats_size); } segment->result = (void *)jbig2_decode_symbol_dict(ctx, segment, ¶ms, segment_data + offset, segment->data_length - offset, GB_stats, GR_stats); #ifdef DUMP_SYMDICT if (segment->result) jbig2_dump_symbol_dict(ctx, segment); #endif /* 7.4.2.2 (7) */ if (flags & 0x0200) { /* todo: retain GB_stats, GR_stats */ jbig2_free(ctx->allocator, GR_stats); jbig2_free(ctx->allocator, GB_stats); jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "segment marks bitmap coding context as retained (NYI)"); goto cleanup; } else { jbig2_free(ctx->allocator, GR_stats); jbig2_free(ctx->allocator, GB_stats); } cleanup: if (params.SDHUFF) { jbig2_release_huffman_table(ctx, params.SDHUFFDH); jbig2_release_huffman_table(ctx, params.SDHUFFDW); jbig2_release_huffman_table(ctx, params.SDHUFFBMSIZE); jbig2_release_huffman_table(ctx, params.SDHUFFAGGINST); } jbig2_sd_release(ctx, params.SDINSYMS); return (segment->result != NULL) ? 0 : -1; too_short: if (params.SDHUFF) { jbig2_release_huffman_table(ctx, params.SDHUFFDH); jbig2_release_huffman_table(ctx, params.SDHUFFDW); jbig2_release_huffman_table(ctx, params.SDHUFFBMSIZE); jbig2_release_huffman_table(ctx, params.SDHUFFAGGINST); } return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); } jbig2dec-0.18/jbig2_symbol_dict.h000066400000000000000000000034661362055302000166560ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_SYMBOL_DICT_H #define _JBIG2_SYMBOL_DICT_H /* symbol dictionary header */ /* the results of decoding a symbol dictionary */ typedef struct { uint32_t n_symbols; Jbig2Image **glyphs; } Jbig2SymbolDict; /* decode a symbol dictionary segment and store the results */ int jbig2_symbol_dictionary(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data); /* get a particular glyph by index */ Jbig2Image *jbig2_sd_glyph(Jbig2SymbolDict *dict, unsigned int id); /* return a new empty symbol dict */ Jbig2SymbolDict *jbig2_sd_new(Jbig2Ctx *ctx, uint32_t n_symbols); /* release the memory associated with a symbol dict */ void jbig2_sd_release(Jbig2Ctx *ctx, Jbig2SymbolDict *dict); /* generate a new symbol dictionary by concatenating a list of existing dictionaries */ Jbig2SymbolDict *jbig2_sd_cat(Jbig2Ctx *ctx, uint32_t n_dicts, Jbig2SymbolDict **dicts); /* count the number of dictionary segments referred to by the given segment */ uint32_t jbig2_sd_count_referred(Jbig2Ctx *ctx, Jbig2Segment *segment); /* return an array of pointers to symbol dictionaries referred to by a segment */ Jbig2SymbolDict **jbig2_sd_list_referred(Jbig2Ctx *ctx, Jbig2Segment *segment); #endif /* _JBIG2_SYMBOL_DICT_H */ jbig2dec-0.18/jbig2_text.c000066400000000000000000001237421362055302000153250ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "os_types.h" #include #include /* memset() */ #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_arith.h" #include "jbig2_arith_int.h" #include "jbig2_arith_iaid.h" #include "jbig2_generic.h" #include "jbig2_huffman.h" #include "jbig2_image.h" #include "jbig2_page.h" #include "jbig2_refinement.h" #include "jbig2_segment.h" #include "jbig2_symbol_dict.h" #include "jbig2_text.h" /** * jbig2_decode_text_region: decode a text region segment * * @ctx: jbig2 decoder context * @segment: jbig2 segment (header) structure * @params: parameters from the text region header * @dicts: an array of referenced symbol dictionaries * @n_dicts: the number of referenced symbol dictionaries * @image: image structure in which to store the decoded region bitmap * @data: pointer to text region data to be decoded * @size: length of text region data * * Implements the text region decoding procedure * described in section 6.4 of the JBIG2 spec. * * returns: 0 on success **/ int jbig2_decode_text_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2TextRegionParams *params, const Jbig2SymbolDict *const *dicts, const uint32_t n_dicts, Jbig2Image *image, const byte *data, const size_t size, Jbig2ArithCx *GR_stats, Jbig2ArithState *as, Jbig2WordStream *ws) { /* relevant bits of 6.4.4 */ uint32_t NINSTANCES; uint32_t ID; int32_t STRIPT; int32_t FIRSTS; int32_t DT; int32_t DFS; int32_t IDS; int32_t CURS; int32_t CURT; int S, T; int x, y; bool first_symbol; uint32_t index, SBNUMSYMS; Jbig2Image *IB = NULL; Jbig2Image *IBO = NULL; Jbig2Image *refimage = NULL; Jbig2HuffmanState *hs = NULL; Jbig2HuffmanTable *SBSYMCODES = NULL; int code = 0; int RI; SBNUMSYMS = 0; for (index = 0; index < n_dicts; index++) { SBNUMSYMS += dicts[index]->n_symbols; } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "symbol list contains %d glyphs in %d dictionaries", SBNUMSYMS, n_dicts); if (params->SBHUFF) { Jbig2HuffmanTable *runcodes = NULL; Jbig2HuffmanParams runcodeparams; Jbig2HuffmanLine runcodelengths[35]; Jbig2HuffmanLine *symcodelengths = NULL; Jbig2HuffmanParams symcodeparams; int err, len, range, r; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "huffman coded text region"); hs = jbig2_huffman_new(ctx, ws); if (hs == NULL) return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region"); /* 7.4.3.1.7 - decode symbol ID Huffman table */ /* this is actually part of the segment header, but it is more convenient to handle it here */ /* parse and build the runlength code huffman table */ for (index = 0; index < 35; index++) { runcodelengths[index].PREFLEN = jbig2_huffman_get_bits(hs, 4, &code); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to read huffman runcode lengths"); goto cleanup1; } if (code > 0) { jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB decoding huffman runcode lengths"); goto cleanup1; } runcodelengths[index].RANGELEN = 0; runcodelengths[index].RANGELOW = index; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, " read runcode%d length %d", index, runcodelengths[index].PREFLEN); } runcodeparams.HTOOB = 0; runcodeparams.lines = runcodelengths; runcodeparams.n_lines = 35; runcodes = jbig2_build_huffman_table(ctx, &runcodeparams); if (runcodes == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "error constructing symbol ID runcode table"); goto cleanup1; } /* decode the symbol ID code lengths using the runlength table */ symcodelengths = jbig2_new(ctx, Jbig2HuffmanLine, SBNUMSYMS); if (symcodelengths == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate memory when reading symbol ID huffman table"); goto cleanup1; } index = 0; while (index < SBNUMSYMS) { code = jbig2_huffman_get(hs, runcodes, &err); if (err < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "error reading symbol ID huffman table"); goto cleanup1; } if (err > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB decoding symbol ID huffman table"); goto cleanup1; } if (code < 0 || code >= 35) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "symbol ID huffman table out of range"); goto cleanup1; } if (code < 32) { len = code; range = 1; } else { if (code == 32) { if (index < 1) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "error decoding symbol ID table: run length with no antecedent"); goto cleanup1; } len = symcodelengths[index - 1].PREFLEN; } else { len = 0; /* code == 33 or 34 */ } err = 0; if (code == 32) range = jbig2_huffman_get_bits(hs, 2, &err) + 3; else if (code == 33) range = jbig2_huffman_get_bits(hs, 3, &err) + 3; else if (code == 34) range = jbig2_huffman_get_bits(hs, 7, &err) + 11; if (err < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to read huffman code"); goto cleanup1; } if (err > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB decoding huffman code"); goto cleanup1; } } jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, " read runcode%d at index %d (length %d range %d)", code, index, len, range); if (index + range > SBNUMSYMS) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "runlength extends %d entries beyond the end of symbol ID table", index + range - SBNUMSYMS); range = SBNUMSYMS - index; } for (r = 0; r < range; r++) { symcodelengths[index + r].PREFLEN = len; symcodelengths[index + r].RANGELEN = 0; symcodelengths[index + r].RANGELOW = index + r; } index += r; } if (index < SBNUMSYMS) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "runlength codes do not cover the available symbol set"); goto cleanup1; } symcodeparams.HTOOB = 0; symcodeparams.lines = symcodelengths; symcodeparams.n_lines = SBNUMSYMS; /* skip to byte boundary */ err = jbig2_huffman_skip(hs); if (err < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to skip to next byte when building huffman table"); goto cleanup1; } /* finally, construct the symbol ID huffman table itself */ SBSYMCODES = jbig2_build_huffman_table(ctx, &symcodeparams); cleanup1: jbig2_free(ctx->allocator, symcodelengths); jbig2_release_huffman_table(ctx, runcodes); if (SBSYMCODES == NULL) { jbig2_huffman_free(ctx, hs); return jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to construct symbol ID huffman table"); } } /* 6.4.5 (1) */ jbig2_image_clear(ctx, image, params->SBDEFPIXEL); /* 6.4.6 */ if (params->SBHUFF) { STRIPT = jbig2_huffman_get(hs, params->SBHUFFDT, &code); } else { code = jbig2_arith_int_decode(ctx, params->IADT, as, &STRIPT); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode strip T"); goto cleanup2; } if (code > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB obtained when decoding strip T"); goto cleanup2; } /* 6.4.5 (2) */ STRIPT *= -(params->SBSTRIPS); FIRSTS = 0; NINSTANCES = 0; /* 6.4.5 (3) */ while (NINSTANCES < params->SBNUMINSTANCES) { /* (3b) */ if (params->SBHUFF) { DT = jbig2_huffman_get(hs, params->SBHUFFDT, &code); } else { code = jbig2_arith_int_decode(ctx, params->IADT, as, &DT); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode delta T"); goto cleanup2; } if (code > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB obtained when decoding delta T"); goto cleanup2; } DT *= params->SBSTRIPS; STRIPT += DT; first_symbol = TRUE; /* 6.4.5 (3c) - decode symbols in strip */ for (;;) { /* (3c.i) */ if (first_symbol) { /* 6.4.7 */ if (params->SBHUFF) { DFS = jbig2_huffman_get(hs, params->SBHUFFFS, &code); } else { code = jbig2_arith_int_decode(ctx, params->IAFS, as, &DFS); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode strip symbol S-difference"); goto cleanup2; } if (code > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB obtained when decoding strip symbol S-difference"); goto cleanup2; } FIRSTS += DFS; CURS = FIRSTS; first_symbol = FALSE; } else { if (NINSTANCES > params->SBNUMINSTANCES) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "too many NINSTANCES (%d) decoded", NINSTANCES); break; } /* (3c.ii) / 6.4.8 */ if (params->SBHUFF) { IDS = jbig2_huffman_get(hs, params->SBHUFFDS, &code); } else { code = jbig2_arith_int_decode(ctx, params->IADS, as, &IDS); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode symbol instance S coordinate"); goto cleanup2; } if (code > 0) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "OOB obtained when decoding symbol instance S coordinate signals end of strip with T value %d", DT); break; } CURS += IDS + params->SBDSOFFSET; } /* (3c.iii) / 6.4.9 */ if (params->SBSTRIPS == 1) { CURT = 0; } else if (params->SBHUFF) { CURT = jbig2_huffman_get_bits(hs, params->LOGSBSTRIPS, &code); } else { code = jbig2_arith_int_decode(ctx, params->IAIT, as, &CURT); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode symbol instance T coordinate"); goto cleanup2; } if (code > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "OOB obtained when decoding symbol instance T coordinate"); goto cleanup2; } T = STRIPT + CURT; /* (3b.iv) / 6.4.10 - decode the symbol ID */ if (params->SBHUFF) { ID = jbig2_huffman_get(hs, SBSYMCODES, &code); } else { code = jbig2_arith_iaid_decode(ctx, params->IAID, as, (int *)&ID); } if (code < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to obtain symbol instance symbol ID"); goto cleanup2; } if (code > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB obtained when decoding symbol instance symbol ID"); goto cleanup2; } if (ID >= SBNUMSYMS) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "ignoring out of range symbol ID (%d/%d)", ID, SBNUMSYMS); IB = NULL; } else { /* (3c.v) / 6.4.11 - look up the symbol bitmap IB */ uint32_t id = ID; index = 0; while (id >= dicts[index]->n_symbols) id -= dicts[index++]->n_symbols; if (dicts[index]->glyphs[id] == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "missing glyph (%d/%d), ignoring", index, id); } else { IB = jbig2_image_reference(ctx, dicts[index]->glyphs[id]); } } if (params->SBREFINE) { if (params->SBHUFF) { RI = jbig2_huffman_get_bits(hs, 1, &code); } else { code = jbig2_arith_int_decode(ctx, params->IARI, as, &RI); } if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode symbol bitmap refinement indicator"); goto cleanup2; } if (code > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB obtained when decoding symbol bitmap refinement indicator"); goto cleanup2; } } else { RI = 0; } if (RI) { Jbig2RefinementRegionParams rparams; int32_t RDW, RDH, RDX, RDY; size_t BMSIZE = 0; int code1 = 0; int code2 = 0; int code3 = 0; int code4 = 0; int code5 = 0; int code6 = 0; /* 6.4.11 (1, 2, 3, 4) */ if (!params->SBHUFF) { code1 = jbig2_arith_int_decode(ctx, params->IARDW, as, &RDW); code2 = jbig2_arith_int_decode(ctx, params->IARDH, as, &RDH); code3 = jbig2_arith_int_decode(ctx, params->IARDX, as, &RDX); code4 = jbig2_arith_int_decode(ctx, params->IARDY, as, &RDY); } else { RDW = jbig2_huffman_get(hs, params->SBHUFFRDW, &code1); RDH = jbig2_huffman_get(hs, params->SBHUFFRDH, &code2); RDX = jbig2_huffman_get(hs, params->SBHUFFRDX, &code3); RDY = jbig2_huffman_get(hs, params->SBHUFFRDY, &code4); BMSIZE = jbig2_huffman_get(hs, params->SBHUFFRSIZE, &code5); code6 = jbig2_huffman_skip(hs); } if (code1 < 0 || code2 < 0 || code3 < 0 || code4 < 0 || code5 < 0 || code6 < 0) { jbig2_image_release(ctx, IB); code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode data"); goto cleanup2; } if (code1 > 0 || code2 > 0 || code3 > 0 || code4 > 0 || code5 > 0 || code6 > 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "OOB obtained when decoding symbol instance refinement data"); goto cleanup2; } /* 6.4.11 (6) */ if (IB) { IBO = IB; IB = NULL; if (((int32_t) IBO->width) + RDW < 0 || ((int32_t) IBO->height) + RDH < 0) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "reference image dimensions negative"); goto cleanup2; } refimage = jbig2_image_new(ctx, IBO->width + RDW, IBO->height + RDH); if (refimage == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate reference image"); goto cleanup2; } jbig2_image_clear(ctx, refimage, 0x00); /* Table 12 */ rparams.GRTEMPLATE = params->SBRTEMPLATE; rparams.GRREFERENCE = IBO; rparams.GRREFERENCEDX = (RDW >> 1) + RDX; rparams.GRREFERENCEDY = (RDH >> 1) + RDY; rparams.TPGRON = 0; memcpy(rparams.grat, params->sbrat, 4); code = jbig2_decode_refinement_region(ctx, segment, &rparams, as, refimage, GR_stats); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode refinement region"); goto cleanup2; } jbig2_image_release(ctx, IBO); IBO = NULL; IB = refimage; refimage = NULL; } /* 6.4.11 (7) */ if (params->SBHUFF) { code = jbig2_huffman_advance(hs, BMSIZE); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to advance after huffman decoding refinement region"); goto cleanup2; } } } /* (3c.vi) */ if ((!params->TRANSPOSED) && (params->REFCORNER > 1) && IB) { CURS += IB->width - 1; } else if ((params->TRANSPOSED) && !(params->REFCORNER & 1) && IB) { CURS += IB->height - 1; } /* (3c.vii) */ S = CURS; /* (3c.viii) */ if (!params->TRANSPOSED) { switch (params->REFCORNER) { case JBIG2_CORNER_TOPLEFT: x = S; y = T; break; case JBIG2_CORNER_TOPRIGHT: if (IB) x = S - IB->width + 1; else x = S + 1; y = T; break; case JBIG2_CORNER_BOTTOMLEFT: x = S; if (IB) y = T - IB->height + 1; else y = T + 1; break; default: case JBIG2_CORNER_BOTTOMRIGHT: if (IB ) { x = S - IB->width + 1; y = T - IB->height + 1; } else { x = S + 1; y = T + 1; } break; } } else { /* TRANSPOSED */ switch (params->REFCORNER) { case JBIG2_CORNER_TOPLEFT: x = T; y = S; break; case JBIG2_CORNER_TOPRIGHT: if (IB) x = T - IB->width + 1; else x = T + 1; y = S; break; case JBIG2_CORNER_BOTTOMLEFT: x = T; if (IB) y = S - IB->height + 1; else y = S + 1; break; default: case JBIG2_CORNER_BOTTOMRIGHT: if (IB) { x = T - IB->width + 1; y = S - IB->height + 1; } else { x = T + 1; y = S + 1; } break; } } /* (3c.ix) */ #ifdef JBIG2_DEBUG jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "composing glyph ID %d: %dx%d @ (%d,%d) symbol %d/%d", ID, IB->width, IB->height, x, y, NINSTANCES + 1, params->SBNUMINSTANCES); #endif code = jbig2_image_compose(ctx, image, IB, x, y, params->SBCOMBOP); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to compose symbol instance symbol bitmap into picture"); goto cleanup2; } /* (3c.x) */ if (IB && (!params->TRANSPOSED) && (params->REFCORNER < 2)) { CURS += IB->width - 1; } else if (IB && (params->TRANSPOSED) && (params->REFCORNER & 1)) { CURS += IB->height - 1; } /* (3c.xi) */ NINSTANCES++; jbig2_image_release(ctx, IB); IB = NULL; } /* end strip */ } /* 6.4.5 (4) */ cleanup2: jbig2_image_release(ctx, refimage); jbig2_image_release(ctx, IBO); jbig2_image_release(ctx, IB); if (params->SBHUFF) { jbig2_release_huffman_table(ctx, SBSYMCODES); } jbig2_huffman_free(ctx, hs); return code; } /** * jbig2_text_region: read a text region segment header **/ int jbig2_text_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data) { uint32_t offset = 0; Jbig2RegionSegmentInfo region_info; Jbig2TextRegionParams params; Jbig2Image *image = NULL; Jbig2SymbolDict **dicts = NULL; uint32_t n_dicts = 0; uint16_t flags = 0; uint16_t huffman_flags = 0; Jbig2ArithCx *GR_stats = NULL; int code = 0; Jbig2WordStream *ws = NULL; Jbig2ArithState *as = NULL; uint32_t table_index = 0; const Jbig2HuffmanParams *huffman_params = NULL; /* 7.4.1 */ if (segment->data_length < 17) goto too_short; jbig2_get_region_segment_info(®ion_info, segment_data); offset += 17; /* Check for T.88 amendment 3 */ if (region_info.flags & 8) return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "region segment flags indicate use of colored bitmap (NYI)"); /* 7.4.3.1.1 */ if (segment->data_length - offset < 2) goto too_short; flags = jbig2_get_uint16(segment_data + offset); offset += 2; jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "text region header flags 0x%04x", flags); /* zero params to ease cleanup later */ memset(¶ms, 0, sizeof(Jbig2TextRegionParams)); params.SBHUFF = flags & 0x0001; params.SBREFINE = flags & 0x0002; params.LOGSBSTRIPS = (flags & 0x000c) >> 2; params.SBSTRIPS = 1 << params.LOGSBSTRIPS; params.REFCORNER = (Jbig2RefCorner)((flags & 0x0030) >> 4); params.TRANSPOSED = flags & 0x0040; params.SBCOMBOP = (Jbig2ComposeOp)((flags & 0x0180) >> 7); params.SBDEFPIXEL = flags & 0x0200; /* SBDSOFFSET is a signed 5 bit integer */ params.SBDSOFFSET = (flags & 0x7C00) >> 10; if (params.SBDSOFFSET > 0x0f) params.SBDSOFFSET -= 0x20; params.SBRTEMPLATE = flags & 0x8000; if (params.SBDSOFFSET) { jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "text region has SBDSOFFSET %d", params.SBDSOFFSET); } if (params.SBHUFF) { /* Huffman coding */ /* 7.4.3.1.2 */ if (segment->data_length - offset < 2) goto too_short; huffman_flags = jbig2_get_uint16(segment_data + offset); offset += 2; if (huffman_flags & 0x8000) jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "reserved bit 15 of text region huffman flags is not zero"); } else { /* arithmetic coding */ /* 7.4.3.1.3 */ if (segment->data_length - offset < 4) goto too_short; if ((params.SBREFINE) && !(params.SBRTEMPLATE)) { params.sbrat[0] = segment_data[offset]; params.sbrat[1] = segment_data[offset + 1]; params.sbrat[2] = segment_data[offset + 2]; params.sbrat[3] = segment_data[offset + 3]; offset += 4; } } /* 7.4.3.1.4 */ if (segment->data_length - offset < 4) goto too_short; params.SBNUMINSTANCES = jbig2_get_uint32(segment_data + offset); offset += 4; if (params.SBHUFF) { /* 7.4.3.1.5 - Symbol ID Huffman table */ /* ...this is handled in the segment body decoder */ /* 7.4.3.1.6 - Other Huffman table selection */ switch (huffman_flags & 0x0003) { case 0: /* Table B.6 */ params.SBHUFFFS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_F); break; case 1: /* Table B.7 */ params.SBHUFFFS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_G); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom FS huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFFS = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid FS huffman table"); goto cleanup1; break; } if (params.SBHUFFFS == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified FS huffman table"); goto cleanup1; } switch ((huffman_flags & 0x000c) >> 2) { case 0: /* Table B.8 */ params.SBHUFFDS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_H); break; case 1: /* Table B.9 */ params.SBHUFFDS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_I); break; case 2: /* Table B.10 */ params.SBHUFFDS = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_J); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom DS huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFDS = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; } if (params.SBHUFFDS == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified DS huffman table"); goto cleanup1; } switch ((huffman_flags & 0x0030) >> 4) { case 0: /* Table B.11 */ params.SBHUFFDT = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_K); break; case 1: /* Table B.12 */ params.SBHUFFDT = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_L); break; case 2: /* Table B.13 */ params.SBHUFFDT = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_M); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom DT huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFDT = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; } if (params.SBHUFFDT == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified DT huffman table"); goto cleanup1; } switch ((huffman_flags & 0x00c0) >> 6) { case 0: /* Table B.14 */ params.SBHUFFRDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom RDW huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDW = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDW huffman table"); goto cleanup1; break; } if (params.SBHUFFRDW == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified RDW huffman table"); goto cleanup1; } switch ((huffman_flags & 0x0300) >> 8) { case 0: /* Table B.14 */ params.SBHUFFRDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom RDH huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDH = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDH huffman table"); goto cleanup1; break; } if (params.SBHUFFRDH == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified RDH huffman table"); goto cleanup1; } switch ((huffman_flags & 0x0c00) >> 10) { case 0: /* Table B.14 */ params.SBHUFFRDX = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDX = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom RDX huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDX = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDX huffman table"); goto cleanup1; break; } if (params.SBHUFFRDX == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified RDX huffman table"); goto cleanup1; } switch ((huffman_flags & 0x3000) >> 12) { case 0: /* Table B.14 */ params.SBHUFFRDY = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_N); break; case 1: /* Table B.15 */ params.SBHUFFRDY = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_O); break; case 3: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom RDY huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRDY = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; case 2: /* invalid */ default: code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "text region specified invalid RDY huffman table"); goto cleanup1; break; } if (params.SBHUFFRDY == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified RDY huffman table"); goto cleanup1; } switch ((huffman_flags & 0x4000) >> 14) { case 0: /* Table B.1 */ params.SBHUFFRSIZE = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A); break; case 1: /* Custom table from referred segment */ huffman_params = jbig2_find_table(ctx, segment, table_index); if (huffman_params == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "custom RSIZE huffman table not found (%d)", table_index); goto cleanup1; } params.SBHUFFRSIZE = jbig2_build_huffman_table(ctx, huffman_params); ++table_index; break; } if (params.SBHUFFRSIZE == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region specified RSIZE huffman table"); goto cleanup1; } if (huffman_flags & 0x8000) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "text region huffman flags bit 15 is set, contrary to spec"); } /* 7.4.3.1.7 */ /* For convenience this is done in the body decoder routine */ } jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number, "text region: %d x %d @ (%d,%d) %d symbols", region_info.width, region_info.height, region_info.x, region_info.y, params.SBNUMINSTANCES); /* 7.4.3.2 (2) - compose the list of symbol dictionaries */ n_dicts = jbig2_sd_count_referred(ctx, segment); if (n_dicts == 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "text region refers to no symbol dictionaries"); } else { dicts = jbig2_sd_list_referred(ctx, segment); if (dicts == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "unable to retrieve symbol dictionaries! previous parsing error?"); goto cleanup1; } else { uint32_t index; if (dicts[0] == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to find first referenced symbol dictionary"); goto cleanup1; } for (index = 1; index < n_dicts; index++) if (dicts[index] == NULL) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to find all referenced symbol dictionaries"); n_dicts = index; } } } /* 7.4.3.2 (3) */ { int stats_size = params.SBRTEMPLATE ? 1 << 10 : 1 << 13; GR_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size); if (GR_stats == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "could not allocate arithmetic decoder state"); goto cleanup1; } memset(GR_stats, 0, stats_size); } image = jbig2_image_new(ctx, region_info.width, region_info.height); if (image == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region image"); goto cleanup2; } if (offset >= segment->data_length) goto too_short; ws = jbig2_word_stream_buf_new(ctx, segment_data + offset, segment->data_length - offset); if (ws == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate word stream when handling text region image"); goto cleanup2; } as = jbig2_arith_new(ctx, ws); if (as == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate arithmetic coding context when handling text region image"); goto cleanup2; } if (!params.SBHUFF) { uint32_t SBSYMCODELEN, index; uint32_t SBNUMSYMS = 0; for (index = 0; index < n_dicts; index++) { SBNUMSYMS += dicts[index]->n_symbols; } params.IADT = jbig2_arith_int_ctx_new(ctx); params.IAFS = jbig2_arith_int_ctx_new(ctx); params.IADS = jbig2_arith_int_ctx_new(ctx); params.IAIT = jbig2_arith_int_ctx_new(ctx); if (params.IADT == NULL || params.IAFS == NULL || params.IADS == NULL || params.IAIT == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region image data"); goto cleanup3; } /* Table 31 */ for (SBSYMCODELEN = 0; (1U << SBSYMCODELEN) < SBNUMSYMS; SBSYMCODELEN++) { } params.IAID = jbig2_arith_iaid_ctx_new(ctx, SBSYMCODELEN); params.IARI = jbig2_arith_int_ctx_new(ctx); params.IARDW = jbig2_arith_int_ctx_new(ctx); params.IARDH = jbig2_arith_int_ctx_new(ctx); params.IARDX = jbig2_arith_int_ctx_new(ctx); params.IARDY = jbig2_arith_int_ctx_new(ctx); if (params.IAID == NULL || params.IARI == NULL || params.IARDW == NULL || params.IARDH == NULL || params.IARDX == NULL || params.IARDY == NULL) { code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate text region image data"); goto cleanup4; } } code = jbig2_decode_text_region(ctx, segment, ¶ms, (const Jbig2SymbolDict * const *)dicts, n_dicts, image, segment_data + offset, segment->data_length - offset, GR_stats, as, ws); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to decode text region image data"); goto cleanup4; } if ((segment->flags & 63) == 4) { /* we have an intermediate region here. save it for later */ segment->result = jbig2_image_reference(ctx, image); } else { /* otherwise composite onto the page */ jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, "composing %dx%d decoded text region onto page at (%d, %d)", region_info.width, region_info.height, region_info.x, region_info.y); code = jbig2_page_add_result(ctx, &ctx->pages[ctx->current_page], image, region_info.x, region_info.y, region_info.op); if (code < 0) jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "unable to add text region to page"); } cleanup4: if (!params.SBHUFF) { jbig2_arith_iaid_ctx_free(ctx, params.IAID); jbig2_arith_int_ctx_free(ctx, params.IARI); jbig2_arith_int_ctx_free(ctx, params.IARDW); jbig2_arith_int_ctx_free(ctx, params.IARDH); jbig2_arith_int_ctx_free(ctx, params.IARDX); jbig2_arith_int_ctx_free(ctx, params.IARDY); } cleanup3: if (!params.SBHUFF) { jbig2_arith_int_ctx_free(ctx, params.IADT); jbig2_arith_int_ctx_free(ctx, params.IAFS); jbig2_arith_int_ctx_free(ctx, params.IADS); jbig2_arith_int_ctx_free(ctx, params.IAIT); } jbig2_free(ctx->allocator, as); jbig2_word_stream_buf_free(ctx, ws); cleanup2: jbig2_free(ctx->allocator, GR_stats); jbig2_image_release(ctx, image); cleanup1: if (params.SBHUFF) { jbig2_release_huffman_table(ctx, params.SBHUFFFS); jbig2_release_huffman_table(ctx, params.SBHUFFDS); jbig2_release_huffman_table(ctx, params.SBHUFFDT); jbig2_release_huffman_table(ctx, params.SBHUFFRDX); jbig2_release_huffman_table(ctx, params.SBHUFFRDY); jbig2_release_huffman_table(ctx, params.SBHUFFRDW); jbig2_release_huffman_table(ctx, params.SBHUFFRDH); jbig2_release_huffman_table(ctx, params.SBHUFFRSIZE); } jbig2_free(ctx->allocator, dicts); return code; too_short: return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "segment too short"); } jbig2dec-0.18/jbig2_text.h000066400000000000000000000044231362055302000153240ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifndef _JBIG2_TEXT_H #define _JBIG2_TEXT_H /** * Headers for Text region handling **/ typedef enum { JBIG2_CORNER_BOTTOMLEFT = 0, JBIG2_CORNER_TOPLEFT = 1, JBIG2_CORNER_BOTTOMRIGHT = 2, JBIG2_CORNER_TOPRIGHT = 3 } Jbig2RefCorner; typedef struct { bool SBHUFF; bool SBREFINE; bool SBDEFPIXEL; Jbig2ComposeOp SBCOMBOP; bool TRANSPOSED; Jbig2RefCorner REFCORNER; int SBDSOFFSET; /* int SBW; */ /* int SBH; */ uint32_t SBNUMINSTANCES; int LOGSBSTRIPS; int SBSTRIPS; /* int SBNUMSYMS; */ /* SBSYMCODES */ /* SBSYMCODELEN */ /* SBSYMS */ Jbig2HuffmanTable *SBHUFFFS; Jbig2HuffmanTable *SBHUFFDS; Jbig2HuffmanTable *SBHUFFDT; Jbig2HuffmanTable *SBHUFFRDW; Jbig2HuffmanTable *SBHUFFRDH; Jbig2HuffmanTable *SBHUFFRDX; Jbig2HuffmanTable *SBHUFFRDY; Jbig2HuffmanTable *SBHUFFRSIZE; Jbig2ArithIntCtx *IADT; Jbig2ArithIntCtx *IAFS; Jbig2ArithIntCtx *IADS; Jbig2ArithIntCtx *IAIT; Jbig2ArithIaidCtx *IAID; Jbig2ArithIntCtx *IARI; Jbig2ArithIntCtx *IARDW; Jbig2ArithIntCtx *IARDH; Jbig2ArithIntCtx *IARDX; Jbig2ArithIntCtx *IARDY; bool SBRTEMPLATE; int8_t sbrat[4]; } Jbig2TextRegionParams; int jbig2_decode_text_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const Jbig2TextRegionParams *params, const Jbig2SymbolDict *const *dicts, const uint32_t n_dicts, Jbig2Image *image, const byte *data, const size_t size, Jbig2ArithCx *GR_stats, Jbig2ArithState *as, Jbig2WordStream *ws); int jbig2_text_region(Jbig2Ctx *ctx, Jbig2Segment *segment, const uint8_t *segment_data); #endif /* _JBIG2_TEXT_H */ jbig2dec-0.18/jbig2dec.1000066400000000000000000000036261362055302000146510ustar00rootroot00000000000000.TH jbig2dec 1 "2020 February 11" "Version 0.18" "jbig2dec Manual" .SH NAME jbig2dec \- File format converter specialized in JBIG2 decoding .SH SYNOPSIS .B jbig2dec .RI [ options ] .I file.jbig2 .br .B jbig2dec .RI [ options ] .I global-stream page-stream .SH DESCRIPTION The .B jbig2dec command converts JBIG2 files to png or pbm files. When passed a single .I file argument it is interpreted as a JBIG2 file stream, with either sequential or random-access organization. When passed two stream arguments, they are interpreted as the global and page-specific portions of an embedded organization, as used in PDF. If a particular page references no global segment stream, /dev/null can be passed for the .I global-stream argument to request the embedded parser. .SH OPTIONS The options are as follows: .TP .BI -o " file" Store the decoded output in .IR file . Defaults to the input with a different extension. Set to \fI-\fR for standard output. .TP .BI -t " type" Force a particular output file format. Supported are \fIpng\fR and \fIpbm\fR. .TP .BR -d " or " --dump Print the structure of the JBIG2 file rather than explicitly decoding it. .TP .BR --hash Print a hash of the decoded document. .TP .BR -q " or " --quiet Suppress warnings and other diagnostic output. .TP .BR -v " or " --verbose Report additional information about the decoding process. Pass just \fB-v\fR for information about the file as it's being decoded. This is the same as \fB--verbose=2\fR. Pass \fB--verbose=3\fR or higher for debugging information. .TP .BR --version Show program version information. .TP .BR -h " or " --help Show usage summary. .SH REPORTING BUGS Report bugs at https://bugs.ghostscript.com Contact the developers at the IRC channel #ghostscript at freenode.net or via the mailing list . .SH AUTHOR This manpage was initially written by Sebastian Rasmussen for jbig2dec and the Debian Project. jbig2dec-0.18/jbig2dec.c000066400000000000000000000412611362055302000147300ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifdef HAVE_GETOPT_H # include #else # include "getopt.h" #endif #include "os_types.h" #include "sha1.h" #ifdef JBIG_EXTERNAL_MEMENTO_H #include JBIG_EXTERNAL_MEMENTO_H #else #include "memento.h" #endif #include "jbig2.h" #include "jbig2_priv.h" #include "jbig2_image.h" #include "jbig2_image_rw.h" typedef enum { usage, dump, render } jbig2dec_mode; typedef enum { jbig2dec_format_none, jbig2dec_format_jbig2, jbig2dec_format_pbm, #ifdef HAVE_LIBPNG jbig2dec_format_png, #endif } jbig2dec_format; typedef struct { jbig2dec_mode mode; int verbose, hash, embedded; SHA1_CTX *hash_ctx; char *output_filename; jbig2dec_format output_format; char *last_message; Jbig2Severity severity; char *type; long repeats; } jbig2dec_params_t; static int print_version(void); static int print_usage(void); /* page hashing functions */ static void hash_init(jbig2dec_params_t *params) { params->hash_ctx = (SHA1_CTX *) malloc(sizeof(SHA1_CTX)); if (params->hash_ctx == NULL) { fprintf(stderr, "unable to allocate hash state\n"); params->hash = 0; return; } else { SHA1_Init(params->hash_ctx); } } static void hash_image(jbig2dec_params_t *params, Jbig2Image *image) { unsigned int N = image->stride * image->height; SHA1_Update(params->hash_ctx, image->data, N); } static void hash_print(jbig2dec_params_t *params, FILE *out) { unsigned char md[SHA1_DIGEST_SIZE]; char digest[2 * SHA1_DIGEST_SIZE + 1]; int i; SHA1_Final(params->hash_ctx, md); for (i = 0; i < SHA1_DIGEST_SIZE; i++) { snprintf(&(digest[2 * i]), 3, "%02x", md[i]); } fprintf(out, "%s", digest); } static void hash_free(jbig2dec_params_t *params) { free(params->hash_ctx); params->hash_ctx = NULL; } static int set_output_format(jbig2dec_params_t *params, const char *format) { #ifdef HAVE_LIBPNG /* this should really by strncasecmp() TODO: we need to provide our own for portability */ if (!strncmp(format, "png", 3) || !strncmp(format, "PNG", 3)) { params->output_format = jbig2dec_format_png; return 0; } #endif /* default to pbm */ params->output_format = jbig2dec_format_pbm; return 0; } static int parse_options(int argc, char *argv[], jbig2dec_params_t *params) { static struct option long_options[] = { {"version", 0, NULL, 'V'}, {"help", 0, NULL, 'h'}, {"quiet", 0, NULL, 'q'}, {"verbose", 2, NULL, 'v'}, {"dump", 0, NULL, 'd'}, {"hash", 0, NULL, 'm'}, {"output", 1, NULL, 'o'}, {"format", 1, NULL, 't'}, {"embedded", 0, NULL, 'e'}, {NULL, 0, NULL, 0} }; int option_idx = 1; int option; while (1) { option = getopt_long(argc, argv, "Vh?qv:do:t:e", long_options, &option_idx); if (option == -1) break; switch (option) { case 0: /* unknown long option */ if (!params->verbose) fprintf(stdout, "unrecognized option: --%s\n", long_options[option_idx].name); break; case 'q': params->verbose = 0; break; case 'v': if (optarg) params->verbose = atoi(optarg); else params->verbose = 2; break; case 'h': case '?': params->mode = usage; break; case 'V': /* the GNU Coding Standards suggest --version should override all other options */ print_version(); exit(0); break; case 'd': params->mode = dump; break; case 'm': params->hash = 1; break; case 'o': params->output_filename = strdup(optarg); break; case 't': set_output_format(params, optarg); break; case 'e': params->embedded = 1; break; default: if (!params->verbose) fprintf(stdout, "unrecognized option: -%c\n", option); break; } } return (optind); } static int print_version(void) { fprintf(stdout, "jbig2dec %d.%d\n", JBIG2_VERSION_MAJOR, JBIG2_VERSION_MINOR); return 0; } static int print_usage(void) { fprintf(stderr, "Usage: jbig2dec [options] \n" " or jbig2dec [options] \n" "\n" " When invoked with a single file, it attempts to parse it as\n" " a normal jbig2 file. Invoked with two files, it treats the\n" " first as the global segments, and the second as the segment\n" " stream for a particular page. This is useful for examining\n" " embedded streams.\n" "\n" " available options:\n" " -h --help this usage summary\n" " -q --quiet suppress diagnostic output\n" " -v --verbose set the verbosity level\n" " -d --dump print the structure of the jbig2 file\n" " rather than explicitly decoding\n" " --version program name and version information\n" " --hash print a hash of the decoded document\n" " -e --embedded expect embedded bit stream without file header\n" " -o send decoded output to \n" " Defaults to the the input with a different\n" " extension. Pass '-' for stdout.\n" " -t force a particular output file format\n" #ifdef HAVE_LIBPNG " supported options are 'png' and 'pbm'\n" #else " the only supported option is 'pbm'\n" #endif "\n"); return 1; } static void error_callback(void *error_callback_data, const char *buf, Jbig2Severity severity, int32_t seg_idx) { jbig2dec_params_t *params = (jbig2dec_params_t *) error_callback_data; char *type; char segment[22]; int len; char *message; switch (severity) { case JBIG2_SEVERITY_DEBUG: if (params->verbose < 3) return; type = "DEBUG"; break; case JBIG2_SEVERITY_INFO: if (params->verbose < 2) return; type = "info"; break; case JBIG2_SEVERITY_WARNING: if (params->verbose < 1) return; type = "WARNING"; break; case JBIG2_SEVERITY_FATAL: type = "FATAL ERROR"; break; default: type = "unknown message"; break; } if (seg_idx == -1) segment[0] = '\0'; else snprintf(segment, sizeof(segment), "(segment 0x%02x)", seg_idx); len = snprintf(NULL, 0, "jbig2dec %s %s %s", type, buf, segment); if (len < 0) { return; } message = malloc(len + 1); if (message == NULL) { return; } len = snprintf(message, len + 1, "jbig2dec %s %s %s", type, buf, segment); if (len < 0) { free(message); return; } if (params->last_message != NULL && strcmp(message, params->last_message)) { if (params->repeats > 1) fprintf(stderr, "jbig2dec %s last message repeated %ld times\n", params->type, params->repeats); fprintf(stderr, "%s\n", message); free(params->last_message); params->last_message = message; params->severity = severity; params->type = type; params->repeats = 0; } else if (params->last_message != NULL) { params->repeats++; if (params->repeats % 1000000 == 0) fprintf(stderr, "jbig2dec %s last message repeated %ld times so far\n", params->type, params->repeats); free(message); } else if (params->last_message == NULL) { fprintf(stderr, "%s\n", message); params->last_message = message; params->severity = severity; params->type = type; params->repeats = 0; } } static void flush_errors(jbig2dec_params_t *params) { if (params->repeats > 1) { fprintf(stderr, "jbig2dec last message repeated %ld times\n", params->repeats); } } static char * make_output_filename(const char *input_filename, const char *extension) { char *output_filename; const char *c, *e; int len; if (extension == NULL) { fprintf(stderr, "no filename extension; cannot create output filename!\n"); exit(1); } if (input_filename == NULL) c = "out"; else { /* strip any leading path */ c = strrchr(input_filename, '/'); /* *nix */ if (c == NULL) c = strrchr(input_filename, '\\'); /* win32/dos */ if (c != NULL) c++; /* skip the path separator */ else c = input_filename; /* no leading path */ } /* make sure we haven't just stripped the last character */ if (*c == '\0') c = "out"; /* strip the extension */ len = strlen(c); e = strrchr(c, '.'); if (e != NULL) len -= strlen(e); /* allocate enough space for the base + ext */ output_filename = (char *)malloc(len + strlen(extension) + 1); if (output_filename == NULL) { fprintf(stderr, "failed to allocate memory for output filename\n"); exit(1); } strncpy(output_filename, c, len); strncpy(output_filename + len, extension, strlen(extension)); *(output_filename + len + strlen(extension)) = '\0'; /* return the new string */ return (output_filename); } static int write_page_image(jbig2dec_params_t *params, FILE *out, Jbig2Image *image) { switch (params->output_format) { #ifdef HAVE_LIBPNG case jbig2dec_format_png: return jbig2_image_write_png(image, out); #endif case jbig2dec_format_pbm: return jbig2_image_write_pbm(image, out); default: fprintf(stderr, "unsupported output format.\n"); return 1; } return 0; } static int write_document_hash(jbig2dec_params_t *params) { FILE *out; if (!strncmp(params->output_filename, "-", 2)) { out = stderr; } else { out = stdout; } fprintf(out, "Hash of decoded document: "); hash_print(params, out); fprintf(out, "\n"); return 0; } int main(int argc, char **argv) { FILE *f = NULL, *f_page = NULL; Jbig2Ctx *ctx = NULL; uint8_t buf[4096]; jbig2dec_params_t params; int filearg; int result = 1; int code; /* set defaults */ params.mode = render; params.verbose = 1; params.hash = 0; params.output_filename = NULL; params.output_format = jbig2dec_format_none; params.embedded = 0; params.last_message = NULL; params.repeats = 0; filearg = parse_options(argc, argv, ¶ms); if (params.hash) hash_init(¶ms); switch (params.mode) { case usage: print_usage(); exit(0); break; case dump: fprintf(stderr, "Sorry, segment dump not yet implemented\n"); break; case render: if ((argc - filearg) == 1) { /* only one argument--open as a jbig2 file */ char *fn = argv[filearg]; f = fopen(fn, "rb"); if (f == NULL) { fprintf(stderr, "error opening %s\n", fn); goto cleanup; } } else if ((argc - filearg) == 2) { /* two arguments open as separate global and page streams */ char *fn = argv[filearg]; char *fn_page = argv[filearg + 1]; f = fopen(fn, "rb"); if (f == NULL) { fprintf(stderr, "error opening %s\n", fn); goto cleanup; } f_page = fopen(fn_page, "rb"); if (f_page == NULL) { fclose(f); fprintf(stderr, "error opening %s\n", fn_page); goto cleanup; } } else { /* any other number of arguments */ result = print_usage(); goto cleanup; } ctx = jbig2_ctx_new(NULL, (Jbig2Options)(f_page != NULL || params.embedded ? JBIG2_OPTIONS_EMBEDDED : 0), NULL, error_callback, ¶ms); if (ctx == NULL) { fclose(f); if (f_page) fclose(f_page); goto cleanup; } /* pull the whole file/global stream into memory */ for (;;) { int n_bytes = fread(buf, 1, sizeof(buf), f); if (n_bytes <= 0) break; if (jbig2_data_in(ctx, buf, n_bytes)) break; } fclose(f); /* if there's a local page stream read that in its entirety */ if (f_page != NULL) { Jbig2GlobalCtx *global_ctx = jbig2_make_global_ctx(ctx); ctx = jbig2_ctx_new(NULL, JBIG2_OPTIONS_EMBEDDED, global_ctx, error_callback, ¶ms); if (ctx != NULL) { for (;;) { int n_bytes = fread(buf, 1, sizeof(buf), f_page); if (n_bytes <= 0) break; if (jbig2_data_in(ctx, buf, n_bytes)) break; } } fclose(f_page); jbig2_global_ctx_free(global_ctx); } /* retrieve and output the returned pages */ { Jbig2Image *image; FILE *out; /* always complete a page, working around streams that lack end of page segments: broken CVision streams, embedded streams or streams with parse errors. */ code = jbig2_complete_page(ctx); if (code < 0) { jbig2_error(ctx, JBIG2_SEVERITY_WARNING, -1, "unable to complete page"); goto cleanup; } if (params.output_filename == NULL) { switch (params.output_format) { #ifdef HAVE_LIBPNG case jbig2dec_format_png: params.output_filename = make_output_filename(argv[filearg], ".png"); break; #endif case jbig2dec_format_pbm: params.output_filename = make_output_filename(argv[filearg], ".pbm"); break; default: fprintf(stderr, "unsupported output format: %d\n", params.output_format); goto cleanup; } } else { int len = strlen(params.output_filename); if ((len >= 3) && (params.output_format == jbig2dec_format_none)) /* try to set the output type by the given extension */ set_output_format(¶ms, params.output_filename + len - 3); } if (!strncmp(params.output_filename, "-", 2)) { out = stdout; } else { if (params.verbose > 1) fprintf(stderr, "saving decoded page as '%s'\n", params.output_filename); if ((out = fopen(params.output_filename, "wb")) == NULL) { fprintf(stderr, "unable to open '%s' for writing\n", params.output_filename); goto cleanup; } } /* retrieve and write out all the completed pages */ while ((image = jbig2_page_out(ctx)) != NULL) { write_page_image(¶ms, out, image); if (params.hash) hash_image(¶ms, image); jbig2_release_page(ctx, image); } if (out != stdout) fclose(out); if (params.hash) write_document_hash(¶ms); } } /* end params.mode switch */ /* fin */ result = 0; cleanup: flush_errors(¶ms); jbig2_ctx_free(ctx); if (params.last_message) free(params.last_message); if (params.output_filename) free(params.output_filename); if (params.hash) hash_free(¶ms); return result; } jbig2dec-0.18/jbig2dec.pc.in000066400000000000000000000003441362055302000155120ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libjbig2dec Description: JBIG2 decoder library. Version: @VERSION@ Libs: -L${libdir} -ljbig2dec Libs.private: -lm Cflags: -I${includedir} jbig2dec-0.18/memcmp.c000066400000000000000000000024741362055302000145400ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* replacement for broken memcmp() */ /* * compares two byte strings 'a' and 'b', both assumed to be 'len' bytes long * returns zero if the two strings are identical, otherwise returns -1 or 1 * depending on the relative magnitude of the first differing elements, * considered as unsigned chars */ int memcmp(const void *b1, const void *b2, size_t len) { unsigned char *a, *b; size_t i; a = (unsigned char *)b1; b = (unsigned char *)b2; for (i = 0; i < len; i++) { if (*a != *b) { /* strings differ */ return (*a < *b) ? -1 : 1; } a++; b++; } /* strings match */ return 0; } jbig2dec-0.18/memento.c000066400000000000000000002511711362055302000147260ustar00rootroot00000000000000/* Copyright (C) 2009-2018 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of that license. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* Inspired by Fortify by Simon P Bullen. */ /* Set the following if you're only looking for leaks, not memory overwrites * to speed the operation */ /* #define MEMENTO_LEAKONLY */ /* Set the following to keep extra details about the history of blocks */ #define MEMENTO_DETAILS /* Don't keep blocks around if they'd mean losing more than a quarter of * the freelist. */ #define MEMENTO_FREELIST_MAX_SINGLE_BLOCK (MEMENTO_FREELIST_MAX/4) #define COMPILING_MEMENTO_C /* SHUT UP, MSVC. I KNOW WHAT I AM DOING. */ #define _CRT_SECURE_NO_WARNINGS /* We have some GS specific tweaks; more for the GS build environment than * anything else. */ /* #define MEMENTO_GS_HACKS */ #ifdef MEMENTO_GS_HACKS /* For GS we include malloc_.h. Anyone else would just include memento.h */ #include "malloc_.h" #include "memory_.h" int atexit(void (*)(void)); #else #include "memento.h" #include #endif #ifndef _MSC_VER #include #include #include #endif #include #include #include #ifdef __ANDROID__ #define MEMENTO_ANDROID #include #endif /* Hacks to portably print large sizes */ #ifdef _MSC_VER #define FMTZ "%llu" #define FMTZ_CAST _int64 #define FMTP "0x%p" #else #define FMTZ "%zu" #define FMTZ_CAST size_t #define FMTP "%p" #endif #define UB(x) ((intptr_t)((x) & 0xFF)) #define B2I(x) (UB(x) | (UB(x)<<8) | (UB(x)<<16) | (UB(x)<<24)) #define B2P(x) ((void *)(B2I(x) | ((B2I(x)<<16)<<16))) #define MEMENTO_PREFILL_UBYTE ((unsigned char)(MEMENTO_PREFILL)) #define MEMENTO_PREFILL_USHORT (((unsigned short)MEMENTO_PREFILL_UBYTE) | (((unsigned short)MEMENTO_PREFILL_UBYTE)<<8)) #define MEMENTO_PREFILL_UINT (((unsigned int)MEMENTO_PREFILL_USHORT) | (((unsigned int)MEMENTO_PREFILL_USHORT)<<16)) #define MEMENTO_PREFILL_PTR (void *)(((uintptr_t)MEMENTO_PREFILL_UINT) | ((((uintptr_t)MEMENTO_PREFILL_UINT)<<16)<<16)) #define MEMENTO_POSTFILL_UBYTE ((unsigned char)(MEMENTO_POSTFILL)) #define MEMENTO_POSTFILL_USHORT (((unsigned short)MEMENTO_POSTFILL_UBYTE) | (((unsigned short)MEMENTO_POSTFILL_UBYTE)<<8)) #define MEMENTO_POSTFILL_UINT (((unsigned int)MEMENTO_POSTFILL_USHORT) | (((unsigned int)MEMENTO_POSTFILL_USHORT)<<16)) #define MEMENTO_POSTFILL_PTR (void *)(((uintptr_t)MEMENTO_POSTFILL_UINT) | ((((uintptr_t)MEMENTO_POSTFILL_UINT)<<16)<<16)) #define MEMENTO_ALLOCFILL_UBYTE ((unsigned char)(MEMENTO_ALLOCFILL)) #define MEMENTO_ALLOCFILL_USHORT (((unsigned short)MEMENTO_ALLOCFILL_UBYTE) | (((unsigned short)MEMENTO_ALLOCFILL_UBYTE)<<8)) #define MEMENTO_ALLOCFILL_UINT (((unsigned int)MEMENTO_ALLOCFILL_USHORT) | (((unsigned int)MEMENTO_ALLOCFILL_USHORT)<<16)) #define MEMENTO_ALLOCFILL_PTR (void *)(((uintptr_t)MEMENTO_ALLOCFILL_UINT) | ((((uintptr_t)MEMENTO_ALLOCFILL_UINT)<<16)<<16)) #define MEMENTO_FREEFILL_UBYTE ((unsigned char)(MEMENTO_FREEFILL)) #define MEMENTO_FREEFILL_USHORT (((unsigned short)MEMENTO_FREEFILL_UBYTE) | (((unsigned short)MEMENTO_FREEFILL_UBYTE)<<8)) #define MEMENTO_FREEFILL_UINT (((unsigned int)MEMENTO_FREEFILL_USHORT) | (((unsigned int)MEMENTO_FREEFILL_USHORT)<<16)) #define MEMENTO_FREEFILL_PTR (void *)(((uintptr_t)MEMENTO_FREEFILL_UINT) | ((((uintptr_t)MEMENTO_FREEFILL_UINT)<<16)<<16)) #ifdef MEMENTO #ifndef MEMENTO_CPP_EXTRAS_ONLY #ifdef MEMENTO_ANDROID #include static char log_buffer[4096]; static int log_fill = 0; static char log_buffer2[4096]; static int android_fprintf(FILE *file, const char *fmt, ...) { va_list args; char *p, *q; va_start(args, fmt); vsnprintf(log_buffer2, sizeof(log_buffer2)-1, fmt, args); va_end(args); /* Ensure we are always null terminated */ log_buffer2[sizeof(log_buffer2)-1] = 0; p = log_buffer2; q = p; do { /* Find the end of the string, or the next \n */ while (*p && *p != '\n') p++; /* We need to output from q to p. Limit ourselves to what * will fit in the existing */ if (p - q >= sizeof(log_buffer)-1 - log_fill) p = q + sizeof(log_buffer)-1 - log_fill; memcpy(&log_buffer[log_fill], q, p-q); log_fill += p-q; if (*p == '\n') { log_buffer[log_fill] = 0; __android_log_print(ANDROID_LOG_ERROR, "memento", "%s", log_buffer); usleep(1); log_fill = 0; p++; /* Skip over the \n */ } else if (log_fill >= sizeof(log_buffer)-1) { log_buffer[sizeof(log_buffer2)-1] = 0; __android_log_print(ANDROID_LOG_ERROR, "memento", "%s", log_buffer); usleep(1); log_fill = 0; } q = p; } while (*p); return 0; } #define fprintf android_fprintf #define MEMENTO_STACKTRACE_METHOD 3 #endif /* _WIN64 defined implies _WIN32 will be */ #ifdef _WIN32 #include static int windows_fprintf(FILE *file, const char *fmt, ...) { va_list args; char text[4096]; int ret; va_start(args, fmt); ret = vfprintf(file, fmt, args); va_end(args); va_start(args, fmt); vsnprintf(text, 4096, fmt, args); OutputDebugStringA(text); va_end(args); return ret; } #define fprintf windows_fprintf #endif #ifndef MEMENTO_STACKTRACE_METHOD #ifdef __GNUC__ #define MEMENTO_STACKTRACE_METHOD 1 #endif #ifdef _WIN32 #define MEMENTO_STACKTRACE_METHOD 2 #endif #endif #if defined(__linux__) #define MEMENTO_HAS_FORK #elif defined(__APPLE__) && defined(__MACH__) #define MEMENTO_HAS_FORK #endif /* Define the underlying allocators, just in case */ void *MEMENTO_UNDERLYING_MALLOC(size_t); void MEMENTO_UNDERLYING_FREE(void *); void *MEMENTO_UNDERLYING_REALLOC(void *,size_t); void *MEMENTO_UNDERLYING_CALLOC(size_t,size_t); /* And some other standard functions we use. We don't include the header * files, just in case they pull in unexpected others. */ int atoi(const char *); char *getenv(const char *); /* How far to search for pointers in each block when calculating nestings */ /* mupdf needs at least 34000ish (sizeof(fz_shade))/ */ #define MEMENTO_PTRSEARCH 65536 #ifndef MEMENTO_MAXPATTERN #define MEMENTO_MAXPATTERN 0 #endif #ifdef MEMENTO_GS_HACKS #include "valgrind.h" #else #ifdef HAVE_VALGRIND #include "valgrind/memcheck.h" #else #define VALGRIND_MAKE_MEM_NOACCESS(p,s) do { } while (0==1) #define VALGRIND_MAKE_MEM_UNDEFINED(p,s) do { } while (0==1) #define VALGRIND_MAKE_MEM_DEFINED(p,s) do { } while (0==1) #endif #endif enum { Memento_PreSize = 16, Memento_PostSize = 16 }; /* Some compile time checks */ typedef struct { char MEMENTO_PRESIZE_MUST_BE_A_MULTIPLE_OF_4[Memento_PreSize & 3 ? -1 : 1]; char MEMENTO_POSTSIZE_MUST_BE_A_MULTIPLE_OF_4[Memento_PostSize & 3 ? -1 : 1]; char MEMENTO_POSTSIZE_MUST_BE_AT_LEAST_4[Memento_PostSize >= 4 ? 1 : -1]; char MEMENTO_PRESIZE_MUST_BE_AT_LEAST_4[Memento_PreSize >= 4 ? 1 : -1]; } MEMENTO_SANITY_CHECK_STRUCT; #define MEMENTO_UINT32 unsigned int #define MEMENTO_UINT16 unsigned short #define MEMENTO_PREFILL_UINT32 ((MEMENTO_UINT32)(MEMENTO_PREFILL | (MEMENTO_PREFILL <<8) | (MEMENTO_PREFILL <<16) |(MEMENTO_PREFILL <<24))) #define MEMENTO_POSTFILL_UINT16 ((MEMENTO_UINT16)(MEMENTO_POSTFILL | (MEMENTO_POSTFILL<<8))) #define MEMENTO_POSTFILL_UINT32 ((MEMENTO_UINT32)(MEMENTO_POSTFILL | (MEMENTO_POSTFILL<<8) | (MEMENTO_POSTFILL<<16) |(MEMENTO_POSTFILL<<24))) #define MEMENTO_FREEFILL_UINT16 ((MEMENTO_UINT16)(MEMENTO_FREEFILL | (MEMENTO_FREEFILL<<8))) #define MEMENTO_FREEFILL_UINT32 ((MEMENTO_UINT32)(MEMENTO_FREEFILL | (MEMENTO_FREEFILL<<8) | (MEMENTO_FREEFILL<<16) |(MEMENTO_FREEFILL<<24))) enum { Memento_Flag_OldBlock = 1, Memento_Flag_HasParent = 2, Memento_Flag_BreakOnFree = 4, Memento_Flag_BreakOnRealloc = 8, Memento_Flag_Freed = 16, Memento_Flag_KnownLeak = 32, Memento_Flag_Reported = 64 }; enum { Memento_EventType_malloc = 0, Memento_EventType_calloc = 1, Memento_EventType_realloc = 2, Memento_EventType_free = 3, Memento_EventType_new = 4, Memento_EventType_delete = 5, Memento_EventType_newArray = 6, Memento_EventType_deleteArray = 7, Memento_EventType_takeRef = 8, Memento_EventType_dropRef = 9, Memento_EventType_reference = 10 }; static const char *eventType[] = { "malloc", "calloc", "realloc", "free", "new", "delete", "new[]", "delete[]", "takeRef", "dropRef", "reference" }; /* When we list leaked blocks at the end of execution, we search for pointers * between blocks in order to be able to give a nice nested view. * Unfortunately, if you have are running your own allocator (such as * postscript's chunk allocator) you can often find that the header of the * block always contains pointers to next or previous blocks. This tends to * mean the nesting displayed is "uninteresting" at best :) * * As a hack to get around this, we have a define MEMENTO_SKIP_SEARCH that * indicates how many bytes to skip over at the start of the chunk. * This may cause us to miss true nestings, but such is life... */ #ifndef MEMENTO_SEARCH_SKIP #ifdef MEMENTO_GS_HACKS #define MEMENTO_SEARCH_SKIP (2*sizeof(void *)) #else #define MEMENTO_SEARCH_SKIP 0 #endif #endif #define MEMENTO_CHILD_MAGIC ((Memento_BlkHeader *)('M' | ('3' << 8) | ('m' << 16) | ('3' << 24))) #define MEMENTO_SIBLING_MAGIC ((Memento_BlkHeader *)('n' | ('t' << 8) | ('0' << 16) | ('!' << 24))) #ifdef MEMENTO_DETAILS typedef struct Memento_BlkDetails Memento_BlkDetails; struct Memento_BlkDetails { Memento_BlkDetails *next; char type; char count; int sequence; void *stack[1]; }; #endif /* MEMENTO_DETAILS */ typedef struct Memento_BlkHeader Memento_BlkHeader; struct Memento_BlkHeader { size_t rawsize; int sequence; int lastCheckedOK; int flags; Memento_BlkHeader *next; Memento_BlkHeader *prev; /* Reused as 'parent' when printing nested list */ const char *label; /* Entries for nesting display calculations. Set to magic * values at all other time. */ Memento_BlkHeader *child; Memento_BlkHeader *sibling; #ifdef MEMENTO_DETAILS Memento_BlkDetails *details; Memento_BlkDetails **details_tail; #endif char preblk[Memento_PreSize]; }; /* In future this could (should) be a smarter data structure, like, say, * splay trees. For now, we use a list. */ typedef struct Memento_Blocks { Memento_BlkHeader *head; Memento_BlkHeader *tail; } Memento_Blocks; /* What sort of Mutex should we use? */ #ifdef MEMENTO_LOCKLESS typedef int Memento_mutex; static void Memento_initMutex(Memento_mutex *m) { (void)m; } #define MEMENTO_DO_LOCK() do { } while (0) #define MEMENTO_DO_UNLOCK() do { } while (0) #else #if defined(_WIN32) || defined(_WIN64) /* Windows */ typedef CRITICAL_SECTION Memento_mutex; static void Memento_initMutex(Memento_mutex *m) { InitializeCriticalSection(m); } #define MEMENTO_DO_LOCK() \ EnterCriticalSection(&memento.mutex) #define MEMENTO_DO_UNLOCK() \ LeaveCriticalSection(&memento.mutex) #else #include typedef pthread_mutex_t Memento_mutex; static void Memento_initMutex(Memento_mutex *m) { pthread_mutex_init(m, NULL); } #define MEMENTO_DO_LOCK() \ pthread_mutex_lock(&memento.mutex) #define MEMENTO_DO_UNLOCK() \ pthread_mutex_unlock(&memento.mutex) #endif #endif /* And our global structure */ static struct { int inited; Memento_Blocks used; Memento_Blocks free; size_t freeListSize; int sequence; int paranoia; int paranoidAt; int countdown; int lastChecked; int breakAt; int failAt; int failing; int nextFailAt; int squeezeAt; int squeezing; int segv; int pattern; int nextPattern; int patternBit; int leaking; size_t maxMemory; size_t alloc; size_t peakAlloc; size_t totalAlloc; size_t numMallocs; size_t numFrees; size_t numReallocs; Memento_mutex mutex; } memento; #define MEMENTO_EXTRASIZE (sizeof(Memento_BlkHeader) + Memento_PostSize) /* Round up size S to the next multiple of N (where N is a power of 2) */ #define MEMENTO_ROUNDUP(S,N) ((S + N-1)&~(N-1)) #define MEMBLK_SIZE(s) MEMENTO_ROUNDUP(s + MEMENTO_EXTRASIZE, MEMENTO_MAXALIGN) #define MEMBLK_FROMBLK(B) (&((Memento_BlkHeader*)(void *)(B))[-1]) #define MEMBLK_TOBLK(B) ((void*)(&((Memento_BlkHeader*)(void*)(B))[1])) #define MEMBLK_POSTPTR(B) \ (&((unsigned char *)(void *)(B))[(B)->rawsize + sizeof(Memento_BlkHeader)]) enum { SkipStackBackTraceLevels = 4 }; #if defined(MEMENTO_STACKTRACE_METHOD) && MEMENTO_STACKTRACE_METHOD == 1 extern size_t backtrace(void **, int); extern void backtrace_symbols_fd(void **, size_t, int); extern char **backtrace_symbols(void **, size_t); #define MEMENTO_BACKTRACE_MAX 256 static void (*print_stack_value)(void *address); /* Libbacktrace gubbins - relies on us having libdl to load the .so */ #ifdef HAVE_LIBDL #include typedef void (*backtrace_error_callback) (void *data, const char *msg, int errnum); typedef struct backtrace_state *(*backtrace_create_state_type)( const char *filename, int threaded, backtrace_error_callback error_callback, void *data); typedef int (*backtrace_full_callback) (void *data, uintptr_t pc, const char *filename, int lineno, const char *function); typedef int (*backtrace_pcinfo_type)(struct backtrace_state *state, uintptr_t pc, backtrace_full_callback callback, backtrace_error_callback error_callback, void *data); typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize); typedef int (*backtrace_syminfo_type)(struct backtrace_state *state, uintptr_t addr, backtrace_syminfo_callback callback, backtrace_error_callback error_callback, void *data); static backtrace_syminfo_type backtrace_syminfo; static backtrace_create_state_type backtrace_create_state; static backtrace_pcinfo_type backtrace_pcinfo; static struct backtrace_state *my_backtrace_state; static void *libbt; static char backtrace_exe[4096]; static void *current_addr; static void error2_cb(void *data, const char *msg, int errnum) { } static void syminfo_cb(void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize) { if (sizeof(void *) == 4) fprintf(stderr, " 0x%08lx %s\n", pc, symname?symname:"?"); else fprintf(stderr, " 0x%016lx %s\n", pc, symname?symname:"?"); } static void error_cb(void *data, const char *msg, int errnum) { backtrace_syminfo(my_backtrace_state, (uintptr_t)current_addr, syminfo_cb, error2_cb, NULL); } static int full_cb(void *data, uintptr_t pc, const char *fname, int line, const char *fn) { if (sizeof(void *) == 4) fprintf(stderr, " 0x%08lx %s(%s:%d)\n", pc, fn?fn:"?", fname?fname:"?", line); else fprintf(stderr, " 0x%016lx %s(%s:%d)\n", pc, fn?fn:"?", fname?fname:"?", line); return 0; } static void print_stack_libbt(void *addr) { current_addr = addr; backtrace_pcinfo(my_backtrace_state, (uintptr_t)addr, full_cb, error_cb, NULL); } static void print_stack_libbt_failed(void *addr) { char **strings; #if 0 /* Let's use a hack from Julian Smith to call gdb to extract the information */ /* Disabled for now, as I can't make this work. */ static char command[1024]; int e; static int gdb_invocation_failed = 0; if (gdb_invocation_failed == 0) { snprintf(command, sizeof(command), //"gdb -q --batch -p=%i -ex 'info line *%p' -ex quit 2>/dev/null", "gdb -q --batch -p=%i -ex 'info line *%p' -ex quit 2>/dev/null| egrep -v '(Thread debugging using)|(Using host libthread_db library)|(A debugging session is active)|(will be detached)|(Quit anyway)|(No such file or directory)|(^0x)|(^$)'", getpid(), addr); printf("%s\n", command); e = system(command); if (e == 0) return; /* That'll do! */ gdb_invocation_failed = 1; /* If it's failed once, it'll probably keep failing. */ } #endif /* We couldn't even get gdb! Make do. */ strings = backtrace_symbols(&addr, 1); if (strings == NULL || strings[0] == NULL) { if (sizeof(void *) == 4) fprintf(stderr, " [0x%08lx]\n", (uintptr_t)addr); else fprintf(stderr, " [0x%016lx]\n", (uintptr_t)addr); } else { fprintf(stderr, " %s\n", strings[0]); } (free)(strings); } static int init_libbt(void) { static int libbt_inited = 0; if (libbt_inited) return 0; libbt_inited = 1; libbt = dlopen("libbacktrace.so", RTLD_LAZY); if (libbt == NULL) libbt = dlopen("/opt/lib/libbacktrace.so", RTLD_LAZY); if (libbt == NULL) libbt = dlopen("/lib/libbacktrace.so", RTLD_LAZY); if (libbt == NULL) libbt = dlopen("/usr/lib/libbacktrace.so", RTLD_LAZY); if (libbt == NULL) libbt = dlopen("/usr/local/lib/libbacktrace.so", RTLD_LAZY); if (libbt == NULL) goto fail; backtrace_create_state = dlsym(libbt, "backtrace_create_state"); backtrace_syminfo = dlsym(libbt, "backtrace_syminfo"); backtrace_pcinfo = dlsym(libbt, "backtrace_pcinfo"); if (backtrace_create_state == NULL || backtrace_syminfo == NULL || backtrace_pcinfo == NULL) { goto fail; } my_backtrace_state = backtrace_create_state(backtrace_exe, 1 /*BACKTRACE_SUPPORTS_THREADS*/, error_cb, NULL); if (my_backtrace_state == NULL) goto fail; print_stack_value = print_stack_libbt; return 1; fail: fprintf(stderr, "MEMENTO: libbacktrace.so failed to load; backtraces will be sparse.\n" "MEMENTO: See memento.h for how to rectify this.\n"); libbt = NULL; backtrace_create_state = NULL; backtrace_syminfo = NULL; print_stack_value = print_stack_libbt_failed; return 0; } #endif static void print_stack_default(void *addr) { char **strings = backtrace_symbols(&addr, 1); if (strings == NULL || strings[0] == NULL) { fprintf(stderr, " ["FMTP"]\n", addr); } #ifdef HAVE_LIBDL else if (strchr(strings[0], ':') == NULL) { /* Probably a "path [address]" format string */ char *s = strchr(strings[0], ' '); if (s != strings[0]) { memcpy(backtrace_exe, strings[0], s - strings[0]); backtrace_exe[s-strings[0]] = 0; init_libbt(); print_stack_value(addr); } } #endif else { fprintf(stderr, " %s\n", strings[0]); } free(strings); } static void Memento_initStacktracer(void) { print_stack_value = print_stack_default; } static int Memento_getStacktrace(void **stack, int *skip) { size_t num; num = backtrace(&stack[0], MEMENTO_BACKTRACE_MAX); *skip = SkipStackBackTraceLevels; if (num <= SkipStackBackTraceLevels) return 0; return (int)(num-SkipStackBackTraceLevels); } static void Memento_showStacktrace(void **stack, int numberOfFrames) { int i; for (i = 0; i < numberOfFrames; i++) { print_stack_value(stack[i]); } } #elif defined(MEMENTO_STACKTRACE_METHOD) && MEMENTO_STACKTRACE_METHOD == 2 #include /* We use DbgHelp.dll rather than DbgHelp.lib. This avoids us needing * extra link time complications, and enables us to fall back gracefully * if the DLL cannot be found. * * To achieve this we have our own potted versions of the required types * inline here. */ #ifdef _WIN64 typedef DWORD64 DWORD_NATIVESIZED; #else typedef DWORD DWORD_NATIVESIZED; #endif #define MEMENTO_BACKTRACE_MAX 64 typedef USHORT (__stdcall *My_CaptureStackBackTraceType)(__in ULONG, __in ULONG, __out PVOID*, __out_opt PULONG); typedef struct MY_IMAGEHLP_LINE { DWORD SizeOfStruct; PVOID Key; DWORD LineNumber; PCHAR FileName; DWORD_NATIVESIZED Address; } MY_IMAGEHLP_LINE, *MY_PIMAGEHLP_LINE; typedef BOOL (__stdcall *My_SymGetLineFromAddrType)(HANDLE hProcess, DWORD_NATIVESIZED dwAddr, PDWORD pdwDisplacement, MY_PIMAGEHLP_LINE Line); typedef struct MY_SYMBOL_INFO { ULONG SizeOfStruct; ULONG TypeIndex; // Type Index of symbol ULONG64 Reserved[2]; ULONG info; ULONG Size; ULONG64 ModBase; // Base Address of module containing this symbol ULONG Flags; ULONG64 Value; // Value of symbol, ValuePresent should be 1 ULONG64 Address; // Address of symbol including base address of module ULONG Register; // register holding value or pointer to value ULONG Scope; // scope of the symbol ULONG Tag; // pdb classification ULONG NameLen; // Actual length of name ULONG MaxNameLen; CHAR Name[1]; // Name of symbol } MY_SYMBOL_INFO, *MY_PSYMBOL_INFO; typedef BOOL (__stdcall *My_SymFromAddrType)(HANDLE hProcess, DWORD64 Address, PDWORD64 Displacement, MY_PSYMBOL_INFO Symbol); typedef BOOL (__stdcall *My_SymInitializeType)(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess); static My_CaptureStackBackTraceType Memento_CaptureStackBackTrace; static My_SymGetLineFromAddrType Memento_SymGetLineFromAddr; static My_SymFromAddrType Memento_SymFromAddr; static My_SymInitializeType Memento_SymInitialize; static HANDLE Memento_process; static void Memento_initStacktracer(void) { HMODULE mod = LoadLibrary("kernel32.dll"); if (mod == NULL) return; Memento_CaptureStackBackTrace = (My_CaptureStackBackTraceType)(GetProcAddress(mod, "RtlCaptureStackBackTrace")); if (Memento_CaptureStackBackTrace == NULL) return; mod = LoadLibrary("Dbghelp.dll"); if (mod == NULL) { Memento_CaptureStackBackTrace = NULL; return; } Memento_SymGetLineFromAddr = (My_SymGetLineFromAddrType)(GetProcAddress(mod, #ifdef _WIN64 "SymGetLineFromAddr64" #else "SymGetLineFromAddr" #endif )); if (Memento_SymGetLineFromAddr == NULL) { Memento_CaptureStackBackTrace = NULL; return; } Memento_SymFromAddr = (My_SymFromAddrType)(GetProcAddress(mod, "SymFromAddr")); if (Memento_SymFromAddr == NULL) { Memento_CaptureStackBackTrace = NULL; return; } Memento_SymInitialize = (My_SymInitializeType)(GetProcAddress(mod, "SymInitialize")); if (Memento_SymInitialize == NULL) { Memento_CaptureStackBackTrace = NULL; return; } Memento_process = GetCurrentProcess(); Memento_SymInitialize(Memento_process, NULL, TRUE); } static int Memento_getStacktrace(void **stack, int *skip) { if (Memento_CaptureStackBackTrace == NULL) return 0; *skip = 0; /* Limit us to 63 levels due to windows bug */ return Memento_CaptureStackBackTrace(SkipStackBackTraceLevels, 63-SkipStackBackTraceLevels, stack, NULL); } static void Memento_showStacktrace(void **stack, int numberOfFrames) { MY_IMAGEHLP_LINE line; int i; char symbol_buffer[sizeof(MY_SYMBOL_INFO) + 1024 + 1]; MY_SYMBOL_INFO *symbol = (MY_SYMBOL_INFO *)symbol_buffer; symbol->MaxNameLen = 1024; symbol->SizeOfStruct = sizeof(MY_SYMBOL_INFO); line.SizeOfStruct = sizeof(MY_IMAGEHLP_LINE); for (i = 0; i < numberOfFrames; i++) { DWORD64 dwDisplacement64; DWORD dwDisplacement; Memento_SymFromAddr(Memento_process, (DWORD64)(stack[i]), &dwDisplacement64, symbol); Memento_SymGetLineFromAddr(Memento_process, (DWORD_NATIVESIZED)(stack[i]), &dwDisplacement, &line); fprintf(stderr, " %s in %s:%d\n", symbol->Name, line.FileName, line.LineNumber); } } #elif defined(MEMENTO_STACKTRACE_METHOD) && MEMENTO_STACKTRACE_METHOD == 3 #include #include /* From cxxabi.h */ extern char* __cxa_demangle(const char* mangled_name, char* output_buffer, size_t* length, int* status); static void Memento_initStacktracer(void) { } #define MEMENTO_BACKTRACE_MAX 256 typedef struct { int count; void **addr; } my_unwind_details; static _Unwind_Reason_Code unwind_populate_callback(struct _Unwind_Context *context, void *arg) { my_unwind_details *uw = (my_unwind_details *)arg; int count = uw->count; if (count >= MEMENTO_BACKTRACE_MAX) return _URC_END_OF_STACK; uw->addr[count] = (void *)_Unwind_GetIP(context); uw->count++; return _URC_NO_REASON; } static int Memento_getStacktrace(void **stack, int *skip) { my_unwind_details uw = { 0, stack }; *skip = 0; /* Collect the backtrace. Deliberately only unwind once, * and avoid using malloc etc until this completes just * in case. */ _Unwind_Backtrace(unwind_populate_callback, &uw); if (uw.count <= SkipStackBackTraceLevels) return 0; *skip = SkipStackBackTraceLevels; return uw.count-SkipStackBackTraceLevels; } static void Memento_showStacktrace(void **stack, int numberOfFrames) { int i; for (i = 0; i < numberOfFrames; i++) { Dl_info info; if (dladdr(stack[i], &info)) { int status = 0; const char *sym = info.dli_sname ? info.dli_sname : ""; char *demangled = __cxa_demangle(sym, NULL, 0, &status); int offset = stack[i] - info.dli_saddr; fprintf(stderr, " ["FMTP"]%s(+0x%x)\n", stack[i], demangled && status == 0 ? demangled : sym, offset); free(demangled); } else { fprintf(stderr, " ["FMTP"]\n", stack[i]); } } } #else static void Memento_initStacktracer(void) { } static int Memento_getStacktrace(void **stack, int *skip) { *skip = 0; return 0; } static void Memento_showStacktrace(void **stack, int numberOfFrames) { } #endif /* MEMENTO_STACKTRACE_METHOD */ #ifdef MEMENTO_DETAILS static void Memento_storeDetails(Memento_BlkHeader *head, int type) { void *stack[MEMENTO_BACKTRACE_MAX]; Memento_BlkDetails *details; int count; int skip; if (head == NULL) return; #ifdef MEMENTO_STACKTRACE_METHOD count = Memento_getStacktrace(stack, &skip); #else skip = 0; count = 0; #endif details = MEMENTO_UNDERLYING_MALLOC(sizeof(*details) + (count-1) * sizeof(void *)); if (details == NULL) return; if (count) memcpy(&details->stack, &stack[skip], count * sizeof(void *)); details->type = type; details->count = count; details->sequence = memento.sequence; details->next = NULL; VALGRIND_MAKE_MEM_DEFINED(&head->details_tail, sizeof(head->details_tail)); *head->details_tail = details; head->details_tail = &details->next; VALGRIND_MAKE_MEM_NOACCESS(&head->details_tail, sizeof(head->details_tail)); } #endif void (Memento_bt)(void) { #ifdef MEMENTO_STACKTRACE_METHOD void *stack[MEMENTO_BACKTRACE_MAX]; int count; int skip; count = Memento_getStacktrace(stack, &skip); Memento_showStacktrace(&stack[skip-2], count-skip+2); #endif } static void Memento_bt_internal(int skip2) { #ifdef MEMENTO_STACKTRACE_METHOD void *stack[MEMENTO_BACKTRACE_MAX]; int count; int skip; count = Memento_getStacktrace(stack, &skip); Memento_showStacktrace(&stack[skip+skip2], count-skip-skip2); #endif } static int Memento_checkAllMemoryLocked(void); void Memento_breakpoint(void) { /* A handy externally visible function for breakpointing */ #if 0 /* Enable this to force automatic breakpointing */ #ifndef NDEBUG #ifdef _MSC_VER __asm int 3; #endif #endif #endif } static void Memento_init(void); #define MEMENTO_LOCK() \ do { if (!memento.inited) Memento_init(); MEMENTO_DO_LOCK(); } while (0) #define MEMENTO_UNLOCK() \ do { MEMENTO_DO_UNLOCK(); } while (0) /* Do this as a macro to prevent another level in the callstack, * which is annoying while stepping. */ #define Memento_breakpointLocked() \ do { MEMENTO_UNLOCK(); Memento_breakpoint(); MEMENTO_LOCK(); } while (0) static void Memento_addBlockHead(Memento_Blocks *blks, Memento_BlkHeader *b, int type) { if (blks->tail == NULL) blks->tail = b; b->next = blks->head; b->prev = NULL; if (blks->head) { VALGRIND_MAKE_MEM_DEFINED(&blks->head->prev, sizeof(blks->head->prev)); blks->head->prev = b; VALGRIND_MAKE_MEM_NOACCESS(&blks->head->prev, sizeof(blks->head->prev)); } blks->head = b; #ifndef MEMENTO_LEAKONLY memset(b->preblk, MEMENTO_PREFILL, Memento_PreSize); memset(MEMBLK_POSTPTR(b), MEMENTO_POSTFILL, Memento_PostSize); #endif VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(b), Memento_PostSize); if (type == 0) { /* malloc */ VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize); } else if (type == 1) { /* free */ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize); } VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); } static void Memento_addBlockTail(Memento_Blocks *blks, Memento_BlkHeader *b, int type) { VALGRIND_MAKE_MEM_DEFINED(&blks->tail, sizeof(Memento_BlkHeader *)); if (blks->head == NULL) blks->head = b; b->prev = blks->tail; b->next = NULL; if (blks->tail) { VALGRIND_MAKE_MEM_DEFINED(&blks->tail->next, sizeof(blks->tail->next)); blks->tail->next = b; VALGRIND_MAKE_MEM_NOACCESS(&blks->tail->next, sizeof(blks->tail->next)); } blks->tail = b; #ifndef MEMENTO_LEAKONLY memset(b->preblk, MEMENTO_PREFILL, Memento_PreSize); memset(MEMBLK_POSTPTR(b), MEMENTO_POSTFILL, Memento_PostSize); #endif VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(b), Memento_PostSize); if (type == 0) { /* malloc */ VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_TOBLK(b), b->rawsize); } else if (type == 1) { /* free */ VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_TOBLK(b), b->rawsize); } VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); VALGRIND_MAKE_MEM_NOACCESS(&blks->tail, sizeof(Memento_BlkHeader *)); } typedef struct BlkCheckData { int found; int preCorrupt; int postCorrupt; int freeCorrupt; size_t index; } BlkCheckData; #ifndef MEMENTO_LEAKONLY static int Memento_Internal_checkAllocedBlock(Memento_BlkHeader *b, void *arg) { int i; MEMENTO_UINT32 *ip; unsigned char *p; BlkCheckData *data = (BlkCheckData *)arg; ip = (MEMENTO_UINT32 *)(void *)(b->preblk); i = Memento_PreSize>>2; do { if (*ip++ != MEMENTO_PREFILL_UINT32) goto pre_corrupt; } while (--i); if (0) { pre_corrupt: data->preCorrupt = 1; } /* Postfill may not be aligned, so have to be slower */ p = MEMBLK_POSTPTR(b); i = Memento_PostSize-4; if ((intptr_t)p & 1) { if (*p++ != MEMENTO_POSTFILL) goto post_corrupt; i--; } if ((intptr_t)p & 2) { if (*(MEMENTO_UINT16 *)p != MEMENTO_POSTFILL_UINT16) goto post_corrupt; p += 2; i -= 2; } do { if (*(MEMENTO_UINT32 *)p != MEMENTO_POSTFILL_UINT32) goto post_corrupt; p += 4; i -= 4; } while (i >= 0); if (i & 2) { if (*(MEMENTO_UINT16 *)p != MEMENTO_POSTFILL_UINT16) goto post_corrupt; p += 2; } if (i & 1) { if (*p != MEMENTO_POSTFILL) goto post_corrupt; } if (0) { post_corrupt: data->postCorrupt = 1; } if ((data->freeCorrupt | data->preCorrupt | data->postCorrupt) == 0) { b->lastCheckedOK = memento.sequence; } data->found |= 1; return 0; } static int Memento_Internal_checkFreedBlock(Memento_BlkHeader *b, void *arg) { size_t i; unsigned char *p; BlkCheckData *data = (BlkCheckData *)arg; p = MEMBLK_TOBLK(b); /* p will always be aligned */ i = b->rawsize; /* Attempt to speed this up by checking an (aligned) int at a time */ if (i >= 4) { i -= 4; do { if (*(MEMENTO_UINT32 *)p != MEMENTO_FREEFILL_UINT32) goto mismatch4; p += 4; i -= 4; } while (i > 0); i += 4; } if (i & 2) { if (*(MEMENTO_UINT16 *)p != MEMENTO_FREEFILL_UINT16) goto mismatch; p += 2; i -= 2; } if (0) { mismatch4: i += 4; } mismatch: while (i) { if (*p++ != (unsigned char)MEMENTO_FREEFILL) break; i--; } if (i) { data->freeCorrupt = 1; data->index = b->rawsize-i; } return Memento_Internal_checkAllocedBlock(b, arg); } #endif /* MEMENTO_LEAKONLY */ static void Memento_removeBlock(Memento_Blocks *blks, Memento_BlkHeader *b) { VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b)); if (b->next) { VALGRIND_MAKE_MEM_DEFINED(&b->next->prev, sizeof(b->next->prev)); b->next->prev = b->prev; VALGRIND_MAKE_MEM_NOACCESS(&b->next->prev, sizeof(b->next->prev)); } if (b->prev) { VALGRIND_MAKE_MEM_DEFINED(&b->prev->next, sizeof(b->prev->next)); b->prev->next = b->next; VALGRIND_MAKE_MEM_NOACCESS(&b->prev->next, sizeof(b->prev->next)); } if (blks->tail == b) blks->tail = b->prev; if (blks->head == b) blks->head = b->next; } static void free_block(Memento_BlkHeader *head) { #ifdef MEMENTO_DETAILS Memento_BlkDetails *details = head->details; while (details) { Memento_BlkDetails *next = details->next; MEMENTO_UNDERLYING_FREE(details); details = next; } #endif MEMENTO_UNDERLYING_FREE(head); } static int Memento_Internal_makeSpace(size_t space) { /* If too big, it can never go on the freelist */ if (space > MEMENTO_FREELIST_MAX_SINGLE_BLOCK) return 0; /* Pretend we added it on. */ memento.freeListSize += space; /* Ditch blocks until it fits within our limit */ while (memento.freeListSize > MEMENTO_FREELIST_MAX) { Memento_BlkHeader *head = memento.free.head; VALGRIND_MAKE_MEM_DEFINED(head, sizeof(*head)); memento.free.head = head->next; memento.freeListSize -= MEMBLK_SIZE(head->rawsize); free_block(head); } /* Make sure we haven't just completely emptied the free list */ /* (This should never happen, but belt and braces... */ if (memento.free.head == NULL) memento.free.tail = NULL; return 1; } static int Memento_appBlocks(Memento_Blocks *blks, int (*app)(Memento_BlkHeader *, void *), void *arg) { Memento_BlkHeader *head = blks->head; Memento_BlkHeader *next; int result; while (head) { VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader)); VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head), head->rawsize + Memento_PostSize); result = app(head, arg); next = head->next; VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize); VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader)); if (result) return result; head = next; } return 0; } #ifndef MEMENTO_LEAKONLY /* Distrustful - check the block is a real one */ static int Memento_appBlockUser(Memento_Blocks *blks, int (*app)(Memento_BlkHeader *, void *), void *arg, Memento_BlkHeader *b) { Memento_BlkHeader *head = blks->head; Memento_BlkHeader *next; int result; while (head && head != b) { VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader)); next = head->next; VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize); head = next; } if (head == b) { VALGRIND_MAKE_MEM_DEFINED(head, sizeof(Memento_BlkHeader)); VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(head), head->rawsize + Memento_PostSize); result = app(head, arg); VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(head), Memento_PostSize); VALGRIND_MAKE_MEM_NOACCESS(head, sizeof(Memento_BlkHeader)); return result; } return 0; } static int Memento_appBlock(Memento_Blocks *blks, int (*app)(Memento_BlkHeader *, void *), void *arg, Memento_BlkHeader *b) { int result; VALGRIND_MAKE_MEM_DEFINED(b, sizeof(Memento_BlkHeader)); VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(b), b->rawsize + Memento_PostSize); result = app(b, arg); VALGRIND_MAKE_MEM_NOACCESS(MEMBLK_POSTPTR(b), Memento_PostSize); VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); return result; } #endif /* MEMENTO_LEAKONLY */ static int showBlock(Memento_BlkHeader *b, int space) { int seq; VALGRIND_MAKE_MEM_DEFINED(b, sizeof(Memento_BlkHeader)); fprintf(stderr, FMTP":(size=" FMTZ ",num=%d)", MEMBLK_TOBLK(b), (FMTZ_CAST)b->rawsize, b->sequence); if (b->label) fprintf(stderr, "%c(%s)", space, b->label); if (b->flags & Memento_Flag_KnownLeak) fprintf(stderr, "(Known Leak)"); seq = b->sequence; VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); return seq; } static void blockDisplay(Memento_BlkHeader *b, int n) { n++; while (n > 40) { fprintf(stderr, "*"); n -= 40; } while(n > 0) { int i = n; if (i > 32) i = 32; n -= i; fprintf(stderr, "%s", &" "[32-i]); } showBlock(b, '\t'); fprintf(stderr, "\n"); } static int Memento_listBlock(Memento_BlkHeader *b, void *arg) { size_t *counts = (size_t *)arg; blockDisplay(b, 0); counts[0]++; VALGRIND_MAKE_MEM_DEFINED(b, sizeof(Memento_BlkHeader)); counts[1]+= b->rawsize; VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); return 0; } static void doNestedDisplay(Memento_BlkHeader *b, int depth) { /* Try and avoid recursion if we can help it */ do { Memento_BlkHeader *c = NULL; blockDisplay(b, depth); VALGRIND_MAKE_MEM_DEFINED(b, sizeof(Memento_BlkHeader)); if (b->sibling) { c = b->child; b = b->sibling; } else { b = b->child; depth++; } VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(Memento_BlkHeader)); if (c) doNestedDisplay(c, depth+1); } while (b); } static int ptrcmp(const void *a_, const void *b_) { const char **a = (const char **)a_; const char **b = (const char **)b_; return (int)(*a-*b); } static int Memento_listBlocksNested(void) { int count, i; size_t size; Memento_BlkHeader *b, *prev; void **blocks, *minptr, *maxptr; intptr_t mask; /* Count the blocks */ count = 0; size = 0; for (b = memento.used.head; b; b = b->next) { VALGRIND_MAKE_MEM_DEFINED(b, sizeof(*b)); size += b->rawsize; count++; } /* Make our block list */ blocks = MEMENTO_UNDERLYING_MALLOC(sizeof(void *) * count); if (blocks == NULL) return 1; /* Populate our block list */ b = memento.used.head; minptr = maxptr = MEMBLK_TOBLK(b); mask = (intptr_t)minptr; for (i = 0; b; b = b->next, i++) { void *p = MEMBLK_TOBLK(b); mask &= (intptr_t)p; if (p < minptr) minptr = p; if (p > maxptr) maxptr = p; blocks[i] = p; b->flags &= ~Memento_Flag_HasParent; b->child = NULL; b->sibling = NULL; b->prev = NULL; /* parent */ } qsort(blocks, count, sizeof(void *), ptrcmp); /* Now, calculate tree */ for (b = memento.used.head; b; b = b->next) { char *p = MEMBLK_TOBLK(b); int end = (b->rawsize < MEMENTO_PTRSEARCH ? b->rawsize : MEMENTO_PTRSEARCH); for (i = MEMENTO_SEARCH_SKIP; i < end; i += sizeof(void *)) { void *q = *(void **)(&p[i]); void **r; /* Do trivial checks on pointer */ if ((mask & (intptr_t)q) != mask || q < minptr || q > maxptr) continue; /* Search for pointer */ r = bsearch(&q, blocks, count, sizeof(void *), ptrcmp); if (r) { /* Found child */ Memento_BlkHeader *child = MEMBLK_FROMBLK(*r); Memento_BlkHeader *parent; /* We're assuming tree structure, not graph - ignore second * and subsequent pointers. */ if (child->prev != NULL) /* parent */ continue; if (child->flags & Memento_Flag_HasParent) continue; /* Not interested in pointers to ourself! */ if (child == b) continue; /* We're also assuming acyclicness here. If this is one of * our parents, ignore it. */ parent = b->prev; /* parent */ while (parent != NULL && parent != child) parent = parent->prev; /* parent */ if (parent == child) continue; child->sibling = b->child; b->child = child; child->prev = b; /* parent */ child->flags |= Memento_Flag_HasParent; } } } /* Now display with nesting */ for (b = memento.used.head; b; b = b->next) { if ((b->flags & Memento_Flag_HasParent) == 0) doNestedDisplay(b, 0); } fprintf(stderr, " Total number of blocks = %d\n", count); fprintf(stderr, " Total size of blocks = "FMTZ"\n", (FMTZ_CAST)size); MEMENTO_UNDERLYING_FREE(blocks); /* Now put the blocks back for valgrind, and restore the prev * and magic values. */ prev = NULL; for (b = memento.used.head; b;) { Memento_BlkHeader *next = b->next; b->prev = prev; b->child = MEMENTO_CHILD_MAGIC; b->sibling = MEMENTO_SIBLING_MAGIC; prev = b; VALGRIND_MAKE_MEM_NOACCESS(b, sizeof(*b)); b = next; } return 0; } void Memento_listBlocks(void) { MEMENTO_LOCK(); fprintf(stderr, "Allocated blocks:\n"); if (Memento_listBlocksNested()) { size_t counts[2]; counts[0] = 0; counts[1] = 0; Memento_appBlocks(&memento.used, Memento_listBlock, &counts[0]); fprintf(stderr, " Total number of blocks = "FMTZ"\n", (FMTZ_CAST)counts[0]); fprintf(stderr, " Total size of blocks = "FMTZ"\n", (FMTZ_CAST)counts[1]); } MEMENTO_UNLOCK(); } static int Memento_listNewBlock(Memento_BlkHeader *b, void *arg) { if (b->flags & Memento_Flag_OldBlock) return 0; b->flags |= Memento_Flag_OldBlock; return Memento_listBlock(b, arg); } void Memento_listNewBlocks(void) { size_t counts[2]; MEMENTO_LOCK(); counts[0] = 0; counts[1] = 0; fprintf(stderr, "Blocks allocated and still extant since last list:\n"); Memento_appBlocks(&memento.used, Memento_listNewBlock, &counts[0]); fprintf(stderr, " Total number of blocks = "FMTZ"\n", (FMTZ_CAST)counts[0]); fprintf(stderr, " Total size of blocks = "FMTZ"\n", (FMTZ_CAST)counts[1]); MEMENTO_UNLOCK(); } static void Memento_endStats(void) { fprintf(stderr, "Total memory malloced = "FMTZ" bytes\n", (FMTZ_CAST)memento.totalAlloc); fprintf(stderr, "Peak memory malloced = "FMTZ" bytes\n", (FMTZ_CAST)memento.peakAlloc); fprintf(stderr, FMTZ" mallocs, "FMTZ" frees, "FMTZ" reallocs\n", (FMTZ_CAST)memento.numMallocs, (FMTZ_CAST)memento.numFrees, (FMTZ_CAST)memento.numReallocs); fprintf(stderr, "Average allocation size "FMTZ" bytes\n", (FMTZ_CAST) (memento.numMallocs != 0 ? memento.totalAlloc/memento.numMallocs: 0)); } void Memento_stats(void) { MEMENTO_LOCK(); fprintf(stderr, "Current memory malloced = "FMTZ" bytes\n", (FMTZ_CAST)memento.alloc); Memento_endStats(); MEMENTO_UNLOCK(); } #ifdef MEMENTO_DETAILS static int showInfo(Memento_BlkHeader *b, void *arg) { Memento_BlkDetails *details; fprintf(stderr, FMTP":(size="FMTZ",num=%d)", MEMBLK_TOBLK(b), (FMTZ_CAST)b->rawsize, b->sequence); if (b->label) fprintf(stderr, " (%s)", b->label); fprintf(stderr, "\nEvents:\n"); details = b->details; while (details) { fprintf(stderr, " Event %d (%s)\n", details->sequence, eventType[(int)details->type]); Memento_showStacktrace(details->stack, details->count); details = details->next; } return 0; } #endif void Memento_listBlockInfo(void) { #ifdef MEMENTO_DETAILS MEMENTO_LOCK(); fprintf(stderr, "Details of allocated blocks:\n"); Memento_appBlocks(&memento.used, showInfo, NULL); MEMENTO_UNLOCK(); #endif } static int Memento_nonLeakBlocksLeaked(void) { Memento_BlkHeader *blk = memento.used.head; while (blk) { Memento_BlkHeader *next; int leaked; VALGRIND_MAKE_MEM_DEFINED(blk, sizeof(*blk)); leaked = ((blk->flags & Memento_Flag_KnownLeak) == 0); next = blk->next; VALGRIND_MAKE_MEM_DEFINED(blk, sizeof(*blk)); if (leaked) return 1; blk = next; } return 0; } void Memento_fin(void) { Memento_checkAllMemory(); if (!memento.segv) { Memento_endStats(); if (Memento_nonLeakBlocksLeaked()) { Memento_listBlocks(); #ifdef MEMENTO_DETAILS fprintf(stderr, "\n"); Memento_listBlockInfo(); #endif Memento_breakpoint(); } } if (memento.squeezing) { if (memento.pattern == 0) fprintf(stderr, "Memory squeezing @ %d complete%s\n", memento.squeezeAt, memento.segv ? " (with SEGV)" : ""); else fprintf(stderr, "Memory squeezing @ %d (%d) complete%s\n", memento.squeezeAt, memento.pattern, memento.segv ? " (with SEGV)" : ""); } else if (memento.segv) { fprintf(stderr, "Memento completed (with SEGV)\n"); } if (memento.failing) { fprintf(stderr, "MEMENTO_FAILAT=%d\n", memento.failAt); fprintf(stderr, "MEMENTO_PATTERN=%d\n", memento.pattern); } if (memento.nextFailAt != 0) { fprintf(stderr, "MEMENTO_NEXTFAILAT=%d\n", memento.nextFailAt); fprintf(stderr, "MEMENTO_NEXTPATTERN=%d\n", memento.nextPattern); } } static void Memento_init(void) { char *env; memset(&memento, 0, sizeof(memento)); memento.inited = 1; memento.used.head = NULL; memento.used.tail = NULL; memento.free.head = NULL; memento.free.tail = NULL; memento.sequence = 0; memento.countdown = 1024; env = getenv("MEMENTO_FAILAT"); memento.failAt = (env ? atoi(env) : 0); env = getenv("MEMENTO_BREAKAT"); memento.breakAt = (env ? atoi(env) : 0); env = getenv("MEMENTO_PARANOIA"); memento.paranoia = (env ? atoi(env) : 0); if (memento.paranoia == 0) memento.paranoia = -1024; env = getenv("MEMENTO_PARANOIDAT"); memento.paranoidAt = (env ? atoi(env) : 0); env = getenv("MEMENTO_SQUEEZEAT"); memento.squeezeAt = (env ? atoi(env) : 0); env = getenv("MEMENTO_PATTERN"); memento.pattern = (env ? atoi(env) : 0); env = getenv("MEMENTO_MAXMEMORY"); memento.maxMemory = (env ? atoi(env) : 0); atexit(Memento_fin); Memento_initMutex(&memento.mutex); Memento_initStacktracer(); Memento_breakpoint(); } typedef struct findBlkData { void *addr; Memento_BlkHeader *blk; int flags; } findBlkData; static int Memento_containsAddr(Memento_BlkHeader *b, void *arg) { findBlkData *data = (findBlkData *)arg; char *blkend = &((char *)MEMBLK_TOBLK(b))[b->rawsize]; if ((MEMBLK_TOBLK(b) <= data->addr) && ((void *)blkend > data->addr)) { data->blk = b; data->flags = 1; return 1; } if (((void *)b <= data->addr) && (MEMBLK_TOBLK(b) > data->addr)) { data->blk = b; data->flags = 2; return 1; } if (((void *)blkend <= data->addr) && ((void *)(blkend + Memento_PostSize) > data->addr)) { data->blk = b; data->flags = 3; return 1; } return 0; } void Memento_info(void *addr) { #ifdef MEMENTO_DETAILS findBlkData data; MEMENTO_LOCK(); data.addr = addr; data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.used, Memento_containsAddr, &data); if (data.blk != NULL) showInfo(data.blk, NULL); data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.free, Memento_containsAddr, &data); if (data.blk != NULL) showInfo(data.blk, NULL); MEMENTO_UNLOCK(); #else printf("Memento not compiled with details support\n"); #endif } #ifdef MEMENTO_HAS_FORK #include #include #include #ifdef MEMENTO_STACKTRACE_METHOD #if MEMENTO_STACKTRACE_METHOD == 1 #include #endif #endif /* FIXME: Find some portable way of getting this */ /* MacOSX has 10240, Ubuntu seems to have 256 */ #ifndef OPEN_MAX #define OPEN_MAX 10240 #endif /* stashed_map[j] = i means that file descriptor i-1 was duplicated to j */ int stashed_map[OPEN_MAX]; static void Memento_signal(int sig) { (void)sig; fprintf(stderr, "SEGV at:\n"); memento.segv = 1; Memento_bt_internal(0); exit(1); } static int squeeze(void) { pid_t pid; int i, status; if (memento.patternBit < 0) return 1; if (memento.squeezing && memento.patternBit >= MEMENTO_MAXPATTERN) return 1; if (memento.patternBit == 0) memento.squeezeAt = memento.sequence; if (!memento.squeezing) { fprintf(stderr, "Memory squeezing @ %d\n", memento.squeezeAt); } else fprintf(stderr, "Memory squeezing @ %d (%x,%x)\n", memento.squeezeAt, memento.pattern, memento.patternBit); /* When we fork below, the child is going to snaffle all our file pointers * and potentially corrupt them. Let's make copies of all of them before * we fork, so we can restore them when we restart. */ for (i = 0; i < OPEN_MAX; i++) { if (stashed_map[i] == 0) { int j = dup(i); stashed_map[j] = i+1; } } fprintf(stderr, "Failing at:\n"); Memento_bt_internal(2); pid = fork(); if (pid == 0) { /* Child */ signal(SIGSEGV, Memento_signal); /* In the child, we always fail the next allocation. */ if (memento.patternBit == 0) { memento.patternBit = 1; } else memento.patternBit <<= 1; memento.squeezing = 1; return 1; } /* In the parent if we hit another allocation, pass it (and record the * fact we passed it in the pattern. */ memento.pattern |= memento.patternBit; memento.patternBit <<= 1; /* Wait for pid to finish, with a timeout. */ { struct timespec tm = { 0, 10 * 1000 * 1000 }; /* 10ms = 100th sec */ int timeout = 30 * 1000 * 1000; /* time out in microseconds! */ while (waitpid(pid, &status, WNOHANG) == 0) { nanosleep(&tm, NULL); timeout -= (tm.tv_nsec/1000); tm.tv_nsec *= 2; if (tm.tv_nsec > 999999999) tm.tv_nsec = 999999999; if (timeout <= 0) { char text[32]; fprintf(stderr, "Child is taking a long time to die. Killing it.\n"); sprintf(text, "kill %d", pid); system(text); break; } } } if (status != 0) { fprintf(stderr, "Child status=%d\n", status); } /* Put the files back */ for (i = 0; i < OPEN_MAX; i++) { if (stashed_map[i] != 0) { dup2(i, stashed_map[i]-1); close(i); stashed_map[i] = 0; } } return 0; } #else #include static void Memento_signal(int sig) { (void)sig; memento.segv = 1; /* If we just return from this function the SEGV will be unhandled, and * we'll launch into whatever JIT debugging system the OS provides. At * least fprintf(stderr, something useful first. If MEMENTO_NOJIT is set, then * just exit to avoid the JIT (and get the usual atexit handling). */ if (getenv("MEMENTO_NOJIT")) exit(1); else Memento_fin(); } static int squeeze(void) { fprintf(stderr, "Memento memory squeezing disabled as no fork!\n"); return 0; } #endif static void Memento_startFailing(void) { if (!memento.failing) { fprintf(stderr, "Starting to fail...\n"); Memento_bt(); fflush(stderr); memento.failing = 1; memento.failAt = memento.sequence; memento.nextFailAt = memento.sequence+1; memento.pattern = 0; memento.patternBit = 0; signal(SIGSEGV, Memento_signal); signal(SIGABRT, Memento_signal); Memento_breakpointLocked(); } } static int Memento_event(void) { memento.sequence++; if ((memento.sequence >= memento.paranoidAt) && (memento.paranoidAt != 0)) { memento.paranoia = 1; memento.countdown = 1; } if (--memento.countdown == 0) { Memento_checkAllMemoryLocked(); if (memento.paranoia > 0) memento.countdown = memento.paranoia; else { memento.countdown = -memento.paranoia; if (memento.paranoia > INT_MIN/2) memento.paranoia *= 2; } } if (memento.sequence == memento.breakAt) { fprintf(stderr, "Breaking at event %d\n", memento.breakAt); return 1; } return 0; } int Memento_sequence(void) { return memento.sequence; } int Memento_breakAt(int event) { MEMENTO_LOCK(); memento.breakAt = event; MEMENTO_UNLOCK(); return event; } static void *safe_find_block(void *ptr) { Memento_BlkHeader *block; int valid; if (ptr == NULL) return NULL; block = MEMBLK_FROMBLK(ptr); /* Sometimes wrapping allocators can mean Memento_label * is called with a value within the block, rather than * at the start of the block. If we detect this, find it * the slow way. */ VALGRIND_MAKE_MEM_DEFINED(&block->child, sizeof(block->child)); VALGRIND_MAKE_MEM_DEFINED(&block->sibling, sizeof(block->sibling)); valid = (block->child == MEMENTO_CHILD_MAGIC && block->sibling == MEMENTO_SIBLING_MAGIC); VALGRIND_MAKE_MEM_NOACCESS(&block->child, sizeof(block->child)); VALGRIND_MAKE_MEM_NOACCESS(&block->sibling, sizeof(block->sibling)); if (!valid) { findBlkData data; data.addr = ptr; data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.used, Memento_containsAddr, &data); if (data.blk == NULL) return NULL; block = data.blk; } return block; } void *Memento_label(void *ptr, const char *label) { Memento_BlkHeader *block; if (ptr == NULL) return NULL; MEMENTO_LOCK(); block = safe_find_block(ptr); if (block != NULL) { VALGRIND_MAKE_MEM_DEFINED(&block->label, sizeof(block->label)); block->label = label; VALGRIND_MAKE_MEM_NOACCESS(&block->label, sizeof(block->label)); } MEMENTO_UNLOCK(); return ptr; } void Memento_tick(void) { MEMENTO_LOCK(); if (Memento_event()) Memento_breakpointLocked(); MEMENTO_UNLOCK(); } static int Memento_failThisEventLocked(void) { int failThisOne; if (Memento_event()) Memento_breakpointLocked(); if ((memento.sequence >= memento.failAt) && (memento.failAt != 0)) Memento_startFailing(); if ((memento.sequence >= memento.squeezeAt) && (memento.squeezeAt != 0)) { return squeeze(); } if (!memento.failing) return 0; failThisOne = ((memento.patternBit & memento.pattern) == 0); /* If we are failing, and we've reached the end of the pattern and we've * still got bits available in the pattern word, and we haven't already * set a nextPattern, then extend the pattern. */ if (memento.failing && ((~(memento.patternBit-1) & memento.pattern) == 0) && (memento.patternBit != 0) && memento.nextPattern == 0) { /* We'll fail this one, and set the 'next' one to pass it. */ memento.nextFailAt = memento.failAt; memento.nextPattern = memento.pattern | memento.patternBit; } memento.patternBit = (memento.patternBit ? memento.patternBit << 1 : 1); return failThisOne; } int Memento_failThisEvent(void) { int ret; if (!memento.inited) Memento_init(); MEMENTO_LOCK(); ret = Memento_failThisEventLocked(); MEMENTO_UNLOCK(); return ret; } static void *do_malloc(size_t s, int eventType) { Memento_BlkHeader *memblk; size_t smem = MEMBLK_SIZE(s); if (Memento_failThisEventLocked()) return NULL; if (s == 0) return NULL; memento.numMallocs++; if (memento.maxMemory != 0 && memento.alloc + s > memento.maxMemory) return NULL; memblk = MEMENTO_UNDERLYING_MALLOC(smem); if (memblk == NULL) return NULL; memento.alloc += s; memento.totalAlloc += s; if (memento.peakAlloc < memento.alloc) memento.peakAlloc = memento.alloc; #ifndef MEMENTO_LEAKONLY memset(MEMBLK_TOBLK(memblk), MEMENTO_ALLOCFILL, s); #endif memblk->rawsize = s; memblk->sequence = memento.sequence; memblk->lastCheckedOK = memblk->sequence; memblk->flags = 0; memblk->label = 0; memblk->child = MEMENTO_CHILD_MAGIC; memblk->sibling = MEMENTO_SIBLING_MAGIC; #ifdef MEMENTO_DETAILS memblk->details = NULL; memblk->details_tail = &memblk->details; Memento_storeDetails(memblk, Memento_EventType_malloc); #endif /* MEMENTO_DETAILS */ Memento_addBlockHead(&memento.used, memblk, 0); if (memento.leaking > 0) memblk->flags |= Memento_Flag_KnownLeak; return MEMBLK_TOBLK(memblk); } void *Memento_malloc(size_t s) { void *ret; if (!memento.inited) Memento_init(); MEMENTO_LOCK(); ret = do_malloc(s, Memento_EventType_malloc); MEMENTO_UNLOCK(); return ret; } void *Memento_calloc(size_t n, size_t s) { void *block; if (!memento.inited) Memento_init(); MEMENTO_LOCK(); block = do_malloc(n*s, Memento_EventType_calloc); if (block) memset(block, 0, n*s); MEMENTO_UNLOCK(); return block; } static void do_reference(Memento_BlkHeader *blk, int event) { #ifdef MEMENTO_DETAILS Memento_storeDetails(blk, event); #endif /* MEMENTO_DETAILS */ } int Memento_checkPointerOrNull(void *blk) { if (blk == NULL) return 0; if (blk == MEMENTO_PREFILL_PTR) fprintf(stderr, "Prefill value found as pointer - buffer underrun?\n"); else if (blk == MEMENTO_POSTFILL_PTR) fprintf(stderr, "Postfill value found as pointer - buffer overrun?\n"); else if (blk == MEMENTO_ALLOCFILL_PTR) fprintf(stderr, "Allocfill value found as pointer - use of uninitialised value?\n"); else if (blk == MEMENTO_FREEFILL_PTR) fprintf(stderr, "Allocfill value found as pointer - use after free?\n"); else return 0; #ifdef MEMENTO_DETAILS fprintf(stderr, "Current backtrace:\n"); Memento_bt(); fprintf(stderr, "History:\n"); Memento_info(blk); #endif return 1; } int Memento_checkBytePointerOrNull(void *blk) { unsigned char i; if (blk == NULL) return 0; Memento_checkPointerOrNull(blk); i = *(unsigned int *)blk; if (i == MEMENTO_PREFILL_UBYTE) fprintf(stderr, "Prefill value found - buffer underrun?\n"); else if (i == MEMENTO_POSTFILL_UBYTE) fprintf(stderr, "Postfill value found - buffer overrun?\n"); else if (i == MEMENTO_ALLOCFILL_UBYTE) fprintf(stderr, "Allocfill value found - use of uninitialised value?\n"); else if (i == MEMENTO_FREEFILL_UBYTE) fprintf(stderr, "Allocfill value found - use after free?\n"); else return 0; #ifdef MEMENTO_DETAILS fprintf(stderr, "Current backtrace:\n"); Memento_bt(); fprintf(stderr, "History:\n"); Memento_info(blk); #endif Memento_breakpoint(); return 1; } int Memento_checkShortPointerOrNull(void *blk) { unsigned short i; if (blk == NULL) return 0; Memento_checkPointerOrNull(blk); i = *(unsigned short *)blk; if (i == MEMENTO_PREFILL_USHORT) fprintf(stderr, "Prefill value found - buffer underrun?\n"); else if (i == MEMENTO_POSTFILL_USHORT) fprintf(stderr, "Postfill value found - buffer overrun?\n"); else if (i == MEMENTO_ALLOCFILL_USHORT) fprintf(stderr, "Allocfill value found - use of uninitialised value?\n"); else if (i == MEMENTO_FREEFILL_USHORT) fprintf(stderr, "Allocfill value found - use after free?\n"); else return 0; #ifdef MEMENTO_DETAILS fprintf(stderr, "Current backtrace:\n"); Memento_bt(); fprintf(stderr, "History:\n"); Memento_info(blk); #endif Memento_breakpoint(); return 1; } int Memento_checkIntPointerOrNull(void *blk) { unsigned int i; if (blk == NULL) return 0; Memento_checkPointerOrNull(blk); i = *(unsigned int *)blk; if (i == MEMENTO_PREFILL_UINT) fprintf(stderr, "Prefill value found - buffer underrun?\n"); else if (i == MEMENTO_POSTFILL_UINT) fprintf(stderr, "Postfill value found - buffer overrun?\n"); else if (i == MEMENTO_ALLOCFILL_UINT) fprintf(stderr, "Allocfill value found - use of uninitialised value?\n"); else if (i == MEMENTO_FREEFILL_UINT) fprintf(stderr, "Allocfill value found - use after free?\n"); else return 0; #ifdef MEMENTO_DETAILS fprintf(stderr, "Current backtrace:\n"); Memento_bt(); fprintf(stderr, "History:\n"); Memento_info(blk); #endif Memento_breakpoint(); return 1; } static void *do_takeRef(void *blk) { MEMENTO_LOCK(); do_reference(safe_find_block(blk), Memento_EventType_takeRef); MEMENTO_UNLOCK(); return blk; } void *Memento_takeByteRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; (void)Memento_checkBytePointerOrNull(blk); return do_takeRef(blk); } void *Memento_takeShortRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; (void)Memento_checkShortPointerOrNull(blk); return do_takeRef(blk); } void *Memento_takeIntRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; (void)Memento_checkIntPointerOrNull(blk); return do_takeRef(blk); } void *Memento_takeRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; return do_takeRef(blk); } static void *do_dropRef(void *blk) { MEMENTO_LOCK(); do_reference(safe_find_block(blk), Memento_EventType_dropRef); MEMENTO_UNLOCK(); return blk; } void *Memento_dropByteRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; Memento_checkBytePointerOrNull(blk); return do_dropRef(blk); } void *Memento_dropShortRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; Memento_checkShortPointerOrNull(blk); return do_dropRef(blk); } void *Memento_dropIntRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; Memento_checkIntPointerOrNull(blk); return do_dropRef(blk); } void *Memento_dropRef(void *blk) { if (!memento.inited) Memento_init(); if (Memento_event()) Memento_breakpoint(); if (!blk) return NULL; return do_dropRef(blk); } void *Memento_adjustRef(void *blk, int adjust) { if (Memento_event()) Memento_breakpoint(); if (blk == NULL) return NULL; while (adjust > 0) { do_takeRef(blk); adjust--; } while (adjust < 0) { do_dropRef(blk); adjust++; } return blk; } void *Memento_reference(void *blk) { if (!blk) return NULL; if (!memento.inited) Memento_init(); MEMENTO_LOCK(); do_reference(safe_find_block(blk), Memento_EventType_reference); MEMENTO_UNLOCK(); return blk; } /* Treat blocks from the user with suspicion, and check them the slow * but safe way. */ static int checkBlockUser(Memento_BlkHeader *memblk, const char *action) { #ifndef MEMENTO_LEAKONLY BlkCheckData data; memset(&data, 0, sizeof(data)); Memento_appBlockUser(&memento.used, Memento_Internal_checkAllocedBlock, &data, memblk); if (!data.found) { /* Failure! */ fprintf(stderr, "Attempt to %s block ", action); showBlock(memblk, 32); fprintf(stderr, "\n"); Memento_breakpointLocked(); return 1; } else if (data.preCorrupt || data.postCorrupt) { fprintf(stderr, "Block "); showBlock(memblk, ' '); fprintf(stderr, " found to be corrupted on %s!\n", action); if (data.preCorrupt) { fprintf(stderr, "Preguard corrupted\n"); } if (data.postCorrupt) { fprintf(stderr, "Postguard corrupted\n"); } fprintf(stderr, "Block last checked OK at allocation %d. Now %d.\n", memblk->lastCheckedOK, memento.sequence); if ((memblk->flags & Memento_Flag_Reported) == 0) { memblk->flags |= Memento_Flag_Reported; Memento_breakpointLocked(); } return 1; } #endif return 0; } static int checkBlock(Memento_BlkHeader *memblk, const char *action) { #ifndef MEMENTO_LEAKONLY BlkCheckData data; #endif if (memblk->child != MEMENTO_CHILD_MAGIC || memblk->sibling != MEMENTO_SIBLING_MAGIC) { /* Failure! */ fprintf(stderr, "Attempt to %s invalid block ", action); showBlock(memblk, 32); fprintf(stderr, "\n"); Memento_breakpointLocked(); return 1; } #ifndef MEMENTO_LEAKONLY memset(&data, 0, sizeof(data)); Memento_appBlock(&memento.used, Memento_Internal_checkAllocedBlock, &data, memblk); if (!data.found) { /* Failure! */ fprintf(stderr, "Attempt to %s block ", action); showBlock(memblk, 32); fprintf(stderr, "\n"); Memento_breakpointLocked(); return 1; } else if (data.preCorrupt || data.postCorrupt) { fprintf(stderr, "Block "); showBlock(memblk, ' '); fprintf(stderr, " found to be corrupted on %s!\n", action); if (data.preCorrupt) { fprintf(stderr, "Preguard corrupted\n"); } if (data.postCorrupt) { fprintf(stderr, "Postguard corrupted\n"); } fprintf(stderr, "Block last checked OK at allocation %d. Now %d.\n", memblk->lastCheckedOK, memento.sequence); if ((memblk->flags & Memento_Flag_Reported) == 0) { memblk->flags |= Memento_Flag_Reported; Memento_breakpointLocked(); } return 1; } #endif return 0; } static void do_free(void *blk, int eventType) { Memento_BlkHeader *memblk; if (Memento_event()) Memento_breakpointLocked(); if (blk == NULL) return; memblk = MEMBLK_FROMBLK(blk); VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); if (checkBlock(memblk, "free")) return; #ifdef MEMENTO_DETAILS Memento_storeDetails(memblk, Memento_EventType_free); #endif VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); if (memblk->flags & Memento_Flag_BreakOnFree) Memento_breakpointLocked(); memento.alloc -= memblk->rawsize; memento.numFrees++; Memento_removeBlock(&memento.used, memblk); VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); if (Memento_Internal_makeSpace(MEMBLK_SIZE(memblk->rawsize))) { VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); VALGRIND_MAKE_MEM_DEFINED(MEMBLK_TOBLK(memblk), memblk->rawsize + Memento_PostSize); #ifndef MEMENTO_LEAKONLY memset(MEMBLK_TOBLK(memblk), MEMENTO_FREEFILL, memblk->rawsize); #endif memblk->flags |= Memento_Flag_Freed; Memento_addBlockTail(&memento.free, memblk, 1); } else { free_block(memblk); } } void Memento_free(void *blk) { if (!memento.inited) Memento_init(); MEMENTO_LOCK(); do_free(blk, Memento_EventType_free); MEMENTO_UNLOCK(); } static void *do_realloc(void *blk, size_t newsize, int type) { Memento_BlkHeader *memblk, *newmemblk; size_t newsizemem; int flags; if (Memento_failThisEventLocked()) return NULL; memblk = MEMBLK_FROMBLK(blk); VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); if (checkBlock(memblk, "realloc")) return NULL; #ifdef MEMENTO_DETAILS Memento_storeDetails(memblk, type); #endif VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); if (memblk->flags & Memento_Flag_BreakOnRealloc) Memento_breakpointLocked(); VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); if (memento.maxMemory != 0 && memento.alloc - memblk->rawsize + newsize > memento.maxMemory) return NULL; newsizemem = MEMBLK_SIZE(newsize); Memento_removeBlock(&memento.used, memblk); VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(*memblk)); flags = memblk->flags; newmemblk = MEMENTO_UNDERLYING_REALLOC(memblk, newsizemem); if (newmemblk == NULL) { Memento_addBlockHead(&memento.used, memblk, 2); return NULL; } memento.numReallocs++; memento.totalAlloc += newsize; memento.alloc -= newmemblk->rawsize; memento.alloc += newsize; if (memento.peakAlloc < memento.alloc) memento.peakAlloc = memento.alloc; newmemblk->flags = flags; #ifndef MEMENTO_LEAKONLY if (newmemblk->rawsize < newsize) { char *newbytes = ((char *)MEMBLK_TOBLK(newmemblk))+newmemblk->rawsize; VALGRIND_MAKE_MEM_DEFINED(newbytes, newsize - newmemblk->rawsize); memset(newbytes, MEMENTO_ALLOCFILL, newsize - newmemblk->rawsize); VALGRIND_MAKE_MEM_UNDEFINED(newbytes, newsize - newmemblk->rawsize); } #endif newmemblk->rawsize = newsize; #ifndef MEMENTO_LEAKONLY VALGRIND_MAKE_MEM_DEFINED(newmemblk->preblk, Memento_PreSize); memset(newmemblk->preblk, MEMENTO_PREFILL, Memento_PreSize); VALGRIND_MAKE_MEM_UNDEFINED(newmemblk->preblk, Memento_PreSize); VALGRIND_MAKE_MEM_DEFINED(MEMBLK_POSTPTR(newmemblk), Memento_PostSize); memset(MEMBLK_POSTPTR(newmemblk), MEMENTO_POSTFILL, Memento_PostSize); VALGRIND_MAKE_MEM_UNDEFINED(MEMBLK_POSTPTR(newmemblk), Memento_PostSize); #endif Memento_addBlockHead(&memento.used, newmemblk, 2); return MEMBLK_TOBLK(newmemblk); } void *Memento_realloc(void *blk, size_t newsize) { void *ret; if (!memento.inited) Memento_init(); if (blk == NULL) { MEMENTO_LOCK(); ret = do_malloc(newsize, Memento_EventType_realloc); MEMENTO_UNLOCK(); return ret; } if (newsize == 0) { MEMENTO_LOCK(); do_free(blk, Memento_EventType_realloc); MEMENTO_UNLOCK(); return NULL; } MEMENTO_LOCK(); ret = do_realloc(blk, newsize, Memento_EventType_realloc); MEMENTO_UNLOCK(); return ret; } int Memento_checkBlock(void *blk) { Memento_BlkHeader *memblk; int ret; if (blk == NULL) return 0; MEMENTO_LOCK(); memblk = MEMBLK_FROMBLK(blk); ret = checkBlockUser(memblk, "check"); MEMENTO_UNLOCK(); return ret; } #ifndef MEMENTO_LEAKONLY static int Memento_Internal_checkAllAlloced(Memento_BlkHeader *memblk, void *arg) { BlkCheckData *data = (BlkCheckData *)arg; Memento_Internal_checkAllocedBlock(memblk, data); if (data->preCorrupt || data->postCorrupt) { if ((data->found & 2) == 0) { fprintf(stderr, "Allocated blocks:\n"); data->found |= 2; } fprintf(stderr, " Block "); showBlock(memblk, ' '); if (data->preCorrupt) { fprintf(stderr, " Preguard "); } if (data->postCorrupt) { fprintf(stderr, "%s Postguard ", (data->preCorrupt ? "&" : "")); } fprintf(stderr, "corrupted.\n " "Block last checked OK at allocation %d. Now %d.\n", memblk->lastCheckedOK, memento.sequence); data->preCorrupt = 0; data->postCorrupt = 0; data->freeCorrupt = 0; if ((memblk->flags & Memento_Flag_Reported) == 0) { memblk->flags |= Memento_Flag_Reported; Memento_breakpointLocked(); } } else memblk->lastCheckedOK = memento.sequence; return 0; } static int Memento_Internal_checkAllFreed(Memento_BlkHeader *memblk, void *arg) { BlkCheckData *data = (BlkCheckData *)arg; Memento_Internal_checkFreedBlock(memblk, data); if (data->preCorrupt || data->postCorrupt || data->freeCorrupt) { if ((data->found & 4) == 0) { fprintf(stderr, "Freed blocks:\n"); data->found |= 4; } fprintf(stderr, " "); showBlock(memblk, ' '); if (data->freeCorrupt) { fprintf(stderr, " index %d (address "FMTP") onwards", (int)data->index, &((char *)MEMBLK_TOBLK(memblk))[data->index]); if (data->preCorrupt) { fprintf(stderr, "+ preguard"); } if (data->postCorrupt) { fprintf(stderr, "+ postguard"); } } else { if (data->preCorrupt) { fprintf(stderr, " preguard"); } if (data->postCorrupt) { fprintf(stderr, "%s Postguard", (data->preCorrupt ? "+" : "")); } } VALGRIND_MAKE_MEM_DEFINED(memblk, sizeof(Memento_BlkHeader)); fprintf(stderr, " corrupted.\n" " Block last checked OK at allocation %d. Now %d.\n", memblk->lastCheckedOK, memento.sequence); if ((memblk->flags & Memento_Flag_Reported) == 0) { memblk->flags |= Memento_Flag_Reported; Memento_breakpointLocked(); } VALGRIND_MAKE_MEM_NOACCESS(memblk, sizeof(Memento_BlkHeader)); data->preCorrupt = 0; data->postCorrupt = 0; data->freeCorrupt = 0; } else memblk->lastCheckedOK = memento.sequence; return 0; } #endif /* MEMENTO_LEAKONLY */ static int Memento_checkAllMemoryLocked(void) { #ifndef MEMENTO_LEAKONLY BlkCheckData data; memset(&data, 0, sizeof(data)); Memento_appBlocks(&memento.used, Memento_Internal_checkAllAlloced, &data); Memento_appBlocks(&memento.free, Memento_Internal_checkAllFreed, &data); return data.found; #else return 0; #endif } int Memento_checkAllMemory(void) { #ifndef MEMENTO_LEAKONLY int ret; MEMENTO_LOCK(); ret = Memento_checkAllMemoryLocked(); MEMENTO_UNLOCK(); if (ret & 6) { Memento_breakpoint(); return 1; } return 0; #endif } int Memento_setParanoia(int i) { memento.paranoia = i; if (memento.paranoia > 0) memento.countdown = memento.paranoia; else memento.countdown = -memento.paranoia; return i; } int Memento_paranoidAt(int i) { memento.paranoidAt = i; return i; } int Memento_getBlockNum(void *b) { Memento_BlkHeader *memblk; if (b == NULL) return 0; memblk = MEMBLK_FROMBLK(b); return (memblk->sequence); } int Memento_check(void) { int result; fprintf(stderr, "Checking memory\n"); result = Memento_checkAllMemory(); fprintf(stderr, "Memory checked!\n"); return result; } int Memento_find(void *a) { findBlkData data; int s; MEMENTO_LOCK(); data.addr = a; data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.used, Memento_containsAddr, &data); if (data.blk != NULL) { fprintf(stderr, "Address "FMTP" is in %sallocated block ", data.addr, (data.flags == 1 ? "" : (data.flags == 2 ? "preguard of " : "postguard of "))); s = showBlock(data.blk, ' '); fprintf(stderr, "\n"); MEMENTO_UNLOCK(); return s; } data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.free, Memento_containsAddr, &data); if (data.blk != NULL) { fprintf(stderr, "Address "FMTP" is in %sfreed block ", data.addr, (data.flags == 1 ? "" : (data.flags == 2 ? "preguard of " : "postguard of "))); s = showBlock(data.blk, ' '); fprintf(stderr, "\n"); MEMENTO_UNLOCK(); return s; } MEMENTO_UNLOCK(); return 0; } void Memento_breakOnFree(void *a) { findBlkData data; MEMENTO_LOCK(); data.addr = a; data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.used, Memento_containsAddr, &data); if (data.blk != NULL) { fprintf(stderr, "Will stop when address "FMTP" (in %sallocated block ", data.addr, (data.flags == 1 ? "" : (data.flags == 2 ? "preguard of " : "postguard of "))); showBlock(data.blk, ' '); fprintf(stderr, ") is freed\n"); VALGRIND_MAKE_MEM_DEFINED(data.blk, sizeof(Memento_BlkHeader)); data.blk->flags |= Memento_Flag_BreakOnFree; VALGRIND_MAKE_MEM_NOACCESS(data.blk, sizeof(Memento_BlkHeader)); MEMENTO_UNLOCK(); return; } data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.free, Memento_containsAddr, &data); if (data.blk != NULL) { fprintf(stderr, "Can't stop on free; address "FMTP" is in %sfreed block ", data.addr, (data.flags == 1 ? "" : (data.flags == 2 ? "preguard of " : "postguard of "))); showBlock(data.blk, ' '); fprintf(stderr, "\n"); MEMENTO_UNLOCK(); return; } fprintf(stderr, "Can't stop on free; address "FMTP" is not in a known block.\n", a); MEMENTO_UNLOCK(); } void Memento_breakOnRealloc(void *a) { findBlkData data; MEMENTO_LOCK(); data.addr = a; data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.used, Memento_containsAddr, &data); if (data.blk != NULL) { fprintf(stderr, "Will stop when address "FMTP" (in %sallocated block ", data.addr, (data.flags == 1 ? "" : (data.flags == 2 ? "preguard of " : "postguard of "))); showBlock(data.blk, ' '); fprintf(stderr, ") is freed (or realloced)\n"); VALGRIND_MAKE_MEM_DEFINED(data.blk, sizeof(Memento_BlkHeader)); data.blk->flags |= Memento_Flag_BreakOnFree | Memento_Flag_BreakOnRealloc; VALGRIND_MAKE_MEM_NOACCESS(data.blk, sizeof(Memento_BlkHeader)); MEMENTO_UNLOCK(); return; } data.blk = NULL; data.flags = 0; Memento_appBlocks(&memento.free, Memento_containsAddr, &data); if (data.blk != NULL) { fprintf(stderr, "Can't stop on free/realloc; address "FMTP" is in %sfreed block ", data.addr, (data.flags == 1 ? "" : (data.flags == 2 ? "preguard of " : "postguard of "))); showBlock(data.blk, ' '); fprintf(stderr, "\n"); MEMENTO_UNLOCK(); return; } fprintf(stderr, "Can't stop on free/realloc; address "FMTP" is not in a known block.\n", a); MEMENTO_UNLOCK(); } int Memento_failAt(int i) { memento.failAt = i; if ((memento.sequence > memento.failAt) && (memento.failing != 0)) Memento_startFailing(); return i; } size_t Memento_setMax(size_t max) { memento.maxMemory = max; return max; } void Memento_startLeaking(void) { memento.leaking++; } void Memento_stopLeaking(void) { memento.leaking--; } int Memento_squeezing(void) { return memento.squeezing; } #endif /* MEMENTO_CPP_EXTRAS_ONLY */ #ifdef __cplusplus /* Dumb overrides for the new and delete operators */ void *operator new(size_t size) { void *ret; if (!memento.inited) Memento_init(); if (size == 0) size = 1; MEMENTO_LOCK(); ret = do_malloc(size, Memento_EventType_new); MEMENTO_UNLOCK(); return ret; } void operator delete(void *pointer) { if (!pointer) return; MEMENTO_LOCK(); do_free(pointer, Memento_EventType_delete); MEMENTO_UNLOCK(); } /* Some C++ systems (apparently) don't provide new[] or delete[] * operators. Provide a way to cope with this */ #ifndef MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS void *operator new[](size_t size) { void *ret; if (!memento.inited) Memento_init(); if (size == 0) size = 1; MEMENTO_LOCK(); ret = do_malloc(size, Memento_EventType_newArray); MEMENTO_UNLOCK(); return ret; } void operator delete[](void *pointer) { MEMENTO_LOCK(); do_free(pointer, Memento_EventType_deleteArray); MEMENTO_UNLOCK(); } #endif /* MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS */ #endif /* __cplusplus */ #else /* Just in case anyone has left some debugging code in... */ void (Memento_breakpoint)(void) { } int (Memento_checkBlock)(void *b) { return 0; } int (Memento_checkAllMemory)(void) { return 0; } int (Memento_check)(void) { return 0; } int (Memento_setParanoia)(int i) { return 0; } int (Memento_paranoidAt)(int i) { return 0; } int (Memento_breakAt)(int i) { return 0; } int (Memento_getBlockNum)(void *i) { return 0; } int (Memento_find)(void *a) { return 0; } int (Memento_failAt)(int i) { return 0; } void (Memento_breakOnFree)(void *a) { } void (Memento_breakOnRealloc)(void *a) { } void *(Memento_takeRef)(void *a) { return a; } void *(Memento_dropRef)(void *a) { return a; } void *(Memento_adjustRef)(void *a, int adjust) { return a; } void *(Memento_reference)(void *a) { return a; } #undef Memento_malloc #undef Memento_free #undef Memento_realloc #undef Memento_calloc void *Memento_malloc(size_t size) { return MEMENTO_UNDERLYING_MALLOC(size); } void Memento_free(void *b) { MEMENTO_UNDERLYING_FREE(b); } void *Memento_realloc(void *b, size_t s) { return MEMENTO_UNDERLYING_REALLOC(b, s); } void *Memento_calloc(size_t n, size_t s) { return MEMENTO_UNDERLYING_CALLOC(n, s); } void (Memento_listBlocks)(void) { } void (Memento_listNewBlocks)(void) { } size_t (Memento_setMax)(size_t max) { return 0; } void (Memento_stats)(void) { } void *(Memento_label)(void *ptr, const char *label) { return ptr; } void (Memento_info)(void *addr) { } void (Memento_listBlockInfo)(void) { } void (Memento_startLeaking)(void) { } void (Memento_stopLeaking)(void) { } int (Memento_squeezing)(void) { return 0; } #endif jbig2dec-0.18/memento.h000066400000000000000000000320051362055302000147240ustar00rootroot00000000000000/* Copyright (C) 2009-2018 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of that license. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* Memento: A library to aid debugging of memory leaks/heap corruption. * * Usage (with C): * First, build your project with MEMENTO defined, and include this * header file wherever you use malloc, realloc or free. * This header file will use macros to point malloc, realloc and free to * point to Memento_malloc, Memento_realloc, Memento_free. * * Run your program, and all mallocs/frees/reallocs should be redirected * through here. When the program exits, you will get a list of all the * leaked blocks, together with some helpful statistics. You can get the * same list of allocated blocks at any point during program execution by * calling Memento_listBlocks(); * * Every call to malloc/free/realloc counts as an 'allocation event'. * On each event Memento increments a counter. Every block is tagged with * the current counter on allocation. Every so often during program * execution, the heap is checked for consistency. By default this happens * after 1024 events, then after 2048 events, then after 4096 events, etc. * This can be changed at runtime by using Memento_setParanoia(int level). * 0 turns off such checking, 1 sets checking to happen on every event, * any positive number n sets checking to happen once every n events, * and any negative number n sets checking to happen after -n events, then * after -2n events etc. * * The default paranoia level is therefore -1024. * * Memento keeps blocks around for a while after they have been freed, and * checks them as part of these heap checks to see if they have been * written to (or are freed twice etc). * * A given heap block can be checked for consistency (it's 'pre' and * 'post' guard blocks are checked to see if they have been written to) * by calling Memento_checkBlock(void *blockAddress); * * A check of all the memory can be triggered by calling Memento_check(); * (or Memento_checkAllMemory(); if you'd like it to be quieter). * * A good place to breakpoint is Memento_breakpoint, as this will then * trigger your debugger if an error is detected. This is done * automatically for debug windows builds. * * If a block is found to be corrupt, information will be printed to the * console, including the address of the block, the size of the block, * the type of corruption, the number of the block and the event on which * it last passed a check for correctness. * * If you rerun, and call Memento_paranoidAt(int event); with this number * the code will wait until it reaches that event and then start * checking the heap after every allocation event. Assuming it is a * deterministic failure, you should then find out where in your program * the error is occurring (between event x-1 and event x). * * Then you can rerun the program again, and call * Memento_breakAt(int event); and the program will call * Memento_Breakpoint() when event x is reached, enabling you to step * through. * * Memento_find(address) will tell you what block (if any) the given * address is in. * * An example: * Suppose we have a gs invocation that crashes with memory corruption. * * Build with -DMEMENTO. * * In your debugger put a breakpoint on Memento_breakpoint. * * Run the program. It will stop in Memento_inited. * * Execute Memento_setParanoia(1); (In VS use Ctrl-Alt-Q). (Note #1) * * Continue execution. * * It will detect the memory corruption on the next allocation event * after it happens, and stop in Memento_breakpoint. The console should * show something like: * * Freed blocks: * 0x172e610(size=288,num=1415) index 256 (0x172e710) onwards corrupted * Block last checked OK at allocation 1457. Now 1458. * * * This means that the block became corrupted between allocation 1457 * and 1458 - so if we rerun and stop the program at 1457, we can then * step through, possibly with a data breakpoint at 0x172e710 and see * when it occurs. * * So restart the program from the beginning. When we stop after * initialisation execute Memento_breakAt(1457); (and maybe * Memento_setParanoia(1), or Memento_setParanoidAt(1457)) * * Continue execution until we hit Memento_breakpoint. * * Now you can step through and watch the memory corruption happen. * * Note #1: Using Memento_setParanoia(1) can cause your program to run * very slowly. You may instead choose to use Memento_setParanoia(100) * (or some other figure). This will only exhaustively check memory on * every 100th allocation event. This trades speed for the size of the * average allocation event range in which detection of memory corruption * occurs. You may (for example) choose to run once checking every 100 * allocations and discover that the corruption happens between events * X and X+100. You can then rerun using Memento_paranoidAt(X), and * it'll only start exhaustively checking when it reaches X. * * More than one memory allocator? * * If you have more than one memory allocator in the system (like for * instance the ghostscript chunk allocator, that builds on top of the * standard malloc and returns chunks itself), then there are some things * to note: * * * If the secondary allocator gets its underlying blocks from calling * malloc, then those will be checked by Memento, but 'subblocks' that * are returned to the secondary allocator will not. There is currently * no way to fix this other than trying to bypass the secondary * allocator. One way I have found to do this with the chunk allocator * is to tweak its idea of a 'large block' so that it puts every * allocation in its own chunk. Clearly this negates the point of having * a secondary allocator, and is therefore not recommended for general * use. * * * Again, if the secondary allocator gets its underlying blocks from * calling malloc (and hence Memento) leak detection should still work * (but whole blocks will be detected rather than subblocks). * * * If on every allocation attempt the secondary allocator calls into * Memento_failThisEvent(), and fails the allocation if it returns true * then more useful features can be used; firstly memory squeezing will * work, and secondly, Memento will have a "finer grained" paranoia * available to it. * * Usage with C++: * * Memento has some experimental code in it to trap new/delete (and * new[]/delete[] if required) calls. * * In order for this to work, either: * * 1) Build memento.c with the c++ compiler. * * or * * 2) Build memento.c as normal with the C compiler, then from any * one of your .cpp files, do: * * #define MEMENTO_CPP_EXTRAS_ONLY * #include "memento.c" * * In the case where MEMENTO is not defined, this will not do anything. * * Both Windows and GCC provide separate new[] and delete[] operators * for arrays. Apparently some systems do not. If this is the case for * your system, define MEMENTO_CPP_NO_ARRAY_CONSTRUCTORS. * * "libbacktrace.so failed to load" * * In order to give nice backtraces on unix, Memento will try to use * a libbacktrace dynamic library. If it can't find it, you'll see * that warning, and your backtraces won't include file/line information. * * To fix this you'll need to build your own libbacktrace. Don't worry * it's really easy: * git clone git://github.com/ianlancetaylor/libbacktrace * cd libbacktrace * ./configure * make * * This leaves the build .so as .libs/libbacktrace.so * * Memento will look for this on LD_LIBRARY_PATH, or in /opt/lib/, * or in /lib/, or in /usr/lib/, or in /usr/local/lib/. I recommend * using /opt/lib/ as this won't conflict with anything that you * get via a package manager like apt. * * sudo mkdir /opt * sudo mkdir /opt/lib * sudo cp .libs/libbacktrace.so /opt/lib/ */ #ifndef MEMENTO_H #include #define MEMENTO_H #ifndef MEMENTO_UNDERLYING_MALLOC #define MEMENTO_UNDERLYING_MALLOC malloc #endif #ifndef MEMENTO_UNDERLYING_FREE #define MEMENTO_UNDERLYING_FREE free #endif #ifndef MEMENTO_UNDERLYING_REALLOC #define MEMENTO_UNDERLYING_REALLOC realloc #endif #ifndef MEMENTO_UNDERLYING_CALLOC #define MEMENTO_UNDERLYING_CALLOC calloc #endif #ifndef MEMENTO_MAXALIGN #define MEMENTO_MAXALIGN (sizeof(int)) #endif #define MEMENTO_PREFILL 0xa6 #define MEMENTO_POSTFILL 0xa7 #define MEMENTO_ALLOCFILL 0xa8 #define MEMENTO_FREEFILL 0xa9 #define MEMENTO_FREELIST_MAX 0x2000000 int Memento_checkBlock(void *); int Memento_checkAllMemory(void); int Memento_check(void); int Memento_setParanoia(int); int Memento_paranoidAt(int); int Memento_breakAt(int); void Memento_breakOnFree(void *a); void Memento_breakOnRealloc(void *a); int Memento_getBlockNum(void *); int Memento_find(void *a); void Memento_breakpoint(void); int Memento_failAt(int); int Memento_failThisEvent(void); void Memento_listBlocks(void); void Memento_listNewBlocks(void); size_t Memento_setMax(size_t); void Memento_stats(void); void *Memento_label(void *, const char *); void Memento_tick(void); void *Memento_malloc(size_t s); void *Memento_realloc(void *, size_t s); void Memento_free(void *); void *Memento_calloc(size_t, size_t); void Memento_info(void *addr); void Memento_listBlockInfo(void); void *Memento_takeByteRef(void *blk); void *Memento_dropByteRef(void *blk); void *Memento_takeShortRef(void *blk); void *Memento_dropShortRef(void *blk); void *Memento_takeIntRef(void *blk); void *Memento_dropIntRef(void *blk); void *Memento_takeRef(void *blk); void *Memento_dropRef(void *blk); void *Memento_adjustRef(void *blk, int adjust); void *Memento_reference(void *blk); int Memento_checkPointerOrNull(void *blk); int Memento_checkBytePointerOrNull(void *blk); int Memento_checkShortPointerOrNull(void *blk); int Memento_checkIntPointerOrNull(void *blk); void Memento_startLeaking(void); void Memento_stopLeaking(void); int Memento_sequence(void); int Memento_squeezing(void); void Memento_fin(void); void Memento_bt(void); #ifdef MEMENTO #ifndef COMPILING_MEMENTO_C #define malloc Memento_malloc #define free Memento_free #define realloc Memento_realloc #define calloc Memento_calloc #endif #else #define Memento_malloc MEMENTO_UNDERLYING_MALLOC #define Memento_free MEMENTO_UNDERLYING_FREE #define Memento_realloc MEMENTO_UNDERLYING_REALLOC #define Memento_calloc MEMENTO_UNDERLYING_CALLOC #define Memento_checkBlock(A) 0 #define Memento_checkAllMemory() 0 #define Memento_check() 0 #define Memento_setParanoia(A) 0 #define Memento_paranoidAt(A) 0 #define Memento_breakAt(A) 0 #define Memento_breakOnFree(A) 0 #define Memento_breakOnRealloc(A) 0 #define Memento_getBlockNum(A) 0 #define Memento_find(A) 0 #define Memento_breakpoint() do {} while (0) #define Memento_failAt(A) 0 #define Memento_failThisEvent() 0 #define Memento_listBlocks() do {} while (0) #define Memento_listNewBlocks() do {} while (0) #define Memento_setMax(A) 0 #define Memento_stats() do {} while (0) #define Memento_label(A,B) (A) #define Memento_info(A) do {} while (0) #define Memento_listBlockInfo() do {} while (0) #define Memento_takeByteRef(A) (A) #define Memento_dropByteRef(A) (A) #define Memento_takeShortRef(A) (A) #define Memento_dropShortRef(A) (A) #define Memento_takeIntRef(A) (A) #define Memento_dropIntRef(A) (A) #define Memento_takeRef(A) (A) #define Memento_dropRef(A) (A) #define Memento_adjustRef(A,V) (A) #define Memento_reference(A) (A) #define Memento_checkPointerOrNull(A) 0 #define Memento_checkBytePointerOrNull(A) 0 #define Memento_checkShortPointerOrNull(A) 0 #define Memento_checkIntPointerOrNull(A) 0 #define Memento_tick() do {} while (0) #define Memento_startLeaking() do {} while (0) #define Memento_stopLeaking() do {} while (0) #define Memento_fin() do {} while (0) #define Memento_bt() do {} while (0) #define Memento_sequence() (0) #define Memento_squeezing() (0) #endif /* MEMENTO */ #endif /* MEMENTO_H */ jbig2dec-0.18/msvc.mak000066400000000000000000000065621362055302000145620ustar00rootroot00000000000000# makefile for jbig2dec # under Microsoft Visual C++ # # To compile zlib.dll: # Get zlib >= 1.2.7, unzip and rename to zlib, # cd zlib, then nmake -f win32\Makefile.msc # To compile libpng.lib: # Get libpng >= 1.6.0, unzip then rename to libpng, # cd libpng, nmake -f scripts\makefile.vcwin32 !ifndef LIBPNGDIR LIBPNGDIR=../libpng !endif !ifndef ZLIBDIR ZLIBDIR=../zlib !endif # define iff you're linking to libpng !if exist("$(ZLIBDIR)") && exist("$(LIBPNGDIR)") && exist ("$(LIBPNGDIR)/pnglibconf.h") LIBPNG_CFLAGS=-DHAVE_LIBPNG -I$(LIBPNGDIR) -I$(ZLIBDIR) LIBPNG_LDFLAGS=$(LIBPNGDIR)/libpng.lib $(ZLIBDIR)/zlib.lib /link /NODEFAULTLIB:LIBCMT JBIG2_IMAGE_PNG_OBJ=jbig2_image_png$(OBJ) !else LIBPNG_CFLAGS= LIBPNG_LDFLAGS= JBIG2_IMAGE_PNG_OBJ= !endif EXE=.exe OBJ=.obj NUL= CFLAGS=-nologo -W4 -Zi -DHAVE_STRING_H=1 -D_CRT_SECURE_NO_WARNINGS $(LIBPNG_CFLAGS) CC=cl FE=-Fe # no libpng # OBJS=getopt$(OBJ) getopt1$(OBJ) jbig2$(OBJ) jbig2_arith$(OBJ) \ jbig2_arith_iaid$(OBJ) jbig2_arith_int$(OBJ) jbig2_huffman$(OBJ) \ jbig2_hufftab$(OBJ) jbig2_generic$(OBJ) jbig2_refinement$(OBJ) \ jbig2_halftone$(OBJ) jbig2_image$(OBJ) jbig2_image_pbm$(OBJ) \ $(JBIG2_IMAGE_PNG_OBJ) jbig2_segment$(OBJ) jbig2_symbol_dict$(OBJ) \ jbig2_text$(OBJ) jbig2_mmr$(OBJ) jbig2_page$(OBJ) jbig2dec$(OBJ) \ sha1$(OBJ) HDRS=getopt.h jbig2.h jbig2_arith.h jbig2_arith_iaid.h jbig2_arith_int.h \ jbig2_generic.h jbig2_huffman.h jbig2_hufftab.h jbig2_image.h \ jbig2_mmr.h jbig2_priv.h jbig2_symbol_dict.h config_win32.h sha1.h all: jbig2dec$(EXE) jbig2dec$(EXE): $(OBJS) $(CC) $(CFLAGS) $(FE)jbig2dec$(EXE) $(OBJS) $(LIBPNG_LDFLAGS) getopt$(OBJ): getopt.c getopt.h $(CC) $(CFLAGS) -c getopt.c getopt1$(OBJ): getopt1.c getopt.h $(CC) $(CFLAGS) -c getopt1.c jbig2$(OBJ): jbig2.c $(HDRS) $(CC) $(CFLAGS) -c jbig2.c jbig2_arith$(OBJ): jbig2_arith.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_arith.c jbig2_arith_iaid$(OBJ): jbig2_arith_iaid.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_arith_iaid.c jbig2_arith_int$(OBJ): jbig2_arith_int.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_arith_int.c jbig2_generic$(OBJ): jbig2_generic.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_generic.c jbig2_refinement$(OBJ): jbig2_refinement.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_refinement.c jbig2_huffman$(OBJ): jbig2_huffman.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_huffman.c jbig2_hufftab$(OBJ): jbig2_hufftab.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_hufftab.c jbig2_image$(OBJ): jbig2_image.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_image.c jbig2_image_pbm$(OBJ): jbig2_image_pbm.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_image_pbm.c jbig2_image_png$(OBJ): jbig2_image_png.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_image_png.c jbig2_halftone$(OBJ): jbig2_halftone.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_halftone.c jbig2_mmr$(OBJ): jbig2_mmr.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_mmr.c jbig2_page$(OBJ): jbig2_page.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_page.c jbig2_segment$(OBJ): jbig2_segment.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_segment.c jbig2_symbol_dict$(OBJ): jbig2_symbol_dict.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_symbol_dict.c jbig2_text$(OBJ): jbig2_text.c $(HDRS) $(CC) $(CFLAGS) -c jbig2_text.c jbig2dec$(OBJ): jbig2dec.c $(HDRS) $(CC) $(CFLAGS) -c jbig2dec.c sha1$(OBJ): sha1.c $(HDRS) $(CC) $(CFLAGS) -c sha1.c clean: -del $(OBJS) -del jbig2dec$(EXE) -del jbig2dec.ilk -del jbig2dec.pdb -del pbm2png$(EXE) -del pbm2png.ilk -del pbm2png.pdb -del vc70.pdb -del vc60.pdb -del vc50.pdb jbig2dec-0.18/os_types.h000066400000000000000000000024751362055302000151350ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ /* indirection layer for build and platform-specific definitions in general, this header should ensure that the stdint types are available, and that any optional compile flags are defined if the build system doesn't pass them directly. */ #ifndef _JBIG2_OS_TYPES_H #define _JBIG2_OS_TYPES_H #if defined(HAVE_CONFIG_H) # include "config_types.h" #elif defined(_WIN32) # include "config_win32.h" #elif defined (STD_INT_USE_SYS_TYPES_H) # include #elif defined (STD_INT_USE_INTTYPES_H) # include #elif defined (STD_INT_USE_SYS_INTTYPES_H) # include #elif defined (STD_INT_USE_SYS_INT_TYPES_H) # include #else # include #endif #endif /* _JBIG2_OS_TYPES_H */ jbig2dec-0.18/pbm2png.c000066400000000000000000000031751362055302000146260ustar00rootroot00000000000000/* Copyright (C) 2001-2019 Artifex Software, Inc. All Rights Reserved. This software is provided AS-IS with no warranty, either express or implied. This software is distributed under license and may not be copied, modified or distributed except as expressly authorized under the terms of the license contained in the file LICENSE in this distribution. Refer to licensing information at http://www.artifex.com or contact Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, CA 94945, U.S.A., +1(415)492-9861, for further information. */ /* jbig2dec */ #ifdef HAVE_CONFIG_H #include "config.h" #include "config_types.h" #elif _WIN32 #include "config_win32.h" #endif #ifdef HAVE_STDINT_H #include #endif #include #include #include #include "jbig2.h" #include "jbig2_image.h" #include "jbig2_image_rw.h" int main(int argc, char *argv[]) { Jbig2Ctx *ctx; Jbig2Image *image; int code; /* we need a context for the allocators */ ctx = jbig2_ctx_new(NULL, 0, NULL, NULL, NULL); if (argc != 3) { fprintf(stderr, "usage: %s \n\n", argv[0]); return 1; } image = jbig2_image_read_pbm_file(ctx, argv[1]); if (image == NULL) { fprintf(stderr, "error reading pbm file '%s'\n", argv[1]); return 1; } else { fprintf(stderr, "converting %dx%d image to png format\n", image->width, image->height); } code = jbig2_image_write_png_file(image, argv[2]); if (code) { fprintf(stderr, "error writing png file '%s' error %d\n", argv[2], code); } return (code); } jbig2dec-0.18/sha1.c000066400000000000000000000271131362055302000141130ustar00rootroot00000000000000/* SHA-1 in C By Steve Reid 100% Public Domain ----------------- Modified 7/98 By James H. Brown Still 100% Public Domain Corrected a problem which generated improper hash values on 16 bit machines Routine SHA1Update changed from void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) to void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned long len) The 'len' parameter was declared an int which works fine on 32 bit machines. However, on 16 bit machines an int is too small for the shifts being done against it. This caused the hash function to generate incorrect values if len was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since an int and a long are the same size in those environments. -- I also corrected a few compiler warnings generated by Borland C. 1. Added #include for exit() prototype 2. Removed unused variable 'j' in SHA1Final 3. Changed exit(0) to return(0) at end of main. ALL changes I made can be located by searching for comments containing 'JHB' ----------------- Modified 8/98 By Steve Reid Still 100% public domain 1- Removed #include and used return() instead of exit() 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net ----------------- Modified 4/01 By Saul Kravitz Still 100% PD Modified to run on Compaq Alpha hardware. ----------------- Modified 07/2002 By Ralph Giles Still 100% public domain modified for use with stdint types, autoconf code cleanup, removed attribution comments switched SHA1Final() argument order for consistency use SHA1_ prefix for public api move public api to sha1.h */ /* Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ /* #define SHA1HANDSOFF */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "os_types.h" #include "sha1.h" void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ /* FIXME: can we do this in an endian-proof way? */ #ifdef WORDS_BIGENDIAN #define blk0(i) block->l[i] #else #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); #ifdef VERBOSE /* SAK */ void SHAPrintContext(SHA1_CTX *context, char *msg) { printf("%s (%d,%d) %x %x %x %x %x\n", msg, context->count[0], context->count[1], context->state[0], context->state[1], context->state[2], context->state[3], context->state[4]); } #endif /* VERBOSE */ /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) { uint32_t a, b, c, d, e; typedef union { uint8_t c[64]; uint32_t l[16]; } CHAR64LONG16; CHAR64LONG16 *block; #ifdef SHA1HANDSOFF static uint8_t workspace[64]; block = (CHAR64LONG16 *) workspace; memcpy(block, buffer, 64); #else block = (CHAR64LONG16 *) buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1Init - Initialize new context */ void SHA1_Init(SHA1_CTX *context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1_Update(SHA1_CTX *context, const uint8_t *data, const size_t len) { size_t i, j; #ifdef VERBOSE SHAPrintContext(context, "before"); #endif j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64 - j)); SHA1_Transform(context->state, context->buffer); for (; i + 63 < len; i += 64) { SHA1_Transform(context->state, data + i); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); #ifdef VERBOSE SHAPrintContext(context, "after "); #endif } /* Add padding and return the message digest. */ void SHA1_Final(SHA1_CTX *context, uint8_t digest[SHA1_DIGEST_SIZE]) { uint32_t i; uint8_t finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ } SHA1_Update(context, (uint8_t *) "\200", 1); while ((context->count[0] & 504) != 448) { SHA1_Update(context, (uint8_t *) "\0", 1); } SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ for (i = 0; i < SHA1_DIGEST_SIZE; i++) { digest[i] = (uint8_t) ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); } /* Wipe variables */ i = 0; memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(finalcount, 0, 8); /* SWR */ #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ SHA1_Transform(context->state, context->buffer); #endif } /*************************************************************/ #if 0 int main(int argc, char **argv) { int i, j; SHA1_CTX context; unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384]; FILE *file; if (argc > 2) { puts("Public domain SHA-1 implementation - by Steve Reid "); puts("Modified for 16 bit environments 7/98 - by James H. Brown "); /* JHB */ puts("Produces the SHA-1 hash of a file, or stdin if no file is specified."); return (0); } if (argc < 2) { file = stdin; } else { if (!(file = fopen(argv[1], "rb"))) { fputs("Unable to open file.", stderr); return (-1); } } SHA1_Init(&context); while (!feof(file)) { /* note: what if ferror(file) */ i = fread(buffer, 1, 16384, file); SHA1_Update(&context, buffer, i); } SHA1_Final(&context, digest); fclose(file); for (i = 0; i < SHA1_DIGEST_SIZE / 4; i++) { for (j = 0; j < 4; j++) { printf("%02X", digest[i * 4 + j]); } putchar(' '); } putchar('\n'); return (0); /* JHB */ } #endif /* self test */ #ifdef TEST static char *test_data[] = { "abc", "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", "A million repetitions of 'a'" }; static char *test_results[] = { "A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D", "84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1", "34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F" }; void digest_to_hex(const uint8_t digest[SHA1_DIGEST_SIZE], char *output) { int i, j; char *c = output; for (i = 0; i < SHA1_DIGEST_SIZE / 4; i++) { for (j = 0; j < 4; j++) { sprintf(c, "%02X", digest[i * 4 + j]); c += 2; } sprintf(c, " "); c += 1; } *(c - 1) = '\0'; } int main(int argc, char **argv) { int k; SHA1_CTX context; uint8_t digest[20]; char output[80]; fprintf(stdout, "verifying SHA-1 implementation... "); for (k = 0; k < 2; k++) { SHA1_Init(&context); SHA1_Update(&context, (uint8_t *) test_data[k], strlen(test_data[k])); SHA1_Final(&context, digest); digest_to_hex(digest, output); if (strcmp(output, test_results[k])) { fprintf(stdout, "FAIL\n"); fprintf(stderr, "* hash of \"%s\" incorrect:\n", test_data[k]); fprintf(stderr, "\t%s returned\n", output); fprintf(stderr, "\t%s is correct\n", test_results[k]); return (1); } } /* million 'a' vector we feed separately */ SHA1_Init(&context); for (k = 0; k < 1000000; k++) SHA1_Update(&context, (uint8_t *) "a", 1); SHA1_Final(&context, digest); digest_to_hex(digest, output); if (strcmp(output, test_results[2])) { fprintf(stdout, "FAIL\n"); fprintf(stderr, "* hash of \"%s\" incorrect:\n", test_data[2]); fprintf(stderr, "\t%s returned\n", output); fprintf(stderr, "\t%s is correct\n", test_results[2]); return (1); } /* success */ fprintf(stdout, "ok\n"); return (0); } #endif /* TEST */ jbig2dec-0.18/sha1.h000066400000000000000000000010741362055302000141160ustar00rootroot00000000000000/* public api for steve reid's public domain SHA-1 implementation */ /* this file is in the public domain */ #ifndef __SHA1_H #define __SHA1_H #ifdef __cplusplus extern "C" { #endif typedef struct { uint32_t state[5]; uint32_t count[2]; uint8_t buffer[64]; } SHA1_CTX; #define SHA1_DIGEST_SIZE 20 void SHA1_Init(SHA1_CTX *context); void SHA1_Update(SHA1_CTX *context, const uint8_t *data, const size_t len); void SHA1_Final(SHA1_CTX *context, uint8_t digest[SHA1_DIGEST_SIZE]); #ifdef __cplusplus } #endif #endif /* __SHA1_H */ jbig2dec-0.18/snprintf.c000066400000000000000000000074271362055302000151300ustar00rootroot00000000000000/* * Revision 12: http://theos.com/~deraadt/snprintf.c * * Copyright (c) 1997 Theo de Raadt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifdef __VMS #include #else #include #endif #include #include #include #include #if __STDC__ #include #include #else #include #endif #include #include #include #ifndef roundup #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) #endif #ifdef __sgi #define size_t ssize_t #endif static int pgsize; static char *curobj; static int caught; static sigjmp_buf bail; #define EXTRABYTES 2 /* XXX: why 2? you don't want to know */ static char * msetup(str, n) char *str; size_t n; { char *e; if (n == 0) return NULL; if (pgsize == 0) pgsize = getpagesize(); curobj = (char *)malloc(n + EXTRABYTES + pgsize * 2); if (curobj == NULL) return NULL; e = curobj + n + EXTRABYTES; e = (char *)roundup((unsigned long)e, pgsize); if (mprotect(e, pgsize, PROT_NONE) == -1) { free(curobj); curobj = NULL; return NULL; } e = e - n - EXTRABYTES; *e = '\0'; return (e); } static void mcatch(int a) { siglongjmp(bail, 1); } static void mcleanup(str, n, p) char *str; size_t n; char *p; { strncpy(str, p, n - 1); str[n - 1] = '\0'; if (mprotect((caddr_t)(p + n + EXTRABYTES), pgsize, PROT_READ | PROT_WRITE | PROT_EXEC) == -1) mprotect((caddr_t)(p + n + EXTRABYTES), pgsize, PROT_READ | PROT_WRITE); free(curobj); } int #if __STDC__ vsnprintf(char *str, size_t n, char const *fmt, va_list ap) #else vsnprintf(str, n, fmt, ap) char *str; size_t n; char *fmt; char *ap; #endif { struct sigaction osa, nsa; char *p; int ret = n + 1; /* if we bail, indicated we overflowed */ memset(&nsa, 0, sizeof nsa); nsa.sa_handler = mcatch; sigemptyset(&nsa.sa_mask); p = msetup(str, n); if (p == NULL) { *str = '\0'; return 0; } if (sigsetjmp(bail, 1) == 0) { if (sigaction(SIGSEGV, &nsa, &osa) == -1) { mcleanup(str, n, p); return (0); } ret = vsprintf(p, fmt, ap); } mcleanup(str, n, p); (void)sigaction(SIGSEGV, &osa, NULL); return (ret); } int #if __STDC__ snprintf(char *str, size_t n, char const *fmt, ...) #else snprintf(str, n, fmt, va_alist) char *str; size_t n; char *fmt; va_dcl #endif { va_list ap; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif return (vsnprintf(str, n, fmt, ap)); va_end(ap); } jbig2dec-0.18/test_jbig2dec.py000077500000000000000000000275571362055302000162140ustar00rootroot00000000000000#! /usr/bin/env python # this is the test script for jbig2dec import os, re import sys, time import hashlib class SelfTest: 'generic class for self tests' def __init__(self): self.result = 'unrun' self.msg = '' def shortDescription(self): 'returns a short name for the test' return "generic self test" def runTest(self): 'call this to execute the test' pass def fail(self, msg=None): self.result = 'FAIL' self.msg = msg def failIf(self, check, msg=None): if check: self.fail(msg) def assertEqual(self, a, b, msg=None): if a != b: self.fail(msg) class SelfTestSuite: 'generic class for running a collection of SelfTest instances' def __init__(self, stream=sys.stderr): self.stream = stream self.tests = [] self.fails = [] self.xfails = [] self.errors = [] def addTest(self, test): self.tests.append(test) def run(self): starttime = time.time() for test in self.tests: self.stream.write("%s ... " % test.shortDescription()) test.result = 'ok' test.runTest() if test.result != 'ok': self.fails.append(test) self.stream.write("%s\n" % test.result) stoptime = time.time() self.stream.write('-'*72 + '\n') self.stream.write('ran %d tests in %.3f seconds\n\n' % (len(self.tests), stoptime - starttime)) if len(self.fails): self.stream.write('FAILED %d of %d tests\n' % (len(self.fails),len(self.tests))) return False else: self.stream.write('PASSED all %d tests\n' % len(self.tests)) return True class KnownFileHash(SelfTest): 'self test to check for correct decode of known test files' # hashes of known test inputs known_NOTHING_DECODED = "da39a3ee5e6b4b0d3255bfef95601890afd80709" known_WHITE_PAGE_DECODED = "28a6bd83a8a3a36910fbc1f5ce06c962e4332911" known_042_DECODED = "ebfdf6e2fc5ff3ee2271c2fa19de0e52712046e8" known_amb_DECODED = "3d4b7992d506894662b53415bd3d0d2a2f8b7953" # these are known test files in the form # (filename, sha-1(file), sha-1(decoded document) known_hashes = ( ('tests/ubc/042_1.jb2', "673e1ee5c55ab241b171e476ba1168a42733ddaa", known_042_DECODED), ('tests/ubc/042_2.jb2', "9aa2804e2d220952035c16fb3c907547884067c5", known_042_DECODED), ('tests/ubc/042_3.jb2', "9663a5f35727f13e61a0a2f0a64207b1f79e7d67", known_042_DECODED), ('tests/ubc/042_4.jb2', "014df658c8b99b600c2ceac3f1d53c7cc2b4917c", known_042_DECODED), ('tests/ubc/042_5.jb2', "264720a6ccbbf72aa6a2cfb6343f43b8e6f2da4b", known_042_DECODED), ('tests/ubc/042_6.jb2', "96f7dc9df4a1b305f9ac082dd136f85ef5b108fe", known_042_DECODED), ('tests/ubc/042_7.jb2', "5526371ba9dc2b8743f20ae3e05a7e60b3dcba76", known_042_DECODED), ('tests/ubc/042_8.jb2', "4bf0c87dfaf40d67c36f2a083579eeda26d54641", known_042_DECODED), ('tests/ubc/042_9.jb2', "53e630e7fe2fe6e1d6164758e15fc93382e07f55", known_042_DECODED), ('tests/ubc/042_10.jb2', "5ca1364367e25cb8f642e9dc677a94d5cfed0c8b", known_042_DECODED), ('tests/ubc/042_11.jb2', "bc194caf022bc5345fc41259e05cea3c08245216", known_042_DECODED), ('tests/ubc/042_12.jb2', "f354df8eb4849bc707f088739e322d1fe3a14ef3", known_042_DECODED), ('tests/ubc/042_13.jb2', "7d428bd542f58591b254d9827f554b0552c950a7", known_WHITE_PAGE_DECODED), ('tests/ubc/042_14.jb2', "c40fe3a02acb6359baf9b40fc9c49bc0800be589", known_WHITE_PAGE_DECODED), ('tests/ubc/042_15.jb2', "a9e39fc1ecb178aec9f05039514d75ea3246246c", known_042_DECODED), ('tests/ubc/042_16.jb2', "4008bbca43670f3c90eaee26516293ba95baaf3d", known_042_DECODED), ('tests/ubc/042_17.jb2', "0ff95637b64c57d659a41c582da03e25321551fb", known_042_DECODED), ('tests/ubc/042_18.jb2', "87381d044f00c4329200e44decbe91bebfa31595", known_042_DECODED), ('tests/ubc/042_19.jb2', "387d95a140b456d4742622c788cf5b51cebbf438", known_042_DECODED), ('tests/ubc/042_20.jb2', "85c19e9ec42b8ddd6b860a1bebea1c67610e7a59", known_042_DECODED), ('tests/ubc/042_21.jb2', "ab535c7d7a61a7b9dc53d546e7419ca78ac7f447", known_042_DECODED), ('tests/ubc/042_22.jb2', "a9e2b365be63716dbde74b0661c3c6efd2a6844d", known_042_DECODED), ('tests/ubc/042_23.jb2', "8ffa40a05e93e10982b38a2233a8da58c1b5c343", known_042_DECODED), ('tests/ubc/042_24.jb2', "2553fe65111c58f6412de51d8cdc71651e778ccf", known_042_DECODED), ('tests/ubc/042_25.jb2', "52de4a3b86252d896a8d783ba71dd0699333dd69", known_042_DECODED), ('tests/ubc/amb_1.jb2', "d6d6d1c981dc37a09108c1e3ed990aa5b345fa6a", known_amb_DECODED), ('tests/ubc/amb_2.jb2', "9af6616a89eb03f8934de72626e301a716366c3c", known_amb_DECODED), ('tests/ubc/200-10-0.jb2', "f6014b43775640ef0874497e0873f8deb291cc32", "49cddf903d3451ba23297a6b68502504093979cf"), ('tests/ubc/200-10-0-stripe.jb2', "d19f58cd180afd1ae2afd11c96471e98c7c6f125", "ac89ae2046c4859348418830287982b6d60bf39b"), ('tests/ubc/200-10-45.jb2', "504297b028810f812cbf075597f589a9fb82121b", "38aa99e40c6a746391c26c953223bcd4549cadd0"), ('tests/ubc/200-10-45-stripe.jb2', "0d9f2a63c9fd224a6b60a9b7c0cd658f47551edd", "2921889fc5ffaafb348084761aa7c54831ec57ba"), ('tests/ubc/200-20-0.jb2', "a40aaf33dd4c3225728ddfc0fad12167ceff1b17", "cc1732742d5d68c6d5c3f4eec9d5887e9ee24cd0"), ('tests/ubc/200-20-0-stripe.jb2', "d499a89baf69a1b5f6fa450ec20b21136052b4cd", "743aa86e7abc9e238e23d02fbc993b048589282a"), ('tests/ubc/200-20-45.jb2', "a39f1e2670f1c08dbd07d14a99965bf7253e6318", "7213fb351f65397c12accf662787aa3bc028c40f"), ('tests/ubc/200-20-45-stripe.jb2', "3aa44cdef38fc8e34376480408ca99364ccbf0ee", "9021716b3eca4da549508db691655eddc4d51548"), ('tests/ubc/200-2-0.jb2', "087f529ba6e3cc5fca3773c1d07e39fb642f5052", "534fceffada398444ce065088a37b6d6517a3406"), ('tests/ubc/200-2-0-stripe.jb2', "dc227f7531ccecda08511bda9359864c66a8d230", "56f0e25ae5863a75d69a1825b820ba004e48d2c4"), ('tests/ubc/200-3-0.jb2', "024a20b82e794eb469b4fae2b4f930c5c079fd6b", "57fe3645b028e6c7a68dcf707674f889038ee4b5"), ('tests/ubc/200-3-0-stripe.jb2', "2322db7dc956863b7257d28a212431e304661998", "32ea498b28a46bf04e0b799c70114ab99ce7d15e"), ('tests/ubc/200-3-45.jb2', "21ba06f8cfcc31b5bd7fa39ad98093180d3e05aa", "83e01d0a83d167fe00f7389e5fec0a660841aeef"), ('tests/ubc/200-3-45-stripe.jb2', "6dfe3cbb019ef0c30ecae7d2196b1b3fd7634288", "20c2ade5766eeb3a70dca9963029c0a74171064b"), ('tests/ubc/200-4-0.jb2', "c7d8d8b8a97388b0fcc6e5e3d8708fbce0881edf", "b85fe470db7542789b0632dc87dbdc721e07ddf5"), ('tests/ubc/200-4-0-stripe.jb2', "840f076fd542b2ae8d0d1663ed7efd5683326bc7", "0acd5a6f24637dad4b948fa24563b1fae04996be"), ('tests/ubc/200-4-45.jb2', "6ed49af06268d57137436ffeea2def6f93ea17eb", "5177abf7e9d641ca4f553bd4847134e51bb1159a"), ('tests/ubc/200-4-45-stripe.jb2', "0dfc5b59a046ab05364298b1767334298fa03eeb", "944a399d8763007ae0477f69b80ec28d7fbe6edd"), ('tests/ubc/200-5-0.jb2', "47770e4144b022790af00098ac830ac8665f62a0", "515eaf8e4537bbda841abf3b7ffbd1b4728c7597"), ('tests/ubc/200-5-0-stripe.jb2', "23f784c297c204bc1bf7cd1559a7c38a95097266", "c69e97f9e1a7e45d6eb3975ecb8a4a7dd7f09e2e"), ('tests/ubc/200-5-45.jb2', "193376e966e8bc22868e38791289e810953b5483", "77fff5286023b77316221d5c36a6d40f8b905ca9"), ('tests/ubc/200-5-45-stripe.jb2', "d211863df684b5c113c2e29aec72c6a533681356", "efd7b9ae877bf3d71c0baa604a1014e1218ada90"), ('tests/ubc/200-6-0.jb2', "e66a8cff6c00575018253a06f9309192cc796fb2", "7b5dae69e6f8953463dd29707f77225cd8a543ad"), ('tests/ubc/200-6-0-stripe.jb2', "55ba1b94e73d96defbb7abbe35ccf13b4e1ac89f", "e8a1b55780dde4102f37ca5fafeff29bbd30e867"), ('tests/ubc/200-6-45.jb2', "71d167f8af4e6c2a3202c26873aedf490e8da8f2", "9093ba8bfc65b87dddc310d437b8ca626ee2283c"), ('tests/ubc/200-6-45-stripe.jb2', "abcf8f71f9ce0cb65c43942ecf0cfda7ece5d7ff", "397a48e4f3a3261928b2175699104117e36349e6"), ('tests/ubc/200-8-0.jb2', "e7004846acb5529d5335c16315d11c188edea89d", "8cfa43f514911d35d9666e52ae51bbd93a9bddfc"), ('tests/ubc/200-8-0-stripe.jb2', "0d96be49231e7e5a52c41bfa7303768465a9fa81", "021fbcfa12122999cded6beea3b7aa3c7018acbd"), ('tests/ubc/200-8-45.jb2', "e28403c3bf1014a5b5e9c3c3e5e99cae47aa09ab", "669986963011b174d5352d38e6c77f459ff3bebd"), ('tests/ubc/200-8-45-stripe.jb2', "c2e19b3e51d06c102a06643f3ea15f77d6df3788", "ceb0ef29cb68fe53d9abceb45ab182c1e6a39ff7"), ('tests/ubc/200-lossless.jb2', "b9989aea1a3edd65e38e7fbeaa89a29d7a2aa342", "94d9324437bc27955e610ef4fbbd684ad3107fea"), ('tests/ubc/600-10-0.jb2', "46c9af206382243d838f86ea45c63e7ca2900b68", "0ad323815315270f02f8220ed3b69133a1639f74"), ('tests/ubc/600-10-45.jb2', "1f143e95bf57d8d2696525797e198efd785f7221", "16002bb4e4cefbb58da5dde531b1064b9e6ad1a7"), ('tests/ubc/600-20-0.jb2', "8c874b1fb89e714ef8c64f33d292db2aea4fd05f", "a537aac28d9e0ea27d43a38024962f86aa1e403b"), ('tests/ubc/600-20-45.jb2', "a9c94915dd140916bc14db7b4bc9fc5d7e73b5a9", "5af6ec6f2e8ae68cfb6df3f82bf47ee2f6c4f0b5"), ('tests/ubc/600-30-0.jb2', "f0b9eea13b5c7a18742238778f1a3b7e1a4d3361", "6feaffc771381922a578bc54c4b50d18e7933ea1"), ('tests/ubc/600-30-45.jb2', "65bb4202b575bba6063ef3597a5eefa356b5e660", "768788c5176d5ffb5d8d0855d8ab34312611f67d"), ('tests/ubc/600-6-0.jb2', "c54abd4bdbb26b1f1209dc03ab10c05cdfd7a63a", "baba4bc5359c0fafc54efcba14da2bd5943222be"), ('tests/ubc/600-6-45.jb2', "94f4f6ea60eda33e0cd8bb94a5a0f90dc05f96a7", "bc3afe7c37533ca43f3244e6877ce38b3e978e9f"), ('tests/ubc/600-lossless.jb2', "60ecd5ddfb0984e3d2691bc385f425a50c753019", "f632d82b3c3d500098ad560e5ab91c69bd20827f") ) def __init__(self, file, file_hash, decode_hash): SelfTest.__init__(self) self.file = file self.file_hash = file_hash self.decode_hash = decode_hash def shortDescription(self): return "Checking '%s' for correct decoded document hash" % self.file def runTest(self): '''jbig2dec should return proper document hashes for known files''' # verify that the input file hash is correct sha1 = hashlib.sha1() with open(self.file, 'rb') as f: sha1.update(f.read()) self.assertEqual(self.file_hash, sha1.hexdigest()) # invoke jbig2dec on our file instance = os.popen('./jbig2dec -q -o /dev/null --hash ' + self.file) lines = instance.readlines() exit_code = instance.close() self.failIf(exit_code, 'jbig2dec should exit normally') # test here for correct hash hash_pattern = re.compile('[0-9a-f]{%d}' % len(decode_hash)) for line in lines: m = hash_pattern.search(line.lower()) if m: self.assertEqual(self.decode_hash, m.group(), 'hash of known decoded document must be correct') return self.fail('document hash was not found in the output') suite = SelfTestSuite() for filename, file_hash, decode_hash in KnownFileHash.known_hashes: # only add tests for files we can find if not os.access(filename, os.R_OK): continue # todo: verify our file matches its encoded document hash suite.addTest(KnownFileHash(filename, file_hash, decode_hash)) # run the defined tests if we're called as a script if __name__ == "__main__": result = suite.run() sys.exit(not result)