diff options
author | Thomas Deutschmann <whissi@gentoo.org> | 2019-10-15 12:24:12 +0200 |
---|---|---|
committer | Thomas Deutschmann <whissi@gentoo.org> | 2020-08-13 11:26:55 +0200 |
commit | e088156d5b620e5e639580dacf85c6dc13823c74 (patch) | |
tree | 57f5c025e203279944da512166c20bc0521d8ccd /jbig2dec | |
download | ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.gz ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.tar.bz2 ghostscript-gpl-patches-e088156d5b620e5e639580dacf85c6dc13823c74.zip |
Import Ghostscript 9.50ghostscript-9.50
Signed-off-by: Thomas Deutschmann <whissi@gentoo.org>
Diffstat (limited to 'jbig2dec')
59 files changed, 19517 insertions, 0 deletions
diff --git a/jbig2dec/CHANGES b/jbig2dec/CHANGES new file mode 100644 index 00000000..052a0382 --- /dev/null +++ b/jbig2dec/CHANGES @@ -0,0 +1,115 @@ +Version 0.17 (2019 October 1) + +* Updated documentation with accurate contact information. + +* Moved version number to jbig2.h, and adapted configure + correspondingly. Added pkg-config file to be installed + along side library. Added run-time check of version + number so that the correct header is used with the matching + binary library. + +* Bug fixes. + +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 + diff --git a/jbig2dec/COPYING b/jbig2dec/COPYING new file mode 100644 index 00000000..dba13ed2 --- /dev/null +++ b/jbig2dec/COPYING @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU 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 <http://www.gnu.org/licenses/>. + +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 +<http://www.gnu.org/licenses/>. diff --git a/jbig2dec/LICENSE b/jbig2dec/LICENSE new file mode 100644 index 00000000..7008d546 --- /dev/null +++ b/jbig2dec/LICENSE @@ -0,0 +1,23 @@ + 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. diff --git a/jbig2dec/Makefile.am b/jbig2dec/Makefile.am new file mode 100644 index 00000000..e207b7a7 --- /dev/null +++ b/jbig2dec/Makefile.am @@ -0,0 +1,57 @@ +## 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_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 + diff --git a/jbig2dec/Makefile.unix b/jbig2dec/Makefile.unix new file mode 100644 index 00000000..24bb12d1 --- /dev/null +++ b/jbig2dec/Makefile.unix @@ -0,0 +1,44 @@ +# 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_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 + diff --git a/jbig2dec/README b/jbig2dec/README new file mode 100644 index 00000000..7a0e53d2 --- /dev/null +++ b/jbig2dec/README @@ -0,0 +1,32 @@ +jbig2dec 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 <output file>] file.jbig2 + +It also supports separate 'global' and 'page' streams, generally +extracted from some embedded format: + + jbig2dec [-o <output file>] <global_stream> <page_stream> + +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 <gs-devel@ghostscript.com>. diff --git a/jbig2dec/annex-h.jbig2 b/jbig2dec/annex-h.jbig2 Binary files differnew file mode 100644 index 00000000..bf2faf85 --- /dev/null +++ b/jbig2dec/annex-h.jbig2 diff --git a/jbig2dec/autogen.sh b/jbig2dec/autogen.sh new file mode 100755 index 00000000..1dbe143c --- /dev/null +++ b/jbig2dec/autogen.sh @@ -0,0 +1,180 @@ +#!/bin/sh +# Run this to set up the build system: configure, makefiles, etc. + +package="jbig2dec" +AUTOMAKE_FLAGS="$AUTOMAKE_FLAGS" + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +cd "$srcdir" + +echo "checking for autoconf... " +(autoconf --version) < /dev/null > /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]\+\)).*/\\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 <<EOF +/* + generated header with missing types for the + jbig2dec program and library. include this + after config.h, within the HAVE_CONFIG_H + ifdef +*/ + +#ifdef HAVE_STDINT_H +# include <stdint.h> +#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 diff --git a/jbig2dec/config_win32.h b/jbig2dec/config_win32.h new file mode 100644 index 00000000..cef8acdb --- /dev/null +++ b/jbig2dec/config_win32.h @@ -0,0 +1,58 @@ +/* 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 <stdint.h> +# 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 +# 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 <stdint.h> + +#endif /* _MSC_VER */ diff --git a/jbig2dec/configure.ac.in b/jbig2dec/configure.ac.in new file mode 100644 index 00000000..972d0818 --- /dev/null +++ b/jbig2dec/configure.ac.in @@ -0,0 +1,150 @@ +# 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 diff --git a/jbig2dec/getopt.c b/jbig2dec/getopt.c new file mode 100644 index 00000000..fea86254 --- /dev/null +++ b/jbig2dec/getopt.c @@ -0,0 +1,947 @@ +/* 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 <stdio.h>. + Ditto for AIX 3.2 and <stdlib.h>. */ +#ifndef _NO_PROTO +# define _NO_PROTO +#endif + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#if !defined __STDC__ || !__STDC__ +/* This is a separate conditional since some stdc systems + reject `defined (const)'. */ +# ifndef const +# define const +# endif +#endif + +#include <stdio.h> + +/* 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 <gnu-versions.h> +# 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 <stdlib.h> +# include <unistd.h> +#endif /* GNU C library. */ + +#ifdef VMS +# include <unixlib.h> +# if HAVE_STRING_H - 0 +# include <string.h> +# endif +#endif + +#ifndef _ +/* This is for other GNU distributions with internationalized messages. */ +# if defined HAVE_LIBINTL_H || defined _LIBC +# include <libintl.h> +# 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 <string.h> +# define my_index strchr +#else + +# if HAVE_STRING_H +# include <string.h> +# else +# include <strings.h> +# 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 */ diff --git a/jbig2dec/getopt.h b/jbig2dec/getopt.h new file mode 100644 index 00000000..6111a4a6 --- /dev/null +++ b/jbig2dec/getopt.h @@ -0,0 +1,173 @@ +/* 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 <features.h>, but + that does not exist if we are standalone. So: if __GNU_LIBRARY__ is + not defined, include <ctype.h>, which will pull in <features.h> 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 <ctype.h> +#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 */ diff --git a/jbig2dec/getopt1.c b/jbig2dec/getopt1.c new file mode 100644 index 00000000..7f061c08 --- /dev/null +++ b/jbig2dec/getopt1.c @@ -0,0 +1,182 @@ +/* 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 <config.h> +#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 <stdio.h> + +/* 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 <gnu-versions.h> +#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 <stdlib.h> +#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 <stdio.h> + +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 */ diff --git a/jbig2dec/jbig2.c b/jbig2dec/jbig2.c new file mode 100644 index 00000000..07c7969f --- /dev/null +++ b/jbig2dec/jbig2.c @@ -0,0 +1,534 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <limits.h> + +#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) + +int16_t +jbig2_get_int16(const byte *bptr) +{ + return get_int16(bptr); +} + +uint16_t +jbig2_get_uint16(const byte *bptr) +{ + return get_uint16(bptr); +} + +int32_t +jbig2_get_int32(const byte *bptr) +{ + return ((int32_t) get_int16(bptr) << 16) | get_uint16(bptr + 2); +} + +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 %u", 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_SEQUENCIAL_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); +} diff --git a/jbig2dec/jbig2.h b/jbig2dec/jbig2.h new file mode 100644 index 00000000..16bb95ac --- /dev/null +++ b/jbig2dec/jbig2.h @@ -0,0 +1,111 @@ +/* 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 (17) + +/* 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 */ + +#ifdef __cplusplus +} +#endif diff --git a/jbig2dec/jbig2_arith.c b/jbig2dec/jbig2_arith.c new file mode 100644 index 00000000..6416fad6 --- /dev/null +++ b/jbig2dec/jbig2_arith.c @@ -0,0 +1,411 @@ +/* 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 <stdio.h> +#include <stdlib.h> + +#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; +}; + +#undef SOFTWARE_CONVENTION + +/* + A note on the "software conventions". + + Previously, I had misinterpreted the spec, and had thought that the + spec's description of the "software convention" was wrong. Now I + believe that this code is both correct and matches the spec, with + SOFTWARE_CONVENTION defined or not. Thanks to William Rucklidge for + the clarification. + + In any case, my benchmarking indicates no speed difference at all. + Therefore, for now we will just use the normative version. + + */ + +static void +jbig2_arith_bytein(Jbig2ArithState *as) +{ + int new_bytes; + byte B; + + /* invariant: as->next_word_bytes > 0 */ + + /* 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 +#ifndef SOFTWARE_CONVENTION + as->C += 0xFF00; +#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 +#ifdef SOFTWARE_CONVENTION + as->C += 0xFE00 - (B1 << 9); +#else + as->C += B1 << 9; +#endif + 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 +#ifndef SOFTWARE_CONVENTION + as->C += 0xFF00; +#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 + +#ifdef SOFTWARE_CONVENTION + as->C += 0xFE00 - (B1 << 9); +#else + as->C += (B1 << 9); +#endif + 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); +#ifdef SOFTWARE_CONVENTION + as->C += 0xFF00 - (B << 8); +#else + as->C += (B << 8); +#endif + } +} + +/** 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 */ +#ifdef SOFTWARE_CONVENTION + result->C = (~(result->next_word >> 8)) & 0xFF0000; +#else + result->C = (result->next_word >> 8) & 0xFF0000; +#endif + + 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; + +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); +} + +bool +jbig2_arith_decode(Jbig2ArithState *as, Jbig2ArithCx *pcx, int *code) +{ + Jbig2ArithCx cx = *pcx; + const Jbig2ArithQe *pqe; + unsigned int index = cx & 0x7f; + bool D; + + if (index >= MAX_QE_ARRAY_SIZE) { + *code = -1; + return 0; + } else { + pqe = &jbig2_arith_Qe[index]; + } + + /* Figure E.15 */ + as->A -= pqe->Qe; + if ( +#ifdef SOFTWARE_CONVENTION + /* Note: I do not think this is correct. See above. */ + (as->C >> 16) < as->A +#else + !((as->C >> 16) < pqe->Qe) +#endif + ) { +#ifndef SOFTWARE_CONVENTION + as->C -= pqe->Qe << 16; +#endif + 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); + *code = 0; + return D; + } else { + *code = 0; + return cx >> 7; + } + } else { +#ifdef SOFTWARE_CONVENTION + as->C -= (as->A) << 16; +#endif + /* 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); + *code = 0; + 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; + int code; + + 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, &code); + +#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 diff --git a/jbig2dec/jbig2_arith.h b/jbig2dec/jbig2_arith.h new file mode 100644 index 00000000..be9382b8 --- /dev/null +++ b/jbig2dec/jbig2_arith.h @@ -0,0 +1,39 @@ +/* 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 */ +bool jbig2_arith_decode(Jbig2ArithState *as, Jbig2ArithCx *pcx, int *code); + +/* 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 */ diff --git a/jbig2dec/jbig2_arith_iaid.c b/jbig2dec/jbig2_arith_iaid.c new file mode 100644 index 00000000..d2177a3e --- /dev/null +++ b/jbig2dec/jbig2_arith_iaid.c @@ -0,0 +1,106 @@ +/* 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 <stddef.h> +#include <string.h> /* memset() */ + +#ifdef VERBOSE +#include <stdio.h> /* 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; + int code = 0; + + /* A.3 (2) */ + for (i = 0; i < SBSYMCODELEN; i++) { + D = jbig2_arith_decode(as, &IAIDx[PREV], &code); + if (code) + 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); + } +} diff --git a/jbig2dec/jbig2_arith_iaid.h b/jbig2dec/jbig2_arith_iaid.h new file mode 100644 index 00000000..b0b3a652 --- /dev/null +++ b/jbig2dec/jbig2_arith_iaid.h @@ -0,0 +1,31 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_arith_int.c b/jbig2dec/jbig2_arith_int.c new file mode 100644 index 00000000..05ec1b85 --- /dev/null +++ b/jbig2dec/jbig2_arith_int.c @@ -0,0 +1,153 @@ +/* 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 <stddef.h> +#include <string.h> /* 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; + int code = 0; + + S = jbig2_arith_decode(as, &IAx[PREV], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode IAx S"); + PREV = (PREV << 1) | S; + + bit = jbig2_arith_decode(as, &IAx[PREV], &code); + if (code) + 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], &code); + if (code) + 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], &code); + if (code) + 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], &code); + if (code) + 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], &code); + if (code) + 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], &code); + if (code) + 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); +} diff --git a/jbig2dec/jbig2_arith_int.h b/jbig2dec/jbig2_arith_int.h new file mode 100644 index 00000000..8d65dc95 --- /dev/null +++ b/jbig2dec/jbig2_arith_int.h @@ -0,0 +1,31 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_generic.c b/jbig2dec/jbig2_generic.c new file mode 100644 index 00000000..6c28352d --- /dev/null +++ b/jbig2dec/jbig2_generic.c @@ -0,0 +1,1251 @@ +/* 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 <stddef.h> +#include <string.h> /* memcpy(), memset() */ + +#ifdef OUTPUT_PBM +#include <stdio.h> +#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. */ + +/* 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; + int code = 0; + + 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++) { + bool bit; + + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + 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; + bool bit; + int code = 0; + + 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"); + + /* this version is generic and easy to understand, but very slow */ + + for (y = 0; y < GBH; y++) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 9; + 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 + 1, y - 2) << 12; + CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 2) << 13; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 14; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[6], y + params->gbat[7]) << 15; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 unoptimized"); + jbig2_image_set_pixel(image, x, y, bit); + } + } + return 0; +} + +static int +jbig2_decode_generic_template1_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; + bool bit; + int code = 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"); + + /* this version is generic and easy to understand, but very slow */ + + for (y = 0; y < GBH; y++) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 2) << 9; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 10; + CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 2) << 11; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 12; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 unoptimized"); + jbig2_image_set_pixel(image, x, y, bit); + } + } + return 0; +} + +static int +jbig2_decode_generic_template1(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; + int code = 0; + +#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++) { + bool bit; + + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + 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; + bool bit; + int code = 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"); + + /* this version is generic and easy to understand, but very slow */ + + for (y = 0; y < GBH; y++) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 2) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 9; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 unoptimized"); + jbig2_image_set_pixel(image, x, y, bit); + } + } + + return 0; +} + +static int +jbig2_decode_generic_template2(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; + int code = 0; + +#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++) { + bool bit; + + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + 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; + int code; + +#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++) { + bool bit; + + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + 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; + bool bit; + int code = 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"); + + /* this version is generic and easy to understand, but very slow */ + + for (y = 0; y < GBH; y++) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y) << 0; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x + 0, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y - 1) << 9; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 unoptimized"); + jbig2_image_set_pixel(image, x, y, bit); + } + } + return 0; +} + +static void +copy_prev_row(Jbig2Image *image, int row) +{ + if (!row) { + /* no previous row */ + memset(image->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; + bool bit; + int LTP = 0; + int code = 0; + + 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++) { + LTP ^= jbig2_arith_decode(as, &GB_stats[0x9B25], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON1"); + if (!LTP) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = jbig2_image_get_pixel(image, x - 1, y); + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 9; + 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 + 1, y - 2) << 12; + CONTEXT |= jbig2_image_get_pixel(image, x, y - 2) << 13; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 14; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[6], y + params->gbat[7]) << 15; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template0 TPGDON2"); + jbig2_image_set_pixel(image, x, y, bit); + } + } else { + copy_prev_row(image, y); + } + } + + return 0; +} + +static int +jbig2_decode_generic_template1_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; + bool bit; + int LTP = 0; + int code = 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++) { + LTP ^= jbig2_arith_decode(as, &GB_stats[0x0795], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 TPGDON1"); + if (!LTP) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = jbig2_image_get_pixel(image, x - 1, y); + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 1) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x + 2, y - 2) << 9; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 10; + CONTEXT |= jbig2_image_get_pixel(image, x, y - 2) << 11; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 12; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template1 TPGDON2"); + jbig2_image_set_pixel(image, x, y, bit); + } + } else { + copy_prev_row(image, y); + } + } + + return 0; +} + +static int +jbig2_decode_generic_template2_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; + bool bit; + int LTP = 0; + int code = 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++) { + LTP ^= jbig2_arith_decode(as, &GB_stats[0xE5], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 TPGDON1"); + if (!LTP) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = jbig2_image_get_pixel(image, x - 1, y); + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 2) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x, y - 2) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 2) << 9; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template2 TPGDON2"); + jbig2_image_set_pixel(image, x, y, bit); + } + } else { + copy_prev_row(image, y); + } + } + + return 0; +} + +static int +jbig2_decode_generic_template3_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; + bool bit; + int LTP = 0; + int code = 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++) { + LTP ^= jbig2_arith_decode(as, &GB_stats[0x0195], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 TPGDON1"); + if (!LTP) { + for (x = 0; x < GBW; x++) { + if (params->USESKIP && jbig2_image_get_pixel(params->SKIP, x, y)) { + jbig2_image_set_pixel(image, x, y, 0); + continue; + } + CONTEXT = jbig2_image_get_pixel(image, x - 1, y); + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y) << 1; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y) << 2; + CONTEXT |= jbig2_image_get_pixel(image, x - 4, y) << 3; + CONTEXT |= jbig2_image_get_pixel(image, x + params->gbat[0], y + params->gbat[1]) << 4; + CONTEXT |= jbig2_image_get_pixel(image, x + 1, y - 1) << 5; + CONTEXT |= jbig2_image_get_pixel(image, x, y - 1) << 6; + CONTEXT |= jbig2_image_get_pixel(image, x - 1, y - 1) << 7; + CONTEXT |= jbig2_image_get_pixel(image, x - 2, y - 1) << 8; + CONTEXT |= jbig2_image_get_pixel(image, x - 3, y - 1) << 9; + bit = jbig2_arith_decode(as, &GB_stats[CONTEXT], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to decode arithmetic code when handling generic template3 TPGDON2"); + jbig2_image_set_pixel(image, x, y, bit); + } + } else { + copy_prev_row(image, y); + } + } + + return 0; +} + +static int +jbig2_decode_generic_region_TPGDON(Jbig2Ctx *ctx, + Jbig2Segment *segment, + const Jbig2GenericRegionParams *params, Jbig2ArithState *as, Jbig2Image *image, Jbig2ArithCx *GB_stats) +{ + switch (params->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 (%d << %d), aborting to prevent DOS", 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; +} diff --git a/jbig2dec/jbig2_generic.h b/jbig2dec/jbig2_generic.h new file mode 100644 index 00000000..39f4d3a2 --- /dev/null +++ b/jbig2dec/jbig2_generic.h @@ -0,0 +1,49 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_halftone.c b/jbig2dec/jbig2_halftone.c new file mode 100644 index 00000000..8a18fc70 --- /dev/null +++ b/jbig2dec/jbig2_halftone.c @@ -0,0 +1,653 @@ +/* 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 <string.h> /* 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; + + /* 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; + uint32_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 * (int32_t) params->HRY + ng * (int32_t) params->HRX) >> 8; + y = (params->HGY + mg * (int32_t) params->HRX - ng * (int32_t) 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"); +} diff --git a/jbig2dec/jbig2_halftone.h b/jbig2dec/jbig2_halftone.h new file mode 100644 index 00000000..22bf666c --- /dev/null +++ b/jbig2dec/jbig2_halftone.h @@ -0,0 +1,59 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_huffman.c b/jbig2dec/jbig2_huffman.c new file mode 100644 index 00000000..908ab59c --- /dev/null +++ b/jbig2dec/jbig2_huffman.c @@ -0,0 +1,2142 @@ +/* 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 <stdlib.h> +#include <string.h> + +#ifdef JBIG2_DEBUG +#include <stdio.h> +#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 && entry->u.RANGELOW == -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 + + /* 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 <stdio.h> + +/* 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; + 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: + for (i = 0; i < 5; i++) + jbig2_release_huffman_table(ctx, tables[i]); + jbig2_ctx_free(ctx); + + return success; +} + +#include <stdio.h> + +/* 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 diff --git a/jbig2dec/jbig2_huffman.h b/jbig2dec/jbig2_huffman.h new file mode 100644 index 00000000..39c50246 --- /dev/null +++ b/jbig2dec/jbig2_huffman.h @@ -0,0 +1,110 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_hufftab.h b/jbig2dec/jbig2_hufftab.h new file mode 100644 index 00000000..29a39db0 --- /dev/null +++ b/jbig2dec/jbig2_hufftab.h @@ -0,0 +1,315 @@ +/* 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 */ + +#ifndef _JBIG2_HUFFTAB_H +#define _JBIG2_HUFFTAB_H + +/* types are in jbig2_huffman.h, you must include that first */ + +#define JBIG2_COUNTOF(x) (sizeof((x)) / sizeof((x)[0])) + +/* Table B.1 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 */ +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 }; + +#undef JBIG2_COUNTOF + +#endif /* _JBIG2_HUFFTAB_H */ diff --git a/jbig2dec/jbig2_image.c b/jbig2dec/jbig2_image.c new file mode 100644 index 00000000..05a81bd7 --- /dev/null +++ b/jbig2dec/jbig2_image.c @@ -0,0 +1,425 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <string.h> /* memcpy() */ + +#include "jbig2.h" +#include "jbig2_priv.h" +#include "jbig2_image.h" + +#if !defined (INT32_MAX) +#define INT32_MAX 0x7fffffff +#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; +} + +/* composite one jbig2_image onto another + slow but general version */ +static int +jbig2_image_compose_unopt(Jbig2Ctx *ctx, Jbig2Image *dst, Jbig2Image *src, int x, int y, Jbig2ComposeOp op) +{ + uint32_t i, j; + uint32_t sw = src->width; + uint32_t sh = src->height; + uint32_t sx = 0; + uint32_t sy = 0; + + /* clip to the dst image boundaries */ + if (x < 0) { + sx += -x; + if (sw < (uint32_t) -x) + sw = 0; + else + sw -= -x; + x = 0; + } + if (y < 0) { + sy += -y; + if (sh < (uint32_t) -y) + sh = 0; + else + sh -= -y; + y = 0; + } + if ((uint32_t) x + sw >= dst->width) { + if (dst->width >= (uint32_t) x) + sw = dst->width - x; + else + sw = 0; + } + if ((uint32_t) y + sh >= dst->height) { + if (dst->height >= (uint32_t) y) + sh = dst->height - y; + else + sh = 0; + } + + switch (op) { + case JBIG2_COMPOSE_OR: + for (j = 0; j < sh; j++) { + for (i = 0; i < sw; i++) { + jbig2_image_set_pixel(dst, i + x, j + y, jbig2_image_get_pixel(src, i + sx, j + sy) | jbig2_image_get_pixel(dst, i + x, j + y)); + } + } + break; + case JBIG2_COMPOSE_AND: + for (j = 0; j < sh; j++) { + for (i = 0; i < sw; i++) { + jbig2_image_set_pixel(dst, i + x, j + y, jbig2_image_get_pixel(src, i + sx, j + sy) & jbig2_image_get_pixel(dst, i + x, j + y)); + } + } + break; + case JBIG2_COMPOSE_XOR: + for (j = 0; j < sh; j++) { + for (i = 0; i < sw; i++) { + jbig2_image_set_pixel(dst, i + x, j + y, jbig2_image_get_pixel(src, i + sx, j + sy) ^ jbig2_image_get_pixel(dst, i + x, j + y)); + } + } + break; + case JBIG2_COMPOSE_XNOR: + for (j = 0; j < sh; j++) { + for (i = 0; i < sw; i++) { + jbig2_image_set_pixel(dst, i + x, j + y, (jbig2_image_get_pixel(src, i + sx, j + sy) == jbig2_image_get_pixel(dst, i + x, j + y))); + } + } + break; + case JBIG2_COMPOSE_REPLACE: + for (j = 0; j < sh; j++) { + for (i = 0; i < sw; i++) { + jbig2_image_set_pixel(dst, i + x, j + y, jbig2_image_get_pixel(src, i + sx, j + sy)); + } + } + break; + } + + return 0; +} + +/* composite one jbig2_image onto another */ +int +jbig2_image_compose(Jbig2Ctx *ctx, Jbig2Image *dst, Jbig2Image *src, int x, int y, Jbig2ComposeOp op) +{ + uint32_t i, j; + uint32_t w, h; + uint32_t leftbyte, rightbyte; + uint32_t shift; + uint8_t *s, *ss; + uint8_t *d, *dd; + uint8_t mask, rightmask; + + if (src == NULL) + return 0; + + /* The optimized code for the OR operator below doesn't + handle the source image partially placed outside the + destination (above and/or to the left). The affected + intersection of the destination is computed correctly, + however the correct subset of the source image is not + chosen. Instead the upper left corner of the source image + is always used. + + In the unoptimized version that handles all operators + (including OR) the correct subset of the source image is + chosen. + + The workaround is to check whether the x/y coordinates to + the composition operator are negative and in this case use + the unoptimized implementation. + + TODO: Fix the optimized OR implementation if possible. */ + if (op != JBIG2_COMPOSE_OR || x < 0 || y < 0) { + /* hand off the the general routine */ + return jbig2_image_compose_unopt(ctx, dst, src, x, y, op); + } + + /* optimized code for the prevalent OR operator */ + + /* clip */ + w = src->width; + h = src->height; + ss = src->data; + + if (x < 0) { + if (w < (uint32_t) -x) + w = 0; + else + w += x; + x = 0; + } + if (y < 0) { + if (h < (uint32_t) -y) + h = 0; + else + h += y; + y = 0; + } + w = ((uint32_t) x + w < dst->width) ? w : ((dst->width >= (uint32_t) x) ? dst->width - (uint32_t) x : 0); + h = ((uint32_t) y + h < dst->height) ? h : ((dst->height >= (uint32_t) y) ? dst->height - (uint32_t) y : 0); +#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; + rightbyte = ((uint32_t) x + w - 1) >> 3; + shift = x & 7; + + /* general OR case */ + s = ss; + d = dd = dst->data + y * dst->stride + leftbyte; + if (d < dst->data || + leftbyte > dst->stride || + d - leftbyte + (size_t) h * dst->stride > dst->data + (size_t) dst->height * dst->stride || + s - leftbyte + (size_t) (h - 1) * src->stride + rightbyte > src->data + (size_t) src->height * src->stride) { + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "preventing heap overflow in jbig2_image_compose"); + } + if (leftbyte == rightbyte) { + mask = 0x100 - (0x100 >> w); + for (j = 0; j < h; j++) { + *d |= (*s & mask) >> shift; + d += dst->stride; + s += src->stride; + } + } else if (shift == 0) { + rightmask = (w & 7) ? 0x100 - (1 << (8 - (w & 7))) : 0xFF; + for (j = 0; j < h; j++) { + for (i = leftbyte; i < rightbyte; i++) + *d++ |= *s++; + *d |= *s & rightmask; + d = (dd += dst->stride); + s = (ss += src->stride); + } + } else { + bool overlap = (((w + 7) >> 3) < ((x + w + 7) >> 3) - (x >> 3)); + + mask = 0x100 - (1 << shift); + if (overlap) + rightmask = (0x100 - (0x100 >> ((x + w) & 7))) >> (8 - shift); + else + rightmask = 0x100 - (0x100 >> (w & 7)); + for (j = 0; j < h; j++) { + *d++ |= (*s & mask) >> shift; + for (i = leftbyte; i < rightbyte - 1; i++) { + *d |= ((*s++ & ~mask) << (8 - shift)); + *d++ |= ((*s & mask) >> shift); + } + if (overlap) + *d |= (*s & rightmask) << (8 - shift); + else + *d |= ((s[0] & ~mask) << (8 - shift)) | ((s[1] & rightmask) >> shift); + d = (dd += dst->stride); + s = (ss += src->stride); + } + } + + 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); +} diff --git a/jbig2dec/jbig2_image.h b/jbig2dec/jbig2_image.h new file mode 100644 index 00000000..5074bce3 --- /dev/null +++ b/jbig2dec/jbig2_image.h @@ -0,0 +1,42 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_image_pbm.c b/jbig2dec/jbig2_image_pbm.c new file mode 100644 index 00000000..41ea2bc2 --- /dev/null +++ b/jbig2dec/jbig2_image_pbm.c @@ -0,0 +1,162 @@ +/* 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 <stdio.h> +#include <ctype.h> + +#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; +} diff --git a/jbig2dec/jbig2_image_png.c b/jbig2dec/jbig2_image_png.c new file mode 100644 index 00000000..134b7f12 --- /dev/null +++ b/jbig2dec/jbig2_image_png.c @@ -0,0 +1,152 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <png.h> + +#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 <pngstruct.h> +#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); +} diff --git a/jbig2dec/jbig2_image_rw.h b/jbig2dec/jbig2_image_rw.h new file mode 100644 index 00000000..63b30097 --- /dev/null +++ b/jbig2dec/jbig2_image_rw.h @@ -0,0 +1,37 @@ +/* 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 <stdio.h> + +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 */ diff --git a/jbig2dec/jbig2_mmr.c b/jbig2dec/jbig2_mmr.c new file mode 100644 index 00000000..c3229df7 --- /dev/null +++ b/jbig2dec/jbig2_mmr.c @@ -0,0 +1,1149 @@ +/* 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 <stddef.h> +#include <stdio.h> +#include <string.h> + +#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++; + } +} + +/* +<raph> the first 2^(initialbits) entries map bit patterns to decodes +<raph> let's say initial_bits is 8 for the sake of example +<raph> and that the code is 1001 +<raph> that means that entries 0x90 .. 0x9f have the entry { val, 4 } +<raph> because those are all the bytes that start with the code +<raph> and the 4 is the length of the code +... if (n_bits > initial_bits) ... +<raph> anyway, in that case, it basically points to a mini table +<raph> the n_bits is the maximum length of all codes beginning with that byte +<raph> so 2^(n_bits - initial_bits) is the size of the mini-table +<raph> 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, b; + + if (line == NULL) + return w; + + if (x == MINUS1) { + a = 0; + x = 0; + } else if (x < w) { + a = getbit(line, x); + x++; + } else { + return x; + } + + while (x < w) { + b = getbit(line, x); + if (a != b) + break; + x++; + } + + 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; +} diff --git a/jbig2dec/jbig2_mmr.h b/jbig2dec/jbig2_mmr.h new file mode 100644 index 00000000..f6317bf3 --- /dev/null +++ b/jbig2dec/jbig2_mmr.h @@ -0,0 +1,29 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_page.c b/jbig2dec/jbig2_page.c new file mode 100644 index 00000000..c07842b7 --- /dev/null +++ b/jbig2dec/jbig2_page.c @@ -0,0 +1,352 @@ +/* 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 <stdlib.h> + +#ifdef OUTPUT_PBM +#include <stdio.h> +#endif + +#include "jbig2.h" +#include "jbig2_priv.h" +#include "jbig2_image.h" +#include "jbig2_page.h" +#include "jbig2_segment.h" + +/* 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; + + /* 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 = 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"); +} diff --git a/jbig2dec/jbig2_page.h b/jbig2dec/jbig2_page.h new file mode 100644 index 00000000..a276285c --- /dev/null +++ b/jbig2dec/jbig2_page.h @@ -0,0 +1,53 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_priv.h b/jbig2dec/jbig2_priv.h new file mode 100644 index 00000000..0fee2b99 --- /dev/null +++ b/jbig2dec/jbig2_priv.h @@ -0,0 +1,136 @@ +/* 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, either uncomment the following, or arrange to + * predefine MEMENTO whilst building. */ +/* #define MEMENTO */ + +/* 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, ...); + +/* 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); + +#endif /* _JBIG2_PRIV_H */ diff --git a/jbig2dec/jbig2_refinement.c b/jbig2dec/jbig2_refinement.c new file mode 100644 index 00000000..2c3ba29e --- /dev/null +++ b/jbig2dec/jbig2_refinement.c @@ -0,0 +1,552 @@ +/* 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 <stddef.h> +#include <string.h> /* memcpy(), memset() */ + +#include <stdio.h> + +#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; + bool bit; + int code = 0; + + 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], &code); + if (code) + 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; + bool bit; + int code = 0; + + 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], &code); + if (code) + 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; + int code = 0; + + 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++) { + bool bit; + + bit = jbig2_arith_decode(as, &GR_stats[CONTEXT], &code); + if (code) + 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, bit, LTP = 0; + uint32_t start_context = (params->GRTEMPLATE ? 0x40 : 0x100); + ContextBuilder mkctx = (params->GRTEMPLATE ? mkctx1 : mkctx0); + int code = 0; + + 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++) { + LTP ^= jbig2_arith_decode(as, &GR_stats[start_context], &code); + if (code) + return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to decode arithmetic code when handling refinement TPGRON1"); + if (!LTP) { + for (x = 0; x < GRW; x++) { + bit = jbig2_arith_decode(as, &GR_stats[mkctx(params, image, x, y)], &code); + if (code) + 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) { + bit = jbig2_arith_decode(as, &GR_stats[mkctx(params, image, x, y)], &code); + if (code) + 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; +} diff --git a/jbig2dec/jbig2_refinement.h b/jbig2dec/jbig2_refinement.h new file mode 100644 index 00000000..49280c62 --- /dev/null +++ b/jbig2dec/jbig2_refinement.h @@ -0,0 +1,43 @@ +/* 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 */ + diff --git a/jbig2dec/jbig2_segment.c b/jbig2dec/jbig2_segment.c new file mode 100644 index 00000000..9c47f630 --- /dev/null +++ b/jbig2dec/jbig2_segment.c @@ -0,0 +1,382 @@ +/* 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 <stddef.h> /* 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", -1); + 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=%d", segment->number, segment->flags, segment->flags & 63, 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; +} diff --git a/jbig2dec/jbig2_segment.h b/jbig2dec/jbig2_segment.h new file mode 100644 index 00000000..8a17fdaf --- /dev/null +++ b/jbig2dec/jbig2_segment.h @@ -0,0 +1,54 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_symbol_dict.c b/jbig2dec/jbig2_symbol_dict.c new file mode 100644 index 00000000..93ea09d5 --- /dev/null +++ b/jbig2dec/jbig2_symbol_dict.c @@ -0,0 +1,1086 @@ +/* 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 <stddef.h> +#include <string.h> /* memset() */ + +#if defined(OUTPUT_PBM) || defined(DUMP_SYMDICT) +#include <stdio.h> +#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/%d)", image->height * stride, + 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 (%d bytes)", image->width, image->height, NSYMSDECODED - HCFIRSTSYM, 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 (%d/%d)", BMSIZE, size - jbig2_huffman_offset(hs)); + goto cleanup; + } + + jbig2_error(ctx, JBIG2_SEVERITY_DEBUG, segment->number, + "reading %dx%d collective bitmap for %d symbols (%d bytes)", image->width, image->height, NSYMSDECODED - HCFIRSTSYM, 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, 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"); +} diff --git a/jbig2dec/jbig2_symbol_dict.h b/jbig2dec/jbig2_symbol_dict.h new file mode 100644 index 00000000..db1aaf75 --- /dev/null +++ b/jbig2dec/jbig2_symbol_dict.h @@ -0,0 +1,55 @@ +/* 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 */ diff --git a/jbig2dec/jbig2_text.c b/jbig2dec/jbig2_text.c new file mode 100644 index 00000000..7fb9eb08 --- /dev/null +++ b/jbig2dec/jbig2_text.c @@ -0,0 +1,1034 @@ +/* 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 <stddef.h> +#include <string.h> /* 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) { + code = jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to construct symbol ID huffman table"); + jbig2_huffman_free(ctx, hs); + return code; + } + } + + /* 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"); +} diff --git a/jbig2dec/jbig2_text.h b/jbig2dec/jbig2_text.h new file mode 100644 index 00000000..2c966b03 --- /dev/null +++ b/jbig2dec/jbig2_text.h @@ -0,0 +1,81 @@ +/* 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 */ diff --git a/jbig2dec/jbig2dec.1 b/jbig2dec/jbig2dec.1 new file mode 100644 index 00000000..3933d381 --- /dev/null +++ b/jbig2dec/jbig2dec.1 @@ -0,0 +1,74 @@ +.TH jbig2dec 1 "2019 September 17" "Version 0.17" "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 <gs-devel@ghostscript.com>. + +.SH AUTHOR +This manpage was initially written by Sebastian Rasmussen +<sebras@gmail.com> for jbig2dec and the Debian Project. diff --git a/jbig2dec/jbig2dec.c b/jbig2dec/jbig2dec.c new file mode 100644 index 00000000..5e3f5e5a --- /dev/null +++ b/jbig2dec/jbig2dec.c @@ -0,0 +1,598 @@ +/* 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 <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +#ifdef HAVE_GETOPT_H +# include <getopt.h> +#else +# include "getopt.h" +#endif + +#include "os_types.h" +#include "sha1.h" + +#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] <file.jbig2>\n" + " or jbig2dec [options] <global_stream> <page_stream>\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 <file> send decoded output to <file>\n" + " Defaults to the the input with a different\n" + " extension. Pass '-' for stdout.\n" + " -t <type> 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.output_filename) + free(params.output_filename); + if (params.hash) + hash_free(¶ms); + + return result; +} diff --git a/jbig2dec/jbig2dec.pc.in b/jbig2dec/jbig2dec.pc.in new file mode 100644 index 00000000..74ebb4c6 --- /dev/null +++ b/jbig2dec/jbig2dec.pc.in @@ -0,0 +1,11 @@ +prefix=@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} diff --git a/jbig2dec/memcmp.c b/jbig2dec/memcmp.c new file mode 100644 index 00000000..fce52e5c --- /dev/null +++ b/jbig2dec/memcmp.c @@ -0,0 +1,54 @@ +/* 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 <stddef.h> + +/* 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; +} diff --git a/jbig2dec/memento.c b/jbig2dec/memento.c new file mode 100644 index 00000000..8ebe0e57 --- /dev/null +++ b/jbig2dec/memento.c @@ -0,0 +1,3036 @@ +/* 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 <stdio.h> +#endif +#ifndef _MSC_VER +#include <stdint.h> +#include <limits.h> +#endif + +#include <stdlib.h> +#include <stdarg.h> + +#ifdef __ANDROID__ +#define MEMENTO_ANDROID +#include <stdio.h> +#endif + +/* Hacks to portably print large sizes */ +#ifdef _MSC_VER +#define FMTZ "%llu" +#define FMTZ_CAST _int64 +#else +#define FMTZ "%zu" +#define FMTZ_CAST size_t +#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 <android/log.h> + +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 <windows.h> + +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 +}; + +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 <pthread.h> +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 + +/* Libbacktrace gubbins - relies on us having libdl to load the .so */ +#ifdef HAVE_LIBDL +#include <dlfcn.h> + +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 void (*print_stack_value)(void *address); +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 = 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) +{ + 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: + 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, " [0x%p]\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; + if (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 <Windows.h> + +/* 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 <unwind.h> +#include <dlfcn.h> + +/* 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 : "<unknown>"; + char *demangled = __cxa_demangle(sym, NULL, 0, &status); + int offset = stack[i] - info.dli_saddr; + fprintf(stderr, " [%p]%s(+0x%x)\n", stack[i], demangled && status == 0 ? demangled : sym, offset); + free(demangled); + } + else + { + fprintf(stderr, " [%p]\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 void showBlock(Memento_BlkHeader *b, int space) +{ + fprintf(stderr, "0x%p:(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)"); +} + +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]++; + counts[1]+= b->rawsize; + return 0; +} + +static void doNestedDisplay(Memento_BlkHeader *b, + int depth) +{ + /* Try and avoid recursion if we can help it */ + do { + blockDisplay(b, depth); + if (b->sibling) { + if (b->child) + doNestedDisplay(b->child, depth+1); + b = b->sibling; + } else { + b = b->child; + depth++; + } + } 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, "0x%p:(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) + { + if ((blk->flags & Memento_Flag_KnownLeak) == 0) + return 1; + blk = 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_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 <unistd.h> +#include <sys/wait.h> +#include <time.h> +#ifdef MEMENTO_STACKTRACE_METHOD +#if MEMENTO_STACKTRACE_METHOD == 1 +#include <signal.h> +#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 <signal.h> + +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"); + 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); + 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); + 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; + } + 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 0x%p) 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 ? "+" : "")); + } + } + 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; + } + 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; + + 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 0x%p is in %sallocated block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, "\n"); + MEMENTO_UNLOCK(); + return data.blk->sequence; + } + data.blk = NULL; + data.flags = 0; + Memento_appBlocks(&memento.free, Memento_containsAddr, &data); + if (data.blk != NULL) { + fprintf(stderr, "Address 0x%p 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 data.blk->sequence; + } + 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 0x%p (in %sallocated block ", + data.addr, + (data.flags == 1 ? "" : (data.flags == 2 ? + "preguard of " : "postguard of "))); + showBlock(data.blk, ' '); + fprintf(stderr, ") is freed\n"); + data.blk->flags |= Memento_Flag_BreakOnFree; + 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 0x%p 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 0x%p 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 0x%p (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"); + data.blk->flags |= Memento_Flag_BreakOnFree | Memento_Flag_BreakOnRealloc; + 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 0x%p 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 0x%p 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--; +} + +#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) +{ +} + +#endif diff --git a/jbig2dec/memento.h b/jbig2dec/memento.h new file mode 100644 index 00000000..d8cf5b34 --- /dev/null +++ b/jbig2dec/memento.h @@ -0,0 +1,305 @@ +/* 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 breakpoints on Memento_inited and + * 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 hit Memento_inited + * 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. + */ + +#ifndef MEMENTO_H + +#include <stdlib.h> + +#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); + +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) + +#endif /* MEMENTO */ + +#endif /* MEMENTO_H */ diff --git a/jbig2dec/msvc.mak b/jbig2dec/msvc.mak new file mode 100644 index 00000000..94bc548f --- /dev/null +++ b/jbig2dec/msvc.mak @@ -0,0 +1,130 @@ +# 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_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_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 + diff --git a/jbig2dec/os_types.h b/jbig2dec/os_types.h new file mode 100644 index 00000000..3b9c9531 --- /dev/null +++ b/jbig2dec/os_types.h @@ -0,0 +1,47 @@ +/* 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 <sys/types.h> +#elif defined (STD_INT_USE_INTTYPES_H) +# include <inttypes.h> +#elif defined (STD_INT_USE_SYS_INTTYPES_H) +# include <sys/inttypes.h> +#elif defined (STD_INT_USE_SYS_INT_TYPES_H) +# include <sys/int_types.h> +#else +# include <stdint.h> +#endif + +#endif /* _JBIG2_OS_TYPES_H */ diff --git a/jbig2dec/pbm2png.c b/jbig2dec/pbm2png.c new file mode 100644 index 00000000..e697328a --- /dev/null +++ b/jbig2dec/pbm2png.c @@ -0,0 +1,67 @@ +/* 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 <stdint.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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 <in.pbm> <out.png>\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); +} diff --git a/jbig2dec/sha1.c b/jbig2dec/sha1.c new file mode 100644 index 00000000..1c691cc0 --- /dev/null +++ b/jbig2dec/sha1.c @@ -0,0 +1,376 @@ +/* +SHA-1 in C +By Steve Reid <sreid@sea-to-sky.net> +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown <jbrown@burgoyne.com> +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 <process.h> 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 <sreid@sea-to-sky.net> +Still 100% public domain + +1- Removed #include <process.h> 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 <Saul.Kravitz@celera.com> +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 07/2002 +By Ralph Giles <giles@ghostscript.com> +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 <stdio.h> +#include <string.h> + +#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 <sreid@sea-to-sky.net>"); + puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>"); /* 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 */ diff --git a/jbig2dec/sha1.h b/jbig2dec/sha1.h new file mode 100644 index 00000000..afeeb42c --- /dev/null +++ b/jbig2dec/sha1.h @@ -0,0 +1,28 @@ +/* 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 */ diff --git a/jbig2dec/snprintf.c b/jbig2dec/snprintf.c new file mode 100644 index 00000000..025396dd --- /dev/null +++ b/jbig2dec/snprintf.c @@ -0,0 +1,163 @@ +/* + * 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 <param.h> +#else +#include <sys/param.h> +#endif +#include <sys/types.h> +#include <sys/mman.h> +#include <signal.h> +#include <stdio.h> +#if __STDC__ +#include <stdarg.h> +#include <stdlib.h> +#else +#include <varargs.h> +#endif +#include <setjmp.h> +#include <unistd.h> +#include <string.h> + +#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); +} diff --git a/jbig2dec/test_jbig2dec.py b/jbig2dec/test_jbig2dec.py new file mode 100755 index 00000000..a8414383 --- /dev/null +++ b/jbig2dec/test_jbig2dec.py @@ -0,0 +1,187 @@ +#! /usr/bin/env python + +# this is the test script for jbig2dec + +import os, re +import sys, time + +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_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 = ( ('../ubc/042_1.jb2', + "673e1ee5c55ab241b171e476ba1168a42733ddaa", + known_042_DECODED), + ('../ubc/042_2.jb2', + "9aa2804e2d220952035c16fb3c907547884067c5", + known_042_DECODED), + ('../ubc/042_3.jb2', + "9663a5f35727f13e61a0a2f0a64207b1f79e7d67", + known_042_DECODED), + ('../ubc/042_4.jb2', + "014df658c8b99b600c2ceac3f1d53c7cc2b4917c", + known_042_DECODED), + ('../ubc/042_5.jb2', + "264720a6ccbbf72aa6a2cfb6343f43b8e6f2da4b", + known_042_DECODED), + ('../ubc/042_6.jb2', + "96f7dc9df4a1b305f9ac082dd136f85ef5b108fe", + known_042_DECODED), + ('../ubc/042_7.jb2', + "5526371ba9dc2b8743f20ae3e05a7e60b3dcba76", + known_042_DECODED), + ('../ubc/042_8.jb2', + "4bf0c87dfaf40d67c36f2a083579eeda26d54641", + known_042_DECODED), + ('../ubc/042_9.jb2', + "53e630e7fe2fe6e1d6164758e15fc93382e07f55", + known_042_DECODED), + ('../ubc/042_10.jb2', + "5ca1364367e25cb8f642e9dc677a94d5cfed0c8b", + known_042_DECODED), + ('../ubc/042_11.jb2', + "bc194caf022bc5345fc41259e05cea3c08245216", + known_042_DECODED), + ('../ubc/042_12.jb2', + "f354df8eb4849bc707f088739e322d1fe3a14ef3", + known_042_DECODED), + ('../ubc/042_13.jb2', + "7d428bd542f58591b254d9827f554b0552c950a7", + known_NOTHING_DECODED), + ('../ubc/042_14.jb2', + "c40fe3a02acb6359baf9b40fc9c49bc0800be589", + known_NOTHING_DECODED), + ('../ubc/042_15.jb2', + "a9e39fc1ecb178aec9f05039514d75ea3246246c", + known_042_DECODED), + ('../ubc/042_16.jb2', + "4008bbca43670f3c90eaee26516293ba95baaf3d", + known_042_DECODED), + ('../ubc/042_17.jb2', + "0ff95637b64c57d659a41c582da03e25321551fb", + known_042_DECODED), + ('../ubc/042_18.jb2', + "87381d044f00c4329200e44decbe91bebfa31595", + known_042_DECODED), + ('../ubc/042_19.jb2', + "387d95a140b456d4742622c788cf5b51cebbf438", + known_042_DECODED), + ('../ubc/042_20.jb2', + "85c19e9ec42b8ddd6b860a1bebea1c67610e7a59", + known_042_DECODED), + ('../ubc/042_21.jb2', + "ab535c7d7a61a7b9dc53d546e7419ca78ac7f447", + known_042_DECODED), + ('../ubc/042_22.jb2', + "a9e2b365be63716dbde74b0661c3c6efd2a6844d", + known_042_DECODED), + ('../ubc/042_23.jb2', + "8ffa40a05e93e10982b38a2233a8da58c1b5c343", + known_042_DECODED), + ('../ubc/042_24.jb2', + "2553fe65111c58f6412de51d8cdc71651e778ccf", + known_042_DECODED), + ('../ubc/042_25.jb2', + "52de4a3b86252d896a8d783ba71dd0699333dd69", + known_042_DECODED), + ('../ubc/amb_1.jb2', + "d6d6d1c981dc37a09108c1e3ed990aa5b345fa6a", + known_amb_DECODED), + ('../ubc/amb_2.jb2', + "9af6616a89eb03f8934de72626e301a716366c3c", + known_amb_DECODED) + ) + + 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''' + # 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) |