summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Deutschmann <whissi@gentoo.org>2019-10-15 12:24:12 +0200
committerThomas Deutschmann <whissi@gentoo.org>2020-08-13 11:26:55 +0200
commite088156d5b620e5e639580dacf85c6dc13823c74 (patch)
tree57f5c025e203279944da512166c20bc0521d8ccd /jbig2dec
downloadghostscript-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')
-rw-r--r--jbig2dec/CHANGES115
-rw-r--r--jbig2dec/COPYING661
-rw-r--r--jbig2dec/LICENSE23
-rw-r--r--jbig2dec/Makefile.am57
-rw-r--r--jbig2dec/Makefile.unix44
-rw-r--r--jbig2dec/README32
-rw-r--r--jbig2dec/annex-h.jbig2bin0 -> 860 bytes
-rwxr-xr-xjbig2dec/autogen.sh180
-rw-r--r--jbig2dec/config_win32.h58
-rw-r--r--jbig2dec/configure.ac.in150
-rw-r--r--jbig2dec/getopt.c947
-rw-r--r--jbig2dec/getopt.h173
-rw-r--r--jbig2dec/getopt1.c182
-rw-r--r--jbig2dec/jbig2.c534
-rw-r--r--jbig2dec/jbig2.h111
-rw-r--r--jbig2dec/jbig2_arith.c411
-rw-r--r--jbig2dec/jbig2_arith.h39
-rw-r--r--jbig2dec/jbig2_arith_iaid.c106
-rw-r--r--jbig2dec/jbig2_arith_iaid.h31
-rw-r--r--jbig2dec/jbig2_arith_int.c153
-rw-r--r--jbig2dec/jbig2_arith_int.h31
-rw-r--r--jbig2dec/jbig2_generic.c1251
-rw-r--r--jbig2dec/jbig2_generic.h49
-rw-r--r--jbig2dec/jbig2_halftone.c653
-rw-r--r--jbig2dec/jbig2_halftone.h59
-rw-r--r--jbig2dec/jbig2_huffman.c2142
-rw-r--r--jbig2dec/jbig2_huffman.h110
-rw-r--r--jbig2dec/jbig2_hufftab.h315
-rw-r--r--jbig2dec/jbig2_image.c425
-rw-r--r--jbig2dec/jbig2_image.h42
-rw-r--r--jbig2dec/jbig2_image_pbm.c162
-rw-r--r--jbig2dec/jbig2_image_png.c152
-rw-r--r--jbig2dec/jbig2_image_rw.h37
-rw-r--r--jbig2dec/jbig2_mmr.c1149
-rw-r--r--jbig2dec/jbig2_mmr.h29
-rw-r--r--jbig2dec/jbig2_page.c352
-rw-r--r--jbig2dec/jbig2_page.h53
-rw-r--r--jbig2dec/jbig2_priv.h136
-rw-r--r--jbig2dec/jbig2_refinement.c552
-rw-r--r--jbig2dec/jbig2_refinement.h43
-rw-r--r--jbig2dec/jbig2_segment.c382
-rw-r--r--jbig2dec/jbig2_segment.h54
-rw-r--r--jbig2dec/jbig2_symbol_dict.c1086
-rw-r--r--jbig2dec/jbig2_symbol_dict.h55
-rw-r--r--jbig2dec/jbig2_text.c1034
-rw-r--r--jbig2dec/jbig2_text.h81
-rw-r--r--jbig2dec/jbig2dec.174
-rw-r--r--jbig2dec/jbig2dec.c598
-rw-r--r--jbig2dec/jbig2dec.pc.in11
-rw-r--r--jbig2dec/memcmp.c54
-rw-r--r--jbig2dec/memento.c3036
-rw-r--r--jbig2dec/memento.h305
-rw-r--r--jbig2dec/msvc.mak130
-rw-r--r--jbig2dec/os_types.h47
-rw-r--r--jbig2dec/pbm2png.c67
-rw-r--r--jbig2dec/sha1.c376
-rw-r--r--jbig2dec/sha1.h28
-rw-r--r--jbig2dec/snprintf.c163
-rwxr-xr-xjbig2dec/test_jbig2dec.py187
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
new file mode 100644
index 00000000..bf2faf85
--- /dev/null
+++ b/jbig2dec/annex-h.jbig2
Binary files differ
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, &params, 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, &params, 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, &params, 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(&region_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, &params, 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, &params, 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, &region_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(&params, 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, &params, 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(&region_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(&params, 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, &params,
+ (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, &params);
+
+ if (params.hash)
+ hash_init(&params);
+
+ 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, &params);
+ 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, &params);
+ 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(&params, 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(&params, out, image);
+ if (params.hash)
+ hash_image(&params, image);
+ jbig2_release_page(ctx, image);
+ }
+
+ if (out != stdout)
+ fclose(out);
+ if (params.hash)
+ write_document_hash(&params);
+ }
+
+
+ } /* end params.mode switch */
+
+ /* fin */
+ result = 0;
+
+cleanup:
+ flush_errors(&params);
+ jbig2_ctx_free(ctx);
+ if (params.output_filename)
+ free(params.output_filename);
+ if (params.hash)
+ hash_free(&params);
+
+ 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)