Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Factory:Rebuild
libgxps
libgxps-0.3.2+5.obscpio
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File libgxps-0.3.2+5.obscpio of Package libgxps
07070100000000000081A40000000000000000000000016447D41100000033000000000000000000000000000000000000001F00000000libgxps-0.3.2+5/.gitattributes.gitattributes export-ignore regtest export-ignore 07070100000001000081A40000000000000000000000016447D4110000004C000000000000000000000000000000000000001800000000libgxps-0.3.2+5/AUTHORSCarlos Garcia Campos <carlosgc@gnome.org> Jason Crain <jason@aquaticape.us> 07070100000002000081A40000000000000000000000016447D411000067A2000000000000000000000000000000000000001800000000libgxps-0.3.2+5/COPYING GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. <one line to give the library's name and a brief idea of what it does.> Copyright (C) <year> <name of author> This 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. This 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 this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. <signature of Ty Coon>, 1 April 1990 Ty Coon, President of Vice That's all there is to it! 07070100000003000081A40000000000000000000000016447D41100000041000000000000000000000000000000000000001C00000000libgxps-0.3.2+5/MAINTAINERSCarlos Garcia Campos E-mail: carlosgc@gnome.org Userid: carlosgc 07070100000004000081A40000000000000000000000016447D4110000208F000000000000000000000000000000000000001500000000libgxps-0.3.2+5/NEWSlibgxps 0.3.2 (16 February 2021) ================================ New features: - Add support to wdp images on Windows using WIC API (Vittorio Vaselli) Bug fixes: - Fix a crash in tools when the surface is NULL on conversion end (#3, Qiuhao Li) Build: - Improve dependency check on Windows (Chun-wei Fan) - Don't assume that cross builds can't generate gir (maxice8) libgxps 0.3.1 (5 January 2019) ============================== Bug fixes: - Fix font scaling when converting xps to pdf (#1, Ignazio Pillai) - Handle errors returned by archive_read_data in GXPSArchive (Carlos Garcia Campos) - Ensure gxps_archive_read_entry() fills the GError in case of failure (Carlos Garcia Campos) - Make the pdf generated by xpstopdf to be 96 dpi (Ignacio Casal Quinteiro) - Fix OUTPUT FILE description in man pages (Jason Crain) - Clear the GError before trying to load an image again in gxps_images_get_image() (Carlos Garcia Campos) - Fix integer overflow in png decoder (Carlos Garcia Campos) libgxps 0.3.0 (8 August 2017) ================================ New features: - Add initial support for resource dictionaries (#777731, Ignacio Casal Quinteiro, Jason Crain) - Support OpenXPS fixed representation schema (#768117, Jason Crain) Bug fixes: - Fix a crash when extracting a malformed file (#785479, Marek Kasik) Build: - Switch to meson build system (#783642, Ignacio Casal Quinteiro) libgxps 0.2.5 (25 February 2017) ================================ Bug fixes: - Fix handling of internal documents paths to ensure we always use "/" instead of the system path separator (#777894, Ignacio Casal Quinteiro) - Improve performance when querying archive entries by using a hash set instead of a GList (#777272, Paolo Borelli) - Improve performance when querying documents in archive by using a GPtrArray instead of a GList (#777269, Paolo Borelli) - Fix a user after free in tools (Paolo Borelli) - Fix several memory leaks (Paolo Borelli) - Fix potential NULL pointer dereference found by coverity (#776323, Philip Withnall) - Add a missing break in switch statement found by coverity (#776323, Philip Withnall) - Fix uninitialised variables found by coverity (#776323, Philip Withnall) - Fix several compile warnings (Ignacio Casal Quinteiro) - Remove wrong return from print_token() that is void (#772296, Ernestas Kulik) - Fix GObject introspection build (#775637, Jason Crain) Build: - Add support for building with Visual studio (#771057, Ignacio Casal Quinteiro) libgxps 0.2.4 (20 June 2016) ============================ Bug fixes: - Fix the build with GCC >= 6 (Michael Catanzaro) - Fix non-literal format string warning for clang (#760177, Ting-Wei Lan) Tools: - Correctly show translated messages for command line tools on some locales (#760437, Ting-Wei Lan) libgxps 0.2.3.2 (4 September 2015) ================================== This is another follow up release to fix the build in some cases due to libm undefined symbols. libgxps 0.2.3.1 (14 August 2015) ================================ This is a follow up release to fix the build in some cases due to libpng undefined symbols. libgxps 0.2.3 (13 August 2015) ============================== API changes: - Add API to get Core Propoerties of a XPS file (Carlos Garcia Campos) New features: - Support ImageBrush Viewbox and rotation/shearing matrices (#751357, Jason Crain) - Add support for JPEG images with GRAYSCALE color space (#744120, Carlos Garcia Campos) - Add support for interleaved zip archives (Carlos Garcia Campos) Bug fixes: - Fix transformation for VisualBrush elements (#742630, Jason Crain) - Fix parsing of dash array with negative values (#715023, Lukas Lueg, Carlos Garcia Campos) - Make gxps_dash_array_parse fail for an odd number of dashes (#715023, Lukas Lueg, Carlos Garcia Campos) - Fix parsing of dash array when it has trailing or leading whitespaces (#715023, Lukas Lueg) - Fix the build with libpng16 (Petr Gajdos) - Fix parsing of real numbers containing e[+-]number (Carlos Garcia Campos) - Fix a crash in documents containing nested FixedDocument (#685741, Carlos Garcia Campos) libgxps 0.2.2 (19 March 2012) ============================= Bug fixes: - Fix COPYING file that used lgl2 instead of lgpl2.1 (#671271, Carlos Garcia Campos) - Fix the build with --enable-gtk-doc (#671274, Michael Biebl) - Fix several memory leaks when parsing fails (#668937, #668940, #668941, #668969, #668968, #668966, #668965, Jason Crain) Tools: - Set image parameters after jpeg_set_defaults(), so the resolution does not get overwritten by the defaults (Adrian Johnson) Documentation: - Ignore private headers when generating API doc (Carlos Garcia Campos) - Add index of new symbols in 0.2.1 (Carlos Garcia Campos) - Add Since: tag to GXPS_VERSION_STRING api docs (Carlos Garcia Campos) libgxps 0.2.1 (21 January 2012) =============================== API changes: - Add GXPS_VERSION_STRING macro with the library version as a string (Carlos Garcia Campos) New features: - Support OpacityMask for Path elements (#663477, Jason Crain) Bug fixes: - Fix MAJOR/MINOR/MICRO version macros (Carlos Garcia Campos) - Fix FSF address in COPYING file (Carlos Garcia Campos) - Fix building with libpng15 (#664666, Alexandre Rostovtsev) - Fix tools build by linking to libm (#664439, Dominique Leuenberger) libgxps 0.2.0 (19 November 2011) ================================ API changes: - This release contains a small API break. Even though all of the XPS documents we have for testing use integer numbers for the page size, the XPS Specification says that page size is a double value. Functions gxps_page_get_size() and gxps_document_get_page_size() now return page width and page height as gdouble values instead of guint. (Carlos Garcia Campos) New features: - Add support for PolyQuadraticBezierSegment figures (Carlos Garcia Campos) - Add support for Smooth Cubic Bézier curve (Carlos Garcia Campos) - Initial implementation of ICC based colors. Only CMYK colors are supported for now (Carlos Garcia Campos) - Support Opacity for SolidColorBrush elements (#663472, Jason Crain) - Add support for scRGB color syntax (Carlos Garcia Campos) - Implement Quadratic Bézier curve (Carlos Garcia Campos) - Support OpacityMask for Glyphs elements (#663476, Jason Crain) - Support OpacityMask for Canvas elements (#663475, Jason Crain) - Support opacity for ImageBrush elements (#663471, Jason Crain) - Support opacity for Glyphs elements (#662654, Jason Crain) - Support opacity for Canvas elements (#662653, Jason Crain) - Support opacity for Path elements (#662652, Jason Crain) - Support opacity for LinearGradientBrush and RadialGradientBrush elements (#662655, Jason Crain) Bug fixes: - Fix parser error messages for invalid content (#663899, Jason Crain) Tools: - Add xpstojpeg tool for converting XPS documents to JPEG (Carlos Garcia Campos) - Add xpstosvg tool for converting XPS documents to SVG (Carlos Garcia Campos) - Add xpstops tool for converting XPS documents to PostScript (Carlos Garcia Campos) - Add xpstopdf tool for converting XPS documents to PDF (Carlos Garcia Campos) - Add xpstopng tool for converting XPS documents to PNG (Carlos Garcia Campos) Documentation: - Add man pages for tools (Carlos Garcia Campos) - Clarify that page sizes returned by gxps_document_get_page_size() might be different than actual page sizes (Carlos Garcia Campos) - Add libgxps.types file so that object hierarchy is included in generated HTML doc (Carlos Garcia Campos) - Add documentation for return value of gxps_document_get_page() (Carlos Garcia Campos) libgxps 0.1.0 (12 October 2011) =============================== This is the first public release of libgxps. 07070100000005000081A40000000000000000000000016447D41100000163000000000000000000000000000000000000001700000000libgxps-0.3.2+5/READMEWhat is libgxps =============== libgxps is a GObject based library for handling and rendering XPS documents. libgxps 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. 07070100000006000081A40000000000000000000000016447D41100000AA4000000000000000000000000000000000000001A00000000libgxps-0.3.2+5/RELEASINGHere are the steps to follow to create a new libgxps release: 1) Ensure that there are no local, uncommitted modifications. It's probably good enough if "git diff HEAD" doesn't output anything and your "master" and "origin" branchs are at the current revision. 2) Fill out an entry in the NEWS file Sift through the logs since the last release. This is most easily done with a comand such as: git log --stat X.Y.Z.. where X.Y.Z is the previous release version. Summarize major changes briefly in a style similar to other entries in NEWS. Take special care to note any additions in the API. These should be easy to find by noting modifications to .h files in the log command above. And more specifically, the following command will show each patch that has changed a public header file since the given version: find libgxps -name 'gxps*.h' ! -name '*-private.h' \ ! -name 'gxps-archive.h' ! -name 'gxps-debug.h' \ ! -name 'gxps-fonts.h' ! -name 'gxps-images.h' \ ! -name 'gxps-brush.h' ! -name 'gxps-color.h' \ ! -name 'gxps-path.h' ! -name 'gxps-glyphs.h' \ ! -name 'gxps-matrix.h' \ ! -name 'gxps-parse-utils.h' | xargs git log -p X.Y.Z.. -- 3) Increment the gxps version in meson.build: If there are API additions, then increment the minor version and reset the micro version to 0. Otherwise, (ie. there are only bug fixes), increment the micro version to the next larger number. Adjust current, revision and age as described in the comments in the top level meson.build. 5) Commit the changes to NEWS and meson.build It's especially important to mention the new version number in your commit log. 6) Run "ninja libgxps-doc release" Running "ninja libgxps-doc release" generates the new tarball on the form of: libgxps-X.Y.Z.tar.xz (But the tar file isn't actually ready yet, as we still have some more steps to follow). 7) Push the commit 8) Tag the release with the following command: git tag -s -m "libgxps X.Y.Z release" X.Y.Z 9) Push the newly created tag out to the central tree with a command something like: git push origin X.Y.Z 10) Upload the tarball to ftp.gnome.org, by scp-ing it to master.gnome.org: scp libgxps-X.Y.Z.tar.xz (username)@master.gnome.org: 11) ssh into master.gnome.org and call ftpadmin install command: ftpadmin install libgxps-X.Y.Z.tar.xz 12) Send a message to announce the new release to gnome-announce-list@gnome.org 07070100000007000081A40000000000000000000000016447D411000001D4000000000000000000000000000000000000001500000000libgxps-0.3.2+5/TODOGeneral ------- - API tests - Demo program GXPSFile -------- - Thumbnail API GXPSDocument ------------ - Info API GXPSPage -------- - Lang API - Images API - Text API - Selections API - Render for printing API Rendering --------- - Resource dictionaries - Transparency + Opacity + Opacity Masks - Visual Brush - Elliptical Arc - Radial gradients when center != origin - Tile modes (FlipX, FlipY, FlipXY) - Triangle line cap - Font props (weight, slant) 07070100000008000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001500000000libgxps-0.3.2+5/docs07070100000009000081A40000000000000000000000016447D41100000071000000000000000000000000000000000000002100000000libgxps-0.3.2+5/docs/meson.buildif get_option('enable-gtk-doc') subdir('reference') endif if get_option('enable-man') subdir('tools') endif 0707010000000A000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001F00000000libgxps-0.3.2+5/docs/reference0707010000000B000081A40000000000000000000000016447D41100000596000000000000000000000000000000000000003100000000libgxps-0.3.2+5/docs/reference/libgxps-docs.sgml<?xml version="1.0"?> <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ <!ENTITY version SYSTEM "version.xml"> ]> <book id="index" xmlns:xi="http://www.w3.org/2003/XInclude"> <bookinfo> <title>GXPS Reference Manual</title> <releaseinfo> for libgxps &version; </releaseinfo> </bookinfo> <chapter> <title>GXPS</title> <xi:include href="xml/gxps-file.xml"/> <xi:include href="xml/gxps-document.xml"/> <xi:include href="xml/gxps-page.xml"/> <xi:include href="xml/gxps-links.xml"/> <xi:include href="xml/gxps-document-structure.xml"/> <xi:include href="xml/gxps-core-properties.xml"/> <xi:include href="xml/gxps-error.xml"/> <xi:include href="xml/gxps-version.xml"/> </chapter> <index id="api-index-full"> <title>Index of all symbols</title> <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include> </index> <index id="api-index-0-2-1" role="0.2.1"> <title>Index of new symbols in 0.2.1</title> <xi:include href="xml/api-index-0.2.1.xml"><xi:fallback /></xi:include> </index> <index id="api-index-0-2-3" role="0.2.3"> <title>Index of new symbols in 0.2.3</title> <xi:include href="xml/api-index-0.2.3.xml"><xi:fallback /></xi:include> </index> <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include> </book> 0707010000000C000081A40000000000000000000000016447D41100000DFF000000000000000000000000000000000000003400000000libgxps-0.3.2+5/docs/reference/libgxps-sections.txt<INCLUDE>libgxps/gxps.h</INCLUDE> <SECTION> <FILE>gxps-file</FILE> <TITLE>GXPSFile</TITLE> GXPSFile GXPS_FILE_ERROR GXPSFileError gxps_file_new gxps_file_get_n_documents gxps_file_get_document gxps_file_get_document_for_link_target gxps_file_get_core_properties <SUBSECTION Standard> GXPS_TYPE_FILE GXPS_FILE GXPS_FILE_CLASS GXPS_IS_FILE GXPS_IS_FILE_CLASS GXPS_FILE_GET_CLASS <SUBSECTION Private> GXPSFilePrivate gxps_file_get_type gxps_file_error_quark </SECTION> <SECTION> <FILE>gxps-document</FILE> <TITLE>GXPSDocument</TITLE> GXPSDocument gxps_document_get_n_pages gxps_document_get_page gxps_document_get_page_size gxps_document_get_page_for_anchor gxps_document_get_structure <SUBSECTION Standard> GXPS_TYPE_DOCUMENT GXPS_DOCUMENT GXPS_DOCUMENT_CLASS GXPS_IS_DOCUMENT GXPS_IS_DOCUMENT_CLASS GXPS_DOCUMENT_GET_CLASS <SUBSECTION Private> GXPSDocumentPrivate gxps_document_get_type </SECTION> <SECTION> <FILE>gxps-page</FILE> <TITLE>GXPSPage</TITLE> GXPSPage GXPS_PAGE_ERROR GXPSPageError gxps_page_get_size gxps_page_render gxps_page_get_links gxps_page_get_anchor_destination <SUBSECTION Standard> GXPS_TYPE_PAGE GXPS_PAGE GXPS_PAGE_CLASS GXPS_IS_PAGE GXPS_IS_PAGE_CLASS GXPS_PAGE_GET_CLASS <SUBSECTION Private> GXPSPagePrivate gxps_page_get_type gxps_page_error_quark </SECTION> <SECTION> <FILE>gxps-links</FILE> <TITLE>GXPS Links</TITLE> GXPSLinkTarget GXPSLink <SUBSECTION> gxps_link_target_copy gxps_link_target_free gxps_link_target_is_internal gxps_link_target_get_anchor gxps_link_target_get_uri <SUBSECTION> gxps_link_copy gxps_link_free gxps_link_get_target gxps_link_get_area <SUBSECTION Standard> GXPS_TYPE_LINK_TARGET GXPS_TYPE_LINK <SUBSECTION Private> gxps_link_target_get_type gxps_link_get_type </SECTION> <SECTION> <FILE>gxps-document-structure</FILE> <TITLE>GXPSDocumentStructure</TITLE> GXPSDocumentStructure GXPSOutlineIter gxps_document_structure_has_outline <SUBSECTION> gxps_document_structure_outline_iter_init gxps_outline_iter_next gxps_outline_iter_children gxps_outline_iter_get_description gxps_outline_iter_get_target <SUBSECTION Standard> GXPS_TYPE_DOCUMENT_STRUCTURE GXPS_DOCUMENT_STRUCTURE GXPS_DOCUMENT_STRUCTURE_CLASS GXPS_IS_DOCUMENT_STRUCTURE GXPS_IS_DOCUMENT_STRUCTURE_CLASS GXPS_DOCUMENT_STRUCTURE_GET_CLASS <SUBSECTION Private> GXPSDocumentStructurePrivate gxps_document_structure_get_type </SECTION> <SECTION> <FILE>gxps-core-properties</FILE> <TITLE>GXPSCoreProperties</TITLE> GXPSCoreProperties gxps_core_properties_get_title gxps_core_properties_get_creator gxps_core_properties_get_description gxps_core_properties_get_subject gxps_core_properties_get_keywords gxps_core_properties_get_version gxps_core_properties_get_revision gxps_core_properties_get_identifier gxps_core_properties_get_language gxps_core_properties_get_category gxps_core_properties_get_content_status gxps_core_properties_get_content_type gxps_core_properties_get_created gxps_core_properties_get_last_modified_by gxps_core_properties_get_modified gxps_core_properties_get_last_printed <SUBSECTION Standard> GXPS_TYPE_CORE_PROPERTIES GXPS_CORE_PROPERTIES GXPS_IS_CORE_PROPERTIES GXPS_CORE_PROPERTIES_CLASS GXPS_IS_CORE_PROPERTIES_CLASS GXPS_CORE_PROPERTIES_GET_CLASS <SUBSECTION Private> GXPSCorePropertiesPrivate gxps_core_properties_get_type </SECTION> <SECTION> <FILE>gxps-error</FILE> GXPS_ERROR GXPSError <SUBSECTION Private> gxps_error_quark </SECTION> <SECTION> <FILE>gxps-version</FILE> <TITLE>Version Information</TITLE> GXPS_MAJOR_VERSION GXPS_MINOR_VERSION GXPS_MICRO_VERSION GXPS_VERSION_STRING GXPS_CHECK_VERSION </SECTION> 0707010000000D000081A40000000000000000000000016447D411000000C4000000000000000000000000000000000000002D00000000libgxps-0.3.2+5/docs/reference/libgxps.types#include <libgxps/gxps.h> gxps_file_get_type gxps_document_get_type gxps_page_get_type gxps_link_target_get_type gxps_link_get_type gxps_document_structure_get_type gxps_core_properties_get_type 0707010000000E000081A40000000000000000000000016447D411000005DC000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/docs/reference/meson.buildversion_conf = configuration_data() version_conf.set('PACKAGE_VERSION', meson.project_version()) configure_file(input: 'version.xml.in', output: 'version.xml', configuration: version_conf) private_headers = [ 'gxps-archive.h', 'gxps-brush.h', 'gxps-color.h', 'gxps-debug.h', 'gxps-fonts.h', 'gxps-glyphs.h', 'gxps-images.h', 'gxps-matrix.h', 'gxps-page-private.h', 'gxps-parse-utils.h', 'gxps-path.h', 'gxps-private.h', 'gxps-resources.h', ] glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix') glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html') docpath = join_paths(gxps_datadir, 'gtk-doc', 'html') gnome.gtkdoc('libgxps', main_xml: 'libgxps-docs.sgml', src_dir: [ join_paths(meson.source_root(), 'libgxps'), join_paths(meson.build_root(), 'libgxps'), ], dependencies: gxps_dep, gobject_typesfile: 'libgxps.types', scan_args: [ '--rebuild-types', '--ignore-decorators=_GXPS_EXTERN', '--ignore-headers=' + ' '.join(private_headers), ], fixxref_args: [ '--html-dir=@0@'.format(docpath), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'glib')), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'gobject')), '--extra-dir=@0@'.format(join_paths(glib_docpath, 'gio')), ], install: true) 0707010000000F000081A40000000000000000000000016447D41100000012000000000000000000000000000000000000002E00000000libgxps-0.3.2+5/docs/reference/version.xml.in@PACKAGE_VERSION@ 07070100000010000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001B00000000libgxps-0.3.2+5/docs/tools07070100000011000081A40000000000000000000000016447D411000003E7000000000000000000000000000000000000002700000000libgxps-0.3.2+5/docs/tools/meson.buildxsltproc = find_program('xsltproc', required: false) if xsltproc.found() xlstproc_flags = [ '--nonet', ] man_files = [] if jpeg_dep.found() man_files += [ 'xpstojpeg' ] endif if cairo_pdf_dep.found() man_files += [ 'xpstopdf' ] endif if png_found man_files += [ 'xpstopng' ] endif if cairo_ps_dep.found() man_files += [ 'xpstops' ] endif if cairo_svg_dep.found() man_files += [ 'xpstosvg' ] endif foreach m: man_files custom_target(m + ' man page', input: '@0@.xml'.format(m), output: '@0@.1'.format(m), command: [ xsltproc, xlstproc_flags, '-o', '@OUTPUT@', 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl', '@INPUT@', ], install: true, install_dir: join_paths(gxps_mandir, 'man1')) endforeach endif 07070100000012000081A40000000000000000000000016447D41100001649000000000000000000000000000000000000002900000000libgxps-0.3.2+5/docs/tools/xpstojpeg.xml<refentry id="xpstojpeg" lang="en"> <refmeta> <refentrytitle>xpstojpeg</refentrytitle> <manvolnum>1</manvolnum> <refmiscinfo class="manual">XPS Tools</refmiscinfo> </refmeta> <refnamediv> <refname>xpstojpeg</refname> <refpurpose>XPS to JPEG converter</refpurpose> </refnamediv> <refsynopsisdiv> <cmdsynopsis> <command>xpstojpeg</command> <arg choice="opt" rep="repeat"><replaceable>OPTION</replaceable></arg> <arg choice="plain">FILE</arg> <arg choice="opt"><replaceable>OUTPUT FILE</replaceable></arg> </cmdsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> <para> <command>xpstojpeg</command> converts XPS documents to JPEG format. <command>xpstojpeg</command> reads the XPS file, <replaceable>FILE</replaceable>, and writes a JPEG file per page with the page number and file type appended to <replaceable>OUTPUT FILE</replaceable>. If <replaceable>OUTPUT FILE</replaceable> is not specified "page" will be used. </para> </refsect1> <refsect1> <title>Options</title> <variablelist> <varlistentry> <term><option>-?</option>, <option>--help</option></term> <listitem> <para> Show help options. </para> </listitem> </varlistentry> <varlistentry> <term><option>-d</option> <replaceable>DOCUMENT</replaceable>, <option>--document</option>=<replaceable>DOCUMENT</replaceable></term> <listitem> <para> The document inside the XPS file to convert. By default, the first document of the XPS file is used. </para> </listitem> </varlistentry> <varlistentry> <term><option>-f</option> <replaceable>PAGE</replaceable>, <option>--first</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The first page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-l</option> <replaceable>PAGE</replaceable>, <option>--last</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The last page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-o</option>, <option>--odd</option></term> <listitem> <para> Convert only odd pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-e</option>, <option>--even</option></term> <listitem> <para> Convert only even pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-r</option> <replaceable>RESOLUTION</replaceable>, <option>--resolution</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal and vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--rx</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--ry</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>-x</option> <replaceable>X</replaceable>, <option>--crop-x</option>=<replaceable>X</replaceable></term> <listitem> <para> The x-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-y</option> <replaceable>Y</replaceable>, <option>--crop-y</option>=<replaceable>Y</replaceable></term> <listitem> <para> The y-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-w</option> <replaceable>WIDTH</replaceable>, <option>--crop-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The width of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>-h</option> <replaceable>HEIGHT</replaceable>, <option>--crop-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The height of crop area. </para> </listitem> </varlistentry> </variablelist> </refsect1> <refsect1> <title>Bugs</title> <para> Please send bug reports to <ulink url="https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps">https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps</ulink>. </para> </refsect1> <refsect1> <title>See also</title> <para> <citerefentry> <refentrytitle>xpstopng</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstopdf</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstops</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstosvg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> </para> </refsect1> </refentry> 07070100000013000081A40000000000000000000000016447D41100001BC4000000000000000000000000000000000000002800000000libgxps-0.3.2+5/docs/tools/xpstopdf.xml<refentry id="xpstopdf" lang="en"> <refmeta> <refentrytitle>xpstopdf</refentrytitle> <manvolnum>1</manvolnum> <refmiscinfo class="manual">XPS Tools</refmiscinfo> </refmeta> <refnamediv> <refname>xpstopdf</refname> <refpurpose>XPS to PDF converter</refpurpose> </refnamediv> <refsynopsisdiv> <cmdsynopsis> <command>xpstopdf</command> <arg choice="opt" rep="repeat"><replaceable>OPTION</replaceable></arg> <arg choice="plain">FILE</arg> <arg choice="opt"><replaceable>OUTPUT FILE</replaceable></arg> </cmdsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> <para> <command>xpstopdf</command> converts XPS documents to PDF format. <command>xpstopdf</command> reads the XPS file, <replaceable>FILE</replaceable>, and writes a PDF file, <replaceable>OUTPUT FILE</replaceable>. If <replaceable>OUTPUT FILE</replaceable> is not specified the output filename will be derived from the <replaceable>FILE</replaceable> filename. </para> </refsect1> <refsect1> <title>Options</title> <variablelist> <varlistentry> <term><option>-?</option>, <option>--help</option></term> <listitem> <para> Show help options. </para> </listitem> </varlistentry> <varlistentry> <term><option>-d</option> <replaceable>DOCUMENT</replaceable>, <option>--document</option>=<replaceable>DOCUMENT</replaceable></term> <listitem> <para> The document inside the XPS file to convert. By default, the first document of the XPS file is used. </para> </listitem> </varlistentry> <varlistentry> <term><option>-f</option> <replaceable>PAGE</replaceable>, <option>--first</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The first page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-l</option> <replaceable>PAGE</replaceable>, <option>--last</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The last page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-o</option>, <option>--odd</option></term> <listitem> <para> Convert only odd pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-e</option>, <option>--even</option></term> <listitem> <para> Convert only even pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-r</option> <replaceable>RESOLUTION</replaceable>, <option>--resolution</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal and vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--rx</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--ry</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>-x</option> <replaceable>X</replaceable>, <option>--crop-x</option>=<replaceable>X</replaceable></term> <listitem> <para> The x-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-y</option> <replaceable>Y</replaceable>, <option>--crop-y</option>=<replaceable>Y</replaceable></term> <listitem> <para> The y-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-w</option> <replaceable>WIDTH</replaceable>, <option>--crop-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The width of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>-h</option> <replaceable>HEIGHT</replaceable>, <option>--crop-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The height of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>--paper-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The paper width. </para> </listitem> </varlistentry> <varlistentry> <term><option>--paper-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The paper height. </para> </listitem> </varlistentry> <varlistentry> <term><option>--expand</option></term> <listitem> <para> Expand pages smaller than the paper to fill the paper. By default, pages are not scaled. </para> </listitem> </varlistentry> <varlistentry> <term><option>--no-shrink</option></term> <listitem> <para> Don't scale pages which are larger than the paper. By default, pages larger than the paper are shrunk to fit. </para> </listitem> </varlistentry> <varlistentry> <term><option>--no-center</option></term> <listitem> <para> Don't center on the paper pages smaller than the paper (after any scaling). By default, pages smaller than the paper are aligned to the lower-left corner. </para> </listitem> </varlistentry> </variablelist> </refsect1> <refsect1> <title>Bugs</title> <para> Please send bug reports to <ulink url="https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps">https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps</ulink>. </para> </refsect1> <refsect1> <title>See also</title> <para> <citerefentry> <refentrytitle>xpstojpeg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstopng</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstops</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstosvg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> </para> </refsect1> </refentry> 07070100000014000081A40000000000000000000000016447D41100001748000000000000000000000000000000000000002800000000libgxps-0.3.2+5/docs/tools/xpstopng.xml<refentry id="xpstopng" lang="en"> <refmeta> <refentrytitle>xpstopng</refentrytitle> <manvolnum>1</manvolnum> <refmiscinfo class="manual">XPS Tools</refmiscinfo> </refmeta> <refnamediv> <refname>xpstopng</refname> <refpurpose>XPS to PNG converter</refpurpose> </refnamediv> <refsynopsisdiv> <cmdsynopsis> <command>xpstopng</command> <arg choice="opt" rep="repeat"><replaceable>OPTION</replaceable></arg> <arg choice="plain">FILE</arg> <arg choice="opt"><replaceable>OUTPUT FILE</replaceable></arg> </cmdsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> <para> <command>xpstopng</command> converts XPS documents to PNG format. <command>xpstopng</command> reads the XPS file, <replaceable>FILE</replaceable>, and writes a PNG file per page with the page number and file type appended to <replaceable>OUTPUT FILE</replaceable>. If <replaceable>OUTPUT FILE</replaceable> is not specified "page" will be used. </para> </refsect1> <refsect1> <title>Options</title> <variablelist> <varlistentry> <term><option>-?</option>, <option>--help</option></term> <listitem> <para> Show help options. </para> </listitem> </varlistentry> <varlistentry> <term><option>-d</option> <replaceable>DOCUMENT</replaceable>, <option>--document</option>=<replaceable>DOCUMENT</replaceable></term> <listitem> <para> The document inside the XPS file to convert. By default, the first document of the XPS file is used. </para> </listitem> </varlistentry> <varlistentry> <term><option>-f</option> <replaceable>PAGE</replaceable>, <option>--first</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The first page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-l</option> <replaceable>PAGE</replaceable>, <option>--last</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The last page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-o</option>, <option>--odd</option></term> <listitem> <para> Convert only odd pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-e</option>, <option>--even</option></term> <listitem> <para> Convert only even pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-r</option> <replaceable>RESOLUTION</replaceable>, <option>--resolution</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal and vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--rx</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--ry</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>-x</option> <replaceable>X</replaceable>, <option>--crop-x</option>=<replaceable>X</replaceable></term> <listitem> <para> The x-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-y</option> <replaceable>Y</replaceable>, <option>--crop-y</option>=<replaceable>Y</replaceable></term> <listitem> <para> The y-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-w</option> <replaceable>WIDTH</replaceable>, <option>--crop-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The width of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>-h</option> <replaceable>HEIGHT</replaceable>, <option>--crop-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The height of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>-t</option>, <option>--transparent-bg</option></term> <listitem> <para> Use a transparent background for pages instead of white. </para> </listitem> </varlistentry> </variablelist> </refsect1> <refsect1> <title>Bugs</title> <para> Please send bug reports to <ulink url="https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps">https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps</ulink>. </para> </refsect1> <refsect1> <title>See also</title> <para> <citerefentry> <refentrytitle>xpstojpeg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstopdf</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstops</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstosvg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> </para> </refsect1> </refentry> 07070100000015000081A40000000000000000000000016447D411000021D2000000000000000000000000000000000000002700000000libgxps-0.3.2+5/docs/tools/xpstops.xml<refentry id="xpstops" lang="en"> <refmeta> <refentrytitle>xpstops</refentrytitle> <manvolnum>1</manvolnum> <refmiscinfo class="manual">XPS Tools</refmiscinfo> </refmeta> <refnamediv> <refname>xpstops</refname> <refpurpose>XPS to PostScript converter</refpurpose> </refnamediv> <refsynopsisdiv> <cmdsynopsis> <command>xpstops</command> <arg choice="opt" rep="repeat"><replaceable>OPTION</replaceable></arg> <arg choice="plain">FILE</arg> <arg choice="opt"><replaceable>OUTPUT FILE</replaceable></arg> </cmdsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> <para> <command>xpstops</command> converts XPS documents to PostScript format. <command>xpstops</command> reads the XPS file, <replaceable>FILE</replaceable>, and writes a PostScript file, <replaceable>OUTPUT FILE</replaceable>. If <replaceable>OUTPUT FILE</replaceable> is not specified the output filename will be derived from the <replaceable>FILE</replaceable> filename. </para> </refsect1> <refsect1> <title>Options</title> <variablelist> <varlistentry> <term><option>-?</option>, <option>--help</option></term> <listitem> <para> Show help options. </para> </listitem> </varlistentry> <varlistentry> <term><option>-d</option> <replaceable>DOCUMENT</replaceable>, <option>--document</option>=<replaceable>DOCUMENT</replaceable></term> <listitem> <para> The document inside the XPS file to convert. By default, the first document of the XPS file is used. </para> </listitem> </varlistentry> <varlistentry> <term><option>-f</option> <replaceable>PAGE</replaceable>, <option>--first</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The first page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-l</option> <replaceable>PAGE</replaceable>, <option>--last</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The last page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-o</option>, <option>--odd</option></term> <listitem> <para> Convert only odd pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-e</option>, <option>--even</option></term> <listitem> <para> Convert only even pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-r</option> <replaceable>RESOLUTION</replaceable>, <option>--resolution</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal and vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--rx</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--ry</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>-x</option> <replaceable>X</replaceable>, <option>--crop-x</option>=<replaceable>X</replaceable></term> <listitem> <para> The x-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-y</option> <replaceable>Y</replaceable>, <option>--crop-y</option>=<replaceable>Y</replaceable></term> <listitem> <para> The y-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-w</option> <replaceable>WIDTH</replaceable>, <option>--crop-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The width of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>-h</option> <replaceable>HEIGHT</replaceable>, <option>--crop-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The height of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>--level2</option></term> <listitem> <para> Generate Level 2 PostScript. Level 2 supports color images and image compression. </para> </listitem> </varlistentry> <varlistentry> <term><option>--level3</option></term> <listitem> <para> Generate Level 3 PostScript. This enables all Level 2 features plus shading patterns and masked images. This is the default setting. </para> </listitem> </varlistentry> <varlistentry> <term><option>--eps</option></term> <listitem> <para> Generate an Encapsulated PostScript (EPS) file. </para> </listitem> </varlistentry> <varlistentry> <term><option>--paper</option>=<replaceable>PAPER</replaceable></term> <listitem> <para> Set the paper size to one of "A0", "A1", "A2", "A3", "A4", "A5", "B4", "B5", "Letter", "Tabloid", "Ledger", "Legal", "Statement", "Executive", "Folio", "Quarto", "10x14". </para> </listitem> </varlistentry> <varlistentry> <term><option>--duplex</option></term> <listitem> <para> Adds the %%IncludeFeature: *Duplex DuplexNoTumble DSC comment to the PostScript file. This tells the print manager to enable duplexing. </para> </listitem> </varlistentry> <varlistentry> <term><option>--paper-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The paper width. </para> </listitem> </varlistentry> <varlistentry> <term><option>--paper-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The paper height. </para> </listitem> </varlistentry> <varlistentry> <term><option>--expand</option></term> <listitem> <para> Expand pages smaller than the paper to fill the paper. By default, pages are not scaled. </para> </listitem> </varlistentry> <varlistentry> <term><option>--no-shrink</option></term> <listitem> <para> Don't scale pages which are larger than the paper. By default, pages larger than the paper are shrunk to fit. </para> </listitem> </varlistentry> <varlistentry> <term><option>--no-center</option></term> <listitem> <para> Don't center on the paper pages smaller than the paper (after any scaling). By default, pages smaller than the paper are aligned to the lower-left corner. </para> </listitem> </varlistentry> </variablelist> </refsect1> <refsect1> <title>Bugs</title> <para> Please send bug reports to <ulink url="https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps">https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps</ulink>. </para> </refsect1> <refsect1> <title>See also</title> <para> <citerefentry> <refentrytitle>xpstojpeg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstopng</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstopdf</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstosvg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> </para> </refsect1> </refentry> 07070100000016000081A40000000000000000000000016447D41100001BC4000000000000000000000000000000000000002800000000libgxps-0.3.2+5/docs/tools/xpstosvg.xml<refentry id="xpstosvg" lang="en"> <refmeta> <refentrytitle>xpstosvg</refentrytitle> <manvolnum>1</manvolnum> <refmiscinfo class="manual">XPS Tools</refmiscinfo> </refmeta> <refnamediv> <refname>xpstosvg</refname> <refpurpose>XPS to SVG converter</refpurpose> </refnamediv> <refsynopsisdiv> <cmdsynopsis> <command>xpstosvg</command> <arg choice="opt" rep="repeat"><replaceable>OPTION</replaceable></arg> <arg choice="plain">FILE</arg> <arg choice="opt"><replaceable>OUTPUT FILE</replaceable></arg> </cmdsynopsis> </refsynopsisdiv> <refsect1> <title>Description</title> <para> <command>xpstosvg</command> converts XPS documents to SVG format. <command>xpstosvg</command> reads the XPS file, <replaceable>FILE</replaceable>, and writes a SVG file, <replaceable>OUTPUT FILE</replaceable>. If <replaceable>OUTPUT FILE</replaceable> is not specified the output filename will be derived from the <replaceable>FILE</replaceable> filename. </para> </refsect1> <refsect1> <title>Options</title> <variablelist> <varlistentry> <term><option>-?</option>, <option>--help</option></term> <listitem> <para> Show help options. </para> </listitem> </varlistentry> <varlistentry> <term><option>-d</option> <replaceable>DOCUMENT</replaceable>, <option>--document</option>=<replaceable>DOCUMENT</replaceable></term> <listitem> <para> The document inside the XPS file to convert. By default, the first document of the XPS file is used. </para> </listitem> </varlistentry> <varlistentry> <term><option>-f</option> <replaceable>PAGE</replaceable>, <option>--first</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The first page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-l</option> <replaceable>PAGE</replaceable>, <option>--last</option>=<replaceable>PAGE</replaceable></term> <listitem> <para> The last page to convert. </para> </listitem> </varlistentry> <varlistentry> <term><option>-o</option>, <option>--odd</option></term> <listitem> <para> Convert only odd pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-e</option>, <option>--even</option></term> <listitem> <para> Convert only even pages. </para> </listitem> </varlistentry> <varlistentry> <term><option>-r</option> <replaceable>RESOLUTION</replaceable>, <option>--resolution</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal and vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--rx</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Horizontal resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>--ry</option>=<replaceable>RESOLUTION</replaceable></term> <listitem> <para> Vertical resolution in PPI (Pixels Per Inch). The default is 150 PPI. </para> </listitem> </varlistentry> <varlistentry> <term><option>-x</option> <replaceable>X</replaceable>, <option>--crop-x</option>=<replaceable>X</replaceable></term> <listitem> <para> The x-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-y</option> <replaceable>Y</replaceable>, <option>--crop-y</option>=<replaceable>Y</replaceable></term> <listitem> <para> The y-coordinate of the crop area top left corner. </para> </listitem> </varlistentry> <varlistentry> <term><option>-w</option> <replaceable>WIDTH</replaceable>, <option>--crop-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The width of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>-h</option> <replaceable>HEIGHT</replaceable>, <option>--crop-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The height of crop area. </para> </listitem> </varlistentry> <varlistentry> <term><option>--paper-width</option>=<replaceable>WIDTH</replaceable></term> <listitem> <para> The paper width. </para> </listitem> </varlistentry> <varlistentry> <term><option>--paper-height</option>=<replaceable>HEIGHT</replaceable></term> <listitem> <para> The paper height. </para> </listitem> </varlistentry> <varlistentry> <term><option>--expand</option></term> <listitem> <para> Expand pages smaller than the paper to fill the paper. By default, pages are not scaled. </para> </listitem> </varlistentry> <varlistentry> <term><option>--no-shrink</option></term> <listitem> <para> Don't scale pages which are larger than the paper. By default, pages larger than the paper are shrunk to fit. </para> </listitem> </varlistentry> <varlistentry> <term><option>--no-center</option></term> <listitem> <para> Don't center on the paper pages smaller than the paper (after any scaling). By default, pages smaller than the paper are aligned to the lower-left corner. </para> </listitem> </varlistentry> </variablelist> </refsect1> <refsect1> <title>Bugs</title> <para> Please send bug reports to <ulink url="https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps">https://bugzilla.gnome.org/enter_bug.cgi?product=libgxps</ulink>. </para> </refsect1> <refsect1> <title>See also</title> <para> <citerefentry> <refentrytitle>xpstojpeg</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstopng</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstopdf</refentrytitle><manvolnum>1</manvolnum> </citerefentry> <citerefentry> <refentrytitle>xpstops</refentrytitle><manvolnum>1</manvolnum> </citerefentry> </para> </refsect1> </refentry> 07070100000017000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001800000000libgxps-0.3.2+5/libgxps07070100000018000081A40000000000000000000000016447D41100000431000000000000000000000000000000000000001D00000000libgxps-0.3.2+5/libgxps.doap<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:gnome="http://api.gnome.org/doap-extensions#" xmlns="http://usefulinc.com/ns/doap#"> <name xml:lang="en">libgxps</name> <shortdesc xml:lang="en">XPS Documents library</shortdesc> <description xml:lang="en">XPS Documents library</description> <category rdf:resource="http://api.gnome.org/doap-extensions#core" /> <programming-language>C</programming-language> <programming-language>Python</programming-language> <homepage rdf:resource="http://live.gnome.org/libgxps" /> <download-page rdf:resource="http://download.gnome.org/sources/libgxps/" /> <bug-database rdf:resource="https://gitlab.gnome.org/GNOME/libgxps/issues" /> <maintainer> <foaf:Person> <foaf:name>Carlos Garcia Campos</foaf:name> <foaf:mbox rdf:resource="mailto:carlosgc@gnome.org" /> <gnome:userid>carlosgc</gnome:userid> </foaf:Person> </maintainer> </Project> 07070100000019000081A40000000000000000000000016447D4110000037D000000000000000000000000000000000000002900000000libgxps-0.3.2+5/libgxps/Makefile.sourcesNULL = GXPS_BASE_NOINST_H_FILES = \ gxps-archive.h \ gxps-brush.h \ gxps-color.h \ gxps-debug.h \ gxps-fonts.h \ gxps-glyphs.h \ gxps-images.h \ gxps-matrix.h \ gxps-page-private.h \ gxps-parse-utils.h \ gxps-path.h \ gxps-private.h \ gxps-resources.h \ $(NULL) GXPS_BASE_INST_H_FILES = \ gxps.h \ gxps-core-properties.h \ gxps-document.h \ gxps-document-structure.h \ gxps-error.h \ gxps-file.h \ gxps-links.h \ gxps-page.h \ gxps-version.h \ $(NULL) GXPS_BASE_SOURCES = \ gxps-archive.c \ gxps-brush.c \ gxps-color.c \ gxps-core-properties.c \ gxps-debug.c \ gxps-document.c \ gxps-document-structure.c \ gxps-error.c \ gxps-file.c \ gxps-fonts.c \ gxps-glyphs.c \ gxps-links.c \ gxps-matrix.c \ gxps-images.c \ gxps-page.c \ gxps-parse-utils.c \ gxps-path.c \ gxps-resources.c \ $(NULL) 0707010000001A000081A40000000000000000000000016447D41100003C55000000000000000000000000000000000000002700000000libgxps-0.3.2+5/libgxps/gxps-archive.c/* GXPSArchive * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * Copyright (C) 2008 Benjamin Otte <otte@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <string.h> #include <archive_entry.h> #include "gxps-archive.h" enum { PROP_0, PROP_FILE }; struct _GXPSArchive { GObject parent; gboolean initialized; GError *init_error; GFile *filename; GHashTable *entries; GXPSResources *resources; }; struct _GXPSArchiveClass { GObjectClass parent_class; }; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (GXPSArchive, gxps_archive, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) #define BUFFER_SIZE 4096 /* Based on code from GVFS */ typedef struct { struct archive *archive; GFile *file; GFileInputStream *stream; guchar buffer[BUFFER_SIZE]; GError *error; } ZipArchive; static int _archive_open (struct archive *archive, void *data) { ZipArchive *zip = (ZipArchive *)data; zip->stream = g_file_read (zip->file, NULL, &zip->error); return (zip->error) ? ARCHIVE_FATAL : ARCHIVE_OK; } static __LA_SSIZE_T _archive_read (struct archive *archive, void *data, const void **buffer) { ZipArchive *zip = (ZipArchive *)data; gssize read_bytes; *buffer = zip->buffer; read_bytes = g_input_stream_read (G_INPUT_STREAM (zip->stream), zip->buffer, sizeof (zip->buffer), NULL, &zip->error); return read_bytes; } static __LA_INT64_T _archive_skip (struct archive *archive, void *data, __LA_INT64_T request) { ZipArchive *zip = (ZipArchive *)data; if (!g_seekable_can_seek (G_SEEKABLE (zip->stream))) return 0; g_seekable_seek (G_SEEKABLE (zip->stream), request, G_SEEK_CUR, NULL, &zip->error); if (zip->error) { g_clear_error (&zip->error); request = 0; } return request; } static int _archive_close (struct archive *archive, void *data) { ZipArchive *zip = (ZipArchive *)data; g_clear_object (&zip->stream); return ARCHIVE_OK; } static ZipArchive * gxps_zip_archive_create (GFile *filename) { ZipArchive *zip; zip = g_slice_new0 (ZipArchive); zip->file = filename; zip->archive = archive_read_new (); archive_read_support_format_zip (zip->archive); archive_read_open2 (zip->archive, zip, _archive_open, _archive_read, _archive_skip, _archive_close); return zip; } static gboolean gxps_zip_archive_iter_next (ZipArchive *zip, struct archive_entry **entry) { int result; result = archive_read_next_header (zip->archive, entry); if (result >= ARCHIVE_WARN && result <= ARCHIVE_OK) { if (result < ARCHIVE_OK) { g_warning ("Error: %s\n", archive_error_string (zip->archive)); archive_set_error (zip->archive, ARCHIVE_OK, "No error"); archive_clear_error (zip->archive); } return TRUE; } return result != ARCHIVE_FATAL && result != ARCHIVE_EOF; } static void gxps_zip_archive_destroy (ZipArchive *zip) { G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* This is a deprecated synonym for archive_read_free() in libarchive * 3.0; but is not deprecated in libarchive 2.0, which we continue to * support. */ archive_read_finish (zip->archive); G_GNUC_END_IGNORE_DEPRECATIONS g_slice_free (ZipArchive, zip); } static void gxps_archive_finalize (GObject *object) { GXPSArchive *archive = GXPS_ARCHIVE (object); g_clear_pointer (&archive->entries, g_hash_table_unref); g_clear_object (&archive->filename); g_clear_error (&archive->init_error); g_clear_object (&archive->resources); G_OBJECT_CLASS (gxps_archive_parent_class)->finalize (object); } static guint caseless_hash (gconstpointer v) { gchar *lower; guint ret; lower = g_ascii_strdown (v, -1); ret = g_str_hash (lower); g_free (lower); return ret; } static gboolean caseless_equal (gconstpointer v1, gconstpointer v2) { return g_ascii_strcasecmp (v1, v2) == 0; } static void gxps_archive_init (GXPSArchive *archive) { archive->entries = g_hash_table_new_full (caseless_hash, caseless_equal, g_free, NULL); } static void gxps_archive_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GXPSArchive *archive = GXPS_ARCHIVE (object); switch (prop_id) { case PROP_FILE: archive->filename = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gxps_archive_class_init (GXPSArchiveClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = gxps_archive_set_property; object_class->finalize = gxps_archive_finalize; g_object_class_install_property (object_class, PROP_FILE, g_param_spec_object ("file", "File", "The archive file", G_TYPE_FILE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); } static gboolean gxps_archive_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { GXPSArchive *archive; ZipArchive *zip; struct archive_entry *entry; const gchar *pathname; archive = GXPS_ARCHIVE (initable); if (archive->initialized) { if (archive->init_error) { g_propagate_error (error, g_error_copy (archive->init_error)); return FALSE; } return TRUE; } archive->initialized = TRUE; zip = gxps_zip_archive_create (archive->filename); if (zip->error) { g_propagate_error (&archive->init_error, zip->error); g_propagate_error (error, g_error_copy (archive->init_error)); gxps_zip_archive_destroy (zip); return FALSE; } while (gxps_zip_archive_iter_next (zip, &entry)) { /* FIXME: We can ignore directories here */ pathname = archive_entry_pathname (entry); if (pathname != NULL) g_hash_table_add (archive->entries, g_strdup (pathname)); archive_read_data_skip (zip->archive); } gxps_zip_archive_destroy (zip); return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = gxps_archive_initable_init; } GXPSArchive * gxps_archive_new (GFile *filename, GError **error) { return g_initable_new (GXPS_TYPE_ARCHIVE, NULL, error, "file", filename, NULL); } gboolean gxps_archive_has_entry (GXPSArchive *archive, const gchar *path) { if (path == NULL) return FALSE; if (path[0] == '/') path++; return g_hash_table_contains (archive->entries, path); } GXPSResources * gxps_archive_get_resources (GXPSArchive *archive) { g_return_val_if_fail (GXPS_IS_ARCHIVE (archive), NULL); if (archive->resources == NULL) archive->resources = g_object_new (GXPS_TYPE_RESOURCES, "archive", archive, NULL); return archive->resources; } /* GXPSArchiveInputStream */ typedef struct _GXPSArchiveInputStream { GInputStream parent; ZipArchive *zip; gboolean is_interleaved; guint piece; struct archive_entry *entry; } GXPSArchiveInputStream; typedef struct _GXPSArchiveInputStreamClass { GInputStreamClass parent_class; } GXPSArchiveInputStreamClass; static GType gxps_archive_input_stream_get_type (void) G_GNUC_CONST; #define GXPS_TYPE_ARCHIVE_INPUT_STREAM (gxps_archive_input_stream_get_type()) #define GXPS_ARCHIVE_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_ARCHIVE_INPUT_STREAM, GXPSArchiveInputStream)) G_DEFINE_TYPE (GXPSArchiveInputStream, gxps_archive_input_stream, G_TYPE_INPUT_STREAM) GInputStream * gxps_archive_open (GXPSArchive *archive, const gchar *path) { GXPSArchiveInputStream *stream; gchar *first_piece_path = NULL; if (path == NULL) return NULL; if (path[0] == '/') path++; if (!g_hash_table_contains (archive->entries, path)) { first_piece_path = g_build_path ("/", path, "[0].piece", NULL); if (!g_hash_table_contains (archive->entries, first_piece_path)) { g_free (first_piece_path); return NULL; } path = first_piece_path; } stream = (GXPSArchiveInputStream *)g_object_new (GXPS_TYPE_ARCHIVE_INPUT_STREAM, NULL); stream->zip = gxps_zip_archive_create (archive->filename); stream->is_interleaved = first_piece_path != NULL; while (gxps_zip_archive_iter_next (stream->zip, &stream->entry)) { if (g_ascii_strcasecmp (path, archive_entry_pathname (stream->entry)) == 0) break; archive_read_data_skip (stream->zip->archive); } g_free (first_piece_path); return G_INPUT_STREAM (stream); } gboolean gxps_archive_read_entry (GXPSArchive *archive, const gchar *path, guchar **buffer, gsize *bytes_read, GError **error) { GInputStream *stream; gssize entry_size; gboolean retval; stream = gxps_archive_open (archive, path); if (!stream) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "The entry '%s' was not found in archive", path); return FALSE; } entry_size = archive_entry_size (GXPS_ARCHIVE_INPUT_STREAM (stream)->entry); if (entry_size <= 0) { gssize bytes; guchar buf[BUFFER_SIZE]; guint buffer_size = BUFFER_SIZE * 4; /* In some cases, I don't know why, archive_entry_size() returns 0, * but the entry can be read, so let's try here. */ *bytes_read = 0; *buffer = g_malloc (buffer_size); do { bytes = g_input_stream_read (stream, &buf, BUFFER_SIZE, NULL, error); if (bytes < 0) { g_free (*buffer); g_object_unref (stream); return FALSE; } if (*bytes_read + bytes > buffer_size) { buffer_size += BUFFER_SIZE * 4; *buffer = g_realloc (*buffer, buffer_size); } memcpy (*buffer + *bytes_read, buf, bytes); *bytes_read += bytes; } while (bytes > 0); g_object_unref (stream); if (*bytes_read == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "The entry '%s' is empty in archive", path); g_free (*buffer); return FALSE; } return TRUE; } *buffer = g_malloc (entry_size); retval = g_input_stream_read_all (stream, *buffer, entry_size, bytes_read, NULL, error); if (!retval) g_free (*buffer); g_object_unref (stream); return retval; } static gboolean gxps_archive_input_stream_is_last_piece (GXPSArchiveInputStream *stream) { return g_str_has_suffix (archive_entry_pathname (stream->entry), ".last.piece"); } static void gxps_archive_input_stream_next_piece (GXPSArchiveInputStream *stream) { gchar *dirname; gchar *prefix; if (!stream->is_interleaved) return; dirname = g_path_get_dirname (archive_entry_pathname (stream->entry)); if (!dirname) return; stream->piece++; prefix = g_strdup_printf ("%s/[%u]", dirname, stream->piece); g_free (dirname); while (gxps_zip_archive_iter_next (stream->zip, &stream->entry)) { if (g_str_has_prefix (archive_entry_pathname (stream->entry), prefix)) { const gchar *suffix = archive_entry_pathname (stream->entry) + strlen (prefix); if (g_ascii_strcasecmp (suffix, ".piece") == 0 || g_ascii_strcasecmp (suffix, ".last.piece") == 0) break; } archive_read_data_skip (stream->zip->archive); } g_free (prefix); } static gssize gxps_archive_input_stream_read (GInputStream *stream, void *buffer, gsize count, GCancellable *cancellable, GError **error) { GXPSArchiveInputStream *istream = GXPS_ARCHIVE_INPUT_STREAM (stream); gssize bytes_read; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return -1; bytes_read = archive_read_data (istream->zip->archive, buffer, count); if (bytes_read < 0) { g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (archive_errno (istream->zip->archive)), archive_error_string (istream->zip->archive)); return -1; } if (bytes_read == 0 && istream->is_interleaved && !gxps_archive_input_stream_is_last_piece (istream)) { /* Read next piece */ gxps_archive_input_stream_next_piece (istream); bytes_read = gxps_archive_input_stream_read (stream, buffer, count, cancellable, error); } return bytes_read; } static gssize gxps_archive_input_stream_skip (GInputStream *stream, gsize count, GCancellable *cancellable, GError **error) { return 0; } static gboolean gxps_archive_input_stream_close (GInputStream *stream, GCancellable *cancellable, GError **error) { GXPSArchiveInputStream *istream = GXPS_ARCHIVE_INPUT_STREAM (stream); if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; g_clear_pointer (&istream->zip, gxps_zip_archive_destroy); return TRUE; } static void gxps_archive_input_stream_finalize (GObject *object) { GXPSArchiveInputStream *stream = GXPS_ARCHIVE_INPUT_STREAM (object); g_clear_pointer (&stream->zip, gxps_zip_archive_destroy); G_OBJECT_CLASS (gxps_archive_input_stream_parent_class)->finalize (object); } static void gxps_archive_input_stream_init (GXPSArchiveInputStream *istream) { } static void gxps_archive_input_stream_class_init (GXPSArchiveInputStreamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass); object_class->finalize = gxps_archive_input_stream_finalize; istream_class->read_fn = gxps_archive_input_stream_read; istream_class->skip = gxps_archive_input_stream_skip; istream_class->close_fn = gxps_archive_input_stream_close; } 0707010000001B000081A40000000000000000000000016447D4110000098B000000000000000000000000000000000000002700000000libgxps-0.3.2+5/libgxps/gxps-archive.h/* GXPSArchive * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_ARCHIVE_H__ #define __GXPS_ARCHIVE_H__ #include <glib-object.h> #include <gio/gio.h> #include <archive.h> #include <libgxps/gxps-version.h> #include <libgxps/gxps-resources.h> G_BEGIN_DECLS #define GXPS_TYPE_ARCHIVE (gxps_archive_get_type ()) #define GXPS_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_ARCHIVE, GXPSArchive)) #define GXPS_ARCHIVE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_ARCHIVE, GXPSArchiveClass)) #define GXPS_IS_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_ARCHIVE)) #define GXPS_IS_ARCHIVE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_ARCHIVE)) #define GXPS_ARCHIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_ARCHIVE, GXPSArchiveClass)) typedef struct _GXPSArchive GXPSArchive; typedef struct _GXPSArchiveClass GXPSArchiveClass; GType gxps_archive_get_type (void) G_GNUC_CONST; GXPSArchive *gxps_archive_new (GFile *filename, GError **error); gboolean gxps_archive_has_entry (GXPSArchive *archive, const gchar *path); GXPSResources *gxps_archive_get_resources (GXPSArchive *archive); GInputStream *gxps_archive_open (GXPSArchive *archive, const gchar *path); gboolean gxps_archive_read_entry (GXPSArchive *archive, const gchar *path, guchar **buffer, gsize *bytes_read, GError **error); G_END_DECLS #endif /* __GXPS_ARCHIVE_H__ */ 0707010000001C000081A40000000000000000000000016447D4110000B7B0000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-brush.c/* GXPSBrush * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <math.h> #include <string.h> #include "gxps-brush.h" #include "gxps-matrix.h" #include "gxps-color.h" #include "gxps-parse-utils.h" #include "gxps-debug.h" typedef struct { GXPSBrush *brush; gchar *image_uri; cairo_matrix_t matrix; cairo_rectangle_t viewport; cairo_rectangle_t viewbox; cairo_extend_t extend; } GXPSBrushImage; struct _GXPSBrushVisual { GXPSBrush *brush; cairo_matrix_t matrix; cairo_rectangle_t viewport; cairo_rectangle_t viewbox; cairo_extend_t extend; }; GXPSBrush * gxps_brush_new (GXPSRenderContext *ctx) { GXPSBrush *brush; brush = g_slice_new0 (GXPSBrush); brush->ctx = ctx; brush->opacity = 1.0; return brush; } static gdouble gxps_transform_hypot (const cairo_matrix_t *matrix, double dx, double dy) { cairo_matrix_transform_distance (matrix, &dx, &dy); return hypot (dx, dy); } void gxps_brush_free (GXPSBrush *brush) { if (G_UNLIKELY (!brush)) return; cairo_pattern_destroy (brush->pattern); g_slice_free (GXPSBrush, brush); } static gboolean hex (const gchar *spec, gint len, guint *c) { const gchar *end; *c = 0; for (end = spec + len; spec != end; spec++) { if (!g_ascii_isxdigit (*spec)) return FALSE; *c = (*c << 4) | g_ascii_xdigit_value (*spec); } return TRUE; } static gboolean gxps_color_s_rgb_parse (const gchar *color_str, GXPSColor *color) { gsize len = strlen (color_str); guint a = 255; guint r, g, b; switch (len) { case 6: if (!hex (color_str, 2, &r) || !hex (color_str + 2, 2, &g) || !hex (color_str + 4, 2, &b)) return FALSE; break; case 8: if (!hex (color_str, 2, &a) || !hex (color_str + 2, 2, &r) || !hex (color_str + 4, 2, &g) || !hex (color_str + 6, 2, &b)) return FALSE; break; default: return FALSE; } color->alpha = a / 255.; color->red = r / 255.; color->green = g / 255.; color->blue = b / 255.; return TRUE; } static gboolean gxps_color_sc_rgb_parse (const gchar *color_str, GXPSColor *color) { gchar **tokens; gsize len; gdouble c[4]; guint i, start; tokens = g_strsplit (color_str, ",", 4); len = g_strv_length (tokens); switch (len) { case 4: if (!gxps_value_get_double (tokens[0], &c[0])) { g_strfreev (tokens); return FALSE; } start = 1; break; case 3: c[0] = 1.0; start = 0; break; default: g_strfreev (tokens); return FALSE; } for (i = start; i < len; i++) { if (!gxps_value_get_double (tokens[i], &c[i])) { g_strfreev (tokens); return FALSE; } } g_strfreev (tokens); color->alpha = CLAMP (c[0], 0., 1.); color->red = CLAMP (c[1], 0., 1.); color->green = CLAMP (c[2], 0., 1.); color->blue = CLAMP (c[3], 0., 1.); return TRUE; } static gboolean gxps_color_icc_parse (const gchar *color_str, GXPSArchive *zip, GXPSColor *color) { const gchar *p; gchar *icc_profile_uri; gchar **tokens; gsize len; gdouble alpha; gdouble values[GXPS_COLOR_MAX_CHANNELS]; guint i, j; gboolean retval; p = strstr (color_str, " "); if (!p) return FALSE; icc_profile_uri = g_strndup (color_str, strlen (color_str) - strlen (p)); tokens = g_strsplit (++p, ",", -1); len = g_strv_length (tokens); if (len < 2) { g_strfreev (tokens); g_free (icc_profile_uri); return FALSE; } if (!gxps_value_get_double (tokens[0], &alpha)) { g_strfreev (tokens); g_free (icc_profile_uri); return FALSE; } for (i = 0, j = 1; i < GXPS_COLOR_MAX_CHANNELS && j < len; i++, j++) { if (!gxps_value_get_double (tokens[j], &values[i])) { g_strfreev (tokens); g_free (icc_profile_uri); return FALSE; } } g_strfreev (tokens); color->alpha = CLAMP (alpha, 0., 1.); retval = gxps_color_new_for_icc (zip, icc_profile_uri, values, i, color); g_free (icc_profile_uri); return retval; } static gboolean gxps_color_parse (const gchar *data, GXPSArchive *zip, GXPSColor *color) { const gchar *p; p = strstr (data, "#"); if (!p) { p = strstr (data, "ContextColor"); if (p == data) { p += strlen ("ContextColor"); return gxps_color_icc_parse (++p, zip, color); } GXPS_DEBUG (g_debug ("Unsupported color %s", data)); return FALSE; } if (p == data) return gxps_color_s_rgb_parse (++p, color); if (strncmp (data, "sc", 2) == 0 && p == data + 2) return gxps_color_sc_rgb_parse (++p, color); GXPS_DEBUG (g_debug ("Unsupported color %s", data)); return FALSE; } gboolean gxps_brush_solid_color_parse (const gchar *data, GXPSArchive *zip, gdouble alpha, cairo_pattern_t **pattern) { GXPSColor color; cairo_pattern_t *retval; if (!gxps_color_parse (data, zip, &color)) return FALSE; retval = cairo_pattern_create_rgba (color.red, color.green, color.blue, color.alpha * alpha); if (cairo_pattern_status (retval)) { cairo_pattern_destroy (retval); return FALSE; } if (pattern) *pattern = retval; return TRUE; } static cairo_extend_t gxps_spread_method_parse (const gchar *spread) { if (strcmp (spread, "Pad") == 0) return CAIRO_EXTEND_PAD; else if (strcmp (spread, "Reflect") == 0) return CAIRO_EXTEND_REFLECT; else if (strcmp (spread, "Repeat") == 0) return CAIRO_EXTEND_REPEAT; return CAIRO_EXTEND_NONE; } static cairo_extend_t gxps_tile_mode_parse (const gchar *tile) { if (strcmp (tile, "Tile") == 0) return CAIRO_EXTEND_REPEAT; else if (strcmp (tile, "FlipX") == 0) GXPS_DEBUG (g_debug ("Unsupported tile mode FlipX")); else if (strcmp (tile, "FlipY") == 0) GXPS_DEBUG (g_debug ("Unsupported tile mode FlipY")); else if (strcmp (tile, "FlipXY") == 0) GXPS_DEBUG (g_debug ("Unsupported tile mode FlipXY")); return CAIRO_EXTEND_NONE; } static GXPSBrushImage * gxps_brush_image_new (GXPSBrush *brush, gchar *image_uri, cairo_rectangle_t *viewport, cairo_rectangle_t *viewbox) { GXPSBrushImage *image; image = g_slice_new0 (GXPSBrushImage); image->brush = brush; cairo_matrix_init_identity (&image->matrix); /* Required values */ image->image_uri = image_uri; image->viewport = *viewport; image->viewbox = *viewbox; return image; } static void gxps_brush_image_free (GXPSBrushImage *image) { if (G_UNLIKELY (!image)) return; g_free (image->image_uri); g_slice_free (GXPSBrushImage, image); } static void brush_image_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSBrushImage *image = (GXPSBrushImage *)user_data; if (strcmp (element_name, "ImageBrush.Transform") == 0) { GXPSMatrix *matrix; matrix = gxps_matrix_new (image->brush->ctx); gxps_matrix_parser_push (context, matrix); } else { gxps_parse_error (context, image->brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void brush_image_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSBrushImage *image = (GXPSBrushImage *)user_data; if (strcmp (element_name, "ImageBrush.Transform") == 0) { GXPSMatrix *matrix; matrix = g_markup_parse_context_pop (context); image->matrix = matrix->matrix; gxps_matrix_free (matrix); } else { gxps_parse_error (context, image->brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void brush_image_error (GMarkupParseContext *context, GError *error, gpointer user_data) { GXPSBrushImage *image = (GXPSBrushImage *)user_data; gxps_brush_image_free (image); } static GMarkupParser brush_image_parser = { brush_image_start_element, brush_image_end_element, NULL, NULL, brush_image_error }; static GXPSBrushVisual * gxps_brush_visual_new (GXPSBrush *brush, cairo_rectangle_t *viewport, cairo_rectangle_t *viewbox) { GXPSBrushVisual *visual; visual = g_slice_new0 (GXPSBrushVisual); visual->brush = brush; /* Default */ visual->extend = CAIRO_EXTEND_NONE; cairo_matrix_init_identity (&visual->matrix); /* Required values */ visual->viewport = *viewport; visual->viewbox = *viewbox; return visual; } static void gxps_brush_visual_free (GXPSBrushVisual *visual) { if (G_UNLIKELY (!visual)) return; g_slice_free (GXPSBrushVisual, visual); } static void brush_gradient_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSBrush *brush = (GXPSBrush *)user_data; if (strcmp (element_name, "LinearGradientBrush.GradientStops") == 0) { } else if (strcmp (element_name, "RadialGradientBrush.GradientStops") == 0) { } else if (strcmp (element_name, "GradientStop") == 0) { gint i; GXPSColor color; gboolean has_color = FALSE; gdouble offset = -1; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Color") == 0) { has_color = TRUE; if (!gxps_color_parse (values[i], brush->ctx->page->priv->zip, &color)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "GradientStop", "Color", values[i], error); return; } } else if (strcmp (names[i], "Offset") == 0) { if (!gxps_value_get_double (values[i], &offset)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "GradientStop", "Offset", values[i], error); return; } } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "GradientStop", names[i], NULL, error); return; } } if (!has_color || offset == -1) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, !has_color ? "Color" : "Offset", NULL, error); return; } cairo_pattern_add_color_stop_rgba (brush->pattern, offset, color.red, color.green, color.blue, color.alpha * brush->opacity); } } static GMarkupParser brush_gradient_parser = { brush_gradient_start_element, NULL, NULL, NULL }; static gboolean gxps_box_parse (const gchar *box, cairo_rectangle_t *rect) { gchar **tokens; gdouble b[4]; guint i; tokens = g_strsplit (box, ",", 4); if (g_strv_length (tokens) != 4) { g_strfreev (tokens); return FALSE; } for (i = 0; i < 4; i++) { if (!gxps_value_get_double (tokens[i], &b[i])) { g_strfreev (tokens); return FALSE; } } rect->x = b[0]; rect->y = b[1]; rect->width = b[2]; rect->height = b[3]; g_strfreev (tokens); return TRUE; } static void brush_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSBrush *brush = (GXPSBrush *)user_data; if (strcmp (element_name, "SolidColorBrush") == 0) { const gchar *color_str = NULL; gint i; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Color") == 0) { color_str = values[i]; } else if (strcmp (names[i], "Opacity") == 0) { if (!gxps_value_get_double (values[i], &brush->opacity)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "SolidColorBrush", "Opacity", values[i], error); return; } } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "SolidColorBrush", names[i], NULL, error); return; } } if (!color_str) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "SolidColorBrush", "Color", NULL, error); return; } GXPS_DEBUG (g_message ("set_fill_pattern (solid)")); if (!gxps_brush_solid_color_parse (color_str, brush->ctx->page->priv->zip, brush->opacity, &brush->pattern)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "SolidColorBrush", "Color", color_str, error); return; } } else if (strcmp (element_name, "ImageBrush") == 0) { GXPSBrushImage *image; gchar *image_source = NULL; cairo_rectangle_t viewport = { 0, }, viewbox = { 0, }; cairo_matrix_t matrix; cairo_extend_t extend = CAIRO_EXTEND_NONE; gint i; cairo_matrix_init_identity (&matrix); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "ImageSource") == 0) { image_source = gxps_resolve_relative_path (brush->ctx->page->priv->source, values[i]); } else if (strcmp (names[i], "Transform") == 0) { if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "ImageBrush", "Transform", values[i], error); return; } } else if (strcmp (names[i], "Viewport") == 0) { if (!gxps_box_parse (values[i], &viewport)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "ImageBrush", "Viewport", values[i], error); return; } } else if (strcmp (names[i], "ViewportUnits") == 0) { } else if (strcmp (names[i], "Viewbox") == 0) { if (!gxps_box_parse (values[i], &viewbox)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "ImageBrush", "Viewbox", values[i], error); return; } } else if (strcmp (names[i], "ViewboxUnits") == 0) { } else if (strcmp (names[i], "TileMode") == 0) { extend = gxps_tile_mode_parse (values[i]); } else if (strcmp (names[i], "Opacity") == 0) { if (!gxps_value_get_double (values[i], &brush->opacity)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "ImageBrush", "Opacity", values[i], error); return; } } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "ImageBrush", names[i], NULL, error); return; } } if (!image_source) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, "ImageSource", NULL, error); return; } /* GXPSBrushImage takes ownership of image_source */ image = gxps_brush_image_new (brush, image_source, &viewport, &viewbox); image->extend = extend; image->matrix = matrix; g_markup_parse_context_push (context, &brush_image_parser, image); } else if (strcmp (element_name, "LinearGradientBrush") == 0) { gint i; gdouble x0, y0, x1, y1; cairo_extend_t extend = CAIRO_EXTEND_PAD; cairo_matrix_t matrix; x0 = y0 = x1 = y1 = -1; cairo_matrix_init_identity (&matrix); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "MappingMode") == 0) { } else if (strcmp (names[i], "StartPoint") == 0) { if (!gxps_point_parse (values[i], &x0, &y0)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "LinearGradientBrush", "StartPoint", values[i], error); return; } } else if (strcmp (names[i], "EndPoint") == 0) { if (!gxps_point_parse (values[i], &x1, &y1)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "LinearGradientBrush", "EndPoint", values[i], error); return; } } else if (strcmp (names[i], "SpreadMethod") == 0) { extend = gxps_spread_method_parse (values[i]); } else if (strcmp (names[i], "Opacity") == 0) { if (!gxps_value_get_double (values[i], &brush->opacity)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "LinearGradientBrush", "Opacity", values[i], error); return; } } else if (strcmp (names[i], "Transform") == 0) { if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "LinearGradientBrush", "Transform", values[i], error); return; } } else if (strcmp (names[i], "ColorInterpolationMode") == 0) { GXPS_DEBUG (g_debug ("Unsupported %s attribute: ColorInterpolationMode", element_name)); } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, element_name, names[i], NULL, error); return; } } if (x0 == -1 || y0 == -1 || x1 == -1 || y1 == -1) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, (x0 == -1 || y0 == -1) ? "StartPoint" : "EndPoint", NULL, error); return; } GXPS_DEBUG (g_message ("set_fill_pattern (linear)")); brush->pattern = cairo_pattern_create_linear (x0, y0, x1, y1); cairo_pattern_set_matrix (brush->pattern, &matrix); cairo_pattern_set_extend (brush->pattern, extend); g_markup_parse_context_push (context, &brush_gradient_parser, brush); } else if (strcmp (element_name, "RadialGradientBrush") == 0) { gint i; gdouble cx0, cy0, r0, cx1, cy1, r1; cairo_extend_t extend = CAIRO_EXTEND_PAD; cairo_matrix_t matrix; cx0 = cy0 = r0 = cx1 = cy1 = r1 = -1; cairo_matrix_init_identity (&matrix); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "MappingMode") == 0) { } else if (strcmp (names[i], "GradientOrigin") == 0) { if (!gxps_point_parse (values[i], &cx0, &cy0)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "RadialGradientBrush", "GradientOrigin", values[i], error); return; } } else if (strcmp (names[i], "Center") == 0) { if (!gxps_point_parse (values[i], &cx1, &cy1)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "RadialGradientBrush", "Center", values[i], error); return; } } else if (strcmp (names[i], "RadiusX") == 0) { if (!gxps_value_get_double (values[i], &r0)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "RadialGradientBrush", "RadiusX", values[i], error); return; } } else if (strcmp (names[i], "RadiusY") == 0) { if (!gxps_value_get_double (values[i], &r1)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "RadialGradientBrush", "RadiusY", values[i], error); return; } } else if (strcmp (names[i], "SpreadMethod") == 0) { extend = gxps_spread_method_parse (values[i]); } else if (strcmp (names[i], "Opacity") == 0) { if (!gxps_value_get_double (values[i], &brush->opacity)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "RadialGradientBrush", "Opacity", values[i], error); return; } } else if (strcmp (names[i], "Transform") == 0) { if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "RadialGradientBrush", "Transform", values[i], error); return; } } else if (strcmp (names[i], "ColorInterpolationMode") == 0) { GXPS_DEBUG (g_debug ("Unsupported %s attribute: ColorInterpolationMode", element_name)); } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, element_name, names[i], NULL, error); return; } } if (cx0 == -1 || cy0 == -1 || cx1 == -1 || cy1 == -1) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, (cx0 == -1 || cy0 == -1) ? "GradientOrigin" : "Center", NULL, error); return; } if (r0 == -1 || r1 == -1) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, (r0 == -1) ? "RadiusX" : "RadiusY", NULL, error); return; } GXPS_DEBUG (g_message ("set_fill_pattern (radial)")); brush->pattern = cairo_pattern_create_radial (cx0, cy0, 0, cx1, cy1, r1); cairo_pattern_set_matrix (brush->pattern, &matrix); cairo_pattern_set_extend (brush->pattern, extend); g_markup_parse_context_push (context, &brush_gradient_parser, brush); } else if (strcmp (element_name, "VisualBrush") == 0) { GXPSBrushVisual *visual; GXPSRenderContext *sub_ctx; cairo_rectangle_t viewport = { 0, }, viewbox = { 0, }; cairo_matrix_t matrix; cairo_extend_t extend = CAIRO_EXTEND_NONE; double width, height; gint i; cairo_matrix_init_identity (&matrix); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "TileMode") == 0) { extend = gxps_tile_mode_parse (values[i]); } else if (strcmp (names[i], "Transform") == 0) { if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "VisualBrush", "Transform", values[i], error); return; } } else if (strcmp (names[i], "Viewport") == 0) { if (!gxps_box_parse (values[i], &viewport)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "VisualBrush", "Viewport", values[i], error); return; } } else if (strcmp (names[i], "ViewportUnits") == 0) { } else if (strcmp (names[i], "Viewbox") == 0) { if (!gxps_box_parse (values[i], &viewbox)) { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "VisualBrush", "Viewbox", values[i], error); return; } } else if (strcmp (names[i], "ViewboxUnits") == 0) { } else if (strcmp (names[i], "Opacity") == 0) { GXPS_DEBUG (g_debug ("Unsupported %s attribute: Opacity", element_name)); } else if (strcmp (names[i], "Visual") == 0) { GXPS_DEBUG (g_debug ("Unsupported %s attribute: Visual", element_name)); } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, element_name, names[i], NULL, error); return; } } /* TODO: check required values */ width = gxps_transform_hypot (&matrix, viewport.width, 0); height = gxps_transform_hypot (&matrix, 0, viewport.height); cairo_save (brush->ctx->cr); cairo_rectangle (brush->ctx->cr, 0, 0, width, height); cairo_clip (brush->ctx->cr); cairo_push_group (brush->ctx->cr); cairo_translate (brush->ctx->cr, -viewbox.x, -viewbox.y); cairo_scale (brush->ctx->cr, width / viewbox.width, height / viewbox.height); visual = gxps_brush_visual_new (brush, &viewport, &viewbox); visual->extend = extend; cairo_matrix_init (&visual->matrix, viewport.width / width, 0, 0, viewport.height / height, viewport.x, viewport.y); cairo_matrix_multiply (&visual->matrix, &visual->matrix, &matrix); cairo_matrix_invert (&visual->matrix); sub_ctx = g_slice_new0 (GXPSRenderContext); sub_ctx->page = brush->ctx->page; sub_ctx->cr = brush->ctx->cr; sub_ctx->visual = visual; gxps_page_render_parser_push (context, sub_ctx); } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void brush_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSBrush *brush = (GXPSBrush *)user_data; if (strcmp (element_name, "SolidColorBrush") == 0) { } else if (strcmp (element_name, "LinearGradientBrush") == 0) { g_markup_parse_context_pop (context); } else if (strcmp (element_name, "RadialGradientBrush") == 0) { g_markup_parse_context_pop (context); } else if (strcmp (element_name, "ImageBrush") == 0) { GXPSBrushImage *brush_image; GXPSImage *image; GError *err = NULL; brush_image = g_markup_parse_context_pop (context); GXPS_DEBUG (g_message ("set_fill_pattern (image)")); image = gxps_page_get_image (brush->ctx->page, brush_image->image_uri, &err); if (image) { cairo_matrix_t matrix; gdouble x_scale, y_scale; cairo_surface_t *clip_surface; /* viewbox units is 1/96 inch, convert to pixels */ brush_image->viewbox.x *= image->res_x / 96; brush_image->viewbox.y *= image->res_y / 96; brush_image->viewbox.width *= image->res_x / 96; brush_image->viewbox.height *= image->res_y / 96; clip_surface = cairo_surface_create_for_rectangle (image->surface, brush_image->viewbox.x, brush_image->viewbox.y, brush_image->viewbox.width, brush_image->viewbox.height); brush_image->brush->pattern = cairo_pattern_create_for_surface (clip_surface); cairo_pattern_set_extend (brush_image->brush->pattern, brush_image->extend); x_scale = brush_image->viewport.width / brush_image->viewbox.width; y_scale = brush_image->viewport.height / brush_image->viewbox.height; cairo_matrix_init (&matrix, x_scale, 0, 0, y_scale, brush_image->viewport.x, brush_image->viewport.y); cairo_matrix_multiply (&matrix, &matrix, &brush_image->matrix); cairo_matrix_invert (&matrix); cairo_pattern_set_matrix (brush_image->brush->pattern, &matrix); if (brush->opacity != 1.0) { cairo_push_group (brush->ctx->cr); cairo_set_source (brush->ctx->cr, brush_image->brush->pattern); cairo_pattern_destroy (brush_image->brush->pattern); cairo_paint_with_alpha (brush->ctx->cr, brush->opacity); brush_image->brush->pattern = cairo_pop_group (brush->ctx->cr); } if (cairo_pattern_status (brush_image->brush->pattern)) { GXPS_DEBUG (g_debug ("%s", cairo_status_to_string (cairo_pattern_status (brush_image->brush->pattern)))); cairo_pattern_destroy (brush_image->brush->pattern); brush_image->brush->pattern = NULL; } cairo_surface_destroy (clip_surface); } else if (err) { GXPS_DEBUG (g_debug ("%s", err->message)); g_error_free (err); } gxps_brush_image_free (brush_image); } else if (strcmp (element_name, "VisualBrush") == 0) { GXPSRenderContext *sub_ctx; GXPSBrushVisual *visual; cairo_matrix_t matrix; sub_ctx = g_markup_parse_context_pop (context); visual = sub_ctx->visual; g_slice_free (GXPSRenderContext, sub_ctx); GXPS_DEBUG (g_message ("set_fill_pattern (visual)")); visual->brush->pattern = cairo_pop_group (brush->ctx->cr); /* Undo the clip */ cairo_restore (brush->ctx->cr); cairo_pattern_set_extend (visual->brush->pattern, visual->extend); cairo_pattern_get_matrix (visual->brush->pattern, &matrix); cairo_matrix_multiply (&matrix, &visual->matrix, &matrix); cairo_pattern_set_matrix (visual->brush->pattern, &matrix); if (cairo_pattern_status (visual->brush->pattern)) { GXPS_DEBUG (g_debug ("%s", cairo_status_to_string (cairo_pattern_status (visual->brush->pattern)))); cairo_pattern_destroy (visual->brush->pattern); visual->brush->pattern = NULL; } gxps_brush_visual_free (visual); } else { gxps_parse_error (context, brush->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void brush_error (GMarkupParseContext *context, GError *error, gpointer user_data) { GXPSBrush *brush = (GXPSBrush *)user_data; gxps_brush_free (brush); } static GMarkupParser brush_parser = { brush_start_element, brush_end_element, NULL, NULL, brush_error }; void gxps_brush_parser_push (GMarkupParseContext *context, GXPSBrush *brush) { g_markup_parse_context_push (context, &brush_parser, brush); } 0707010000001D000081A40000000000000000000000016447D411000006AC000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-brush.h/* GXPSBrush * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_BRUSH_H__ #define __GXPS_BRUSH_H__ #include <cairo.h> #include "gxps-page-private.h" G_BEGIN_DECLS typedef struct _GXPSBrush GXPSBrush; struct _GXPSBrush { GXPSRenderContext *ctx; cairo_pattern_t *pattern; gdouble opacity; }; GXPSBrush *gxps_brush_new (GXPSRenderContext *ctx); void gxps_brush_free (GXPSBrush *brush); gboolean gxps_brush_solid_color_parse (const gchar *data, GXPSArchive *zip, gdouble alpha, cairo_pattern_t **pattern); void gxps_brush_parser_push (GMarkupParseContext *context, GXPSBrush *brush); G_END_DECLS #endif /* __GXPS_BRUSH_H__ */ 0707010000001E000081A40000000000000000000000016447D41100001F05000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-color.c/* GXPSColor * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <glib.h> #ifdef HAVE_LIBLCMS2 #include <lcms2.h> #endif #include "gxps-color.h" #include "gxps-error.h" #include "gxps-debug.h" #define ICC_PROFILE_CACHE_KEY "gxps-icc-profile-cache" #ifdef HAVE_LIBLCMS2 #ifdef GXPS_ENABLE_DEBUG static const gchar * get_color_space_string (cmsColorSpaceSignature color_space) { switch (color_space) { case cmsSigXYZData: return "XYZ"; case cmsSigLabData: return "Lab"; case cmsSigLuvData: return "Luv"; case cmsSigYCbCrData: return "YCbr"; case cmsSigYxyData: return "Yxy"; case cmsSigRgbData: return "RGB"; case cmsSigGrayData: return "GRAY"; case cmsSigHsvData: return "HSV"; case cmsSigHlsData: return "HLS"; case cmsSigCmykData: return "CMYK"; case cmsSigCmyData: return "CMY"; case cmsSigMCH1Data: return "MCH1"; case cmsSigMCH2Data: return "MCH2"; case cmsSigMCH3Data: return "MCH3"; case cmsSigMCH4Data: return "MCH4"; case cmsSigMCH5Data: return "MCH5"; case cmsSigMCH6Data: return "MCH6"; case cmsSigMCH7Data: return "MCH7"; case cmsSigMCH8Data: return "MCH8"; case cmsSigMCH9Data: return "MCH9"; case cmsSigMCHAData: return "MCHA"; case cmsSigMCHBData: return "MCHB"; case cmsSigMCHCData: return "MCHC"; case cmsSigMCHDData: return "MCHD"; case cmsSigMCHEData: return "MCHE"; case cmsSigMCHFData: return "MCHF"; case cmsSigNamedData: return "nmcl"; case cmsSig1colorData: return "1CLR"; case cmsSig2colorData: return "2CLR"; case cmsSig3colorData: return "3CLR"; case cmsSig4colorData: return "4CLR"; case cmsSig5colorData: return "5CLR"; case cmsSig6colorData: return "6CLR"; case cmsSig7colorData: return "7CLR"; case cmsSig8colorData: return "8CLR"; case cmsSig9colorData: return "9CLR"; case cmsSig10colorData: return "ACLR"; case cmsSig11colorData: return "BCLR"; case cmsSig12colorData: return "CCLR"; case cmsSig13colorData: return "DCLR"; case cmsSig14colorData: return "ECLR"; case cmsSig15colorData: return "FCLR"; case cmsSigLuvKData: return "LuvK"; default: g_assert_not_reached (); } return NULL; } #endif /* GXPS_ENABLE_DEBUG */ static cmsHPROFILE create_rgb_profile (gpointer args) { return cmsCreate_sRGBProfile (); } static cmsHPROFILE get_s_rgb_profile (void) { static GOnce once_init = G_ONCE_INIT; return g_once (&once_init, create_rgb_profile, NULL); } static gboolean gxps_color_new_for_icc_profile (cmsHPROFILE profile, gdouble *values, guint n_values, GXPSColor *color) { cmsHTRANSFORM transform; gdouble cmyk[4]; gdouble rgb[3]; if (cmsChannelsOf (cmsGetColorSpace (profile)) != n_values) return FALSE; if (cmsGetColorSpace (profile) != cmsSigCmykData) { GXPS_DEBUG (g_debug ("Unsupported color space %s", get_color_space_string (cmsGetColorSpace (profile)))); return FALSE; } cmyk[0] = CLAMP (values[0], 0., 1.) * 100.; cmyk[1] = CLAMP (values[1], 0., 1.) * 100.; cmyk[2] = CLAMP (values[2], 0., 1.) * 100.; cmyk[3] = CLAMP (values[3], 0., 1.) * 100.; transform = cmsCreateTransform (profile, TYPE_CMYK_DBL, get_s_rgb_profile (), TYPE_RGB_DBL, INTENT_PERCEPTUAL, 0); cmsDoTransform (transform, cmyk, rgb, 1); cmsDeleteTransform (transform); color->red = rgb[0]; color->green = rgb[1]; color->blue = rgb[2]; return TRUE; } static cmsHPROFILE gxps_color_create_icc_profile (GXPSArchive *zip, const gchar *icc_profile_uri) { cmsHPROFILE profile; guchar *profile_data; gsize profile_data_len; gboolean res; res = gxps_archive_read_entry (zip, icc_profile_uri, &profile_data, &profile_data_len, NULL); if (!res) { GXPS_DEBUG (g_debug ("ICC profile source %s not found in archive", icc_profile_uri)); return NULL; } profile = cmsOpenProfileFromMem (profile_data, profile_data_len); g_free (profile_data); if (!profile) { GXPS_DEBUG (g_debug ("Failed to load ICC profile %s", icc_profile_uri)); return NULL; } return profile; } #endif /* HAVE_LIBLCMS2 */ gboolean gxps_color_new_for_icc (GXPSArchive *zip, const gchar *icc_profile_uri, gdouble *values, guint n_values, GXPSColor *color) { #ifdef HAVE_LIBLCMS2 GHashTable *icc_cache; cmsHPROFILE profile; icc_cache = g_object_get_data (G_OBJECT (zip), ICC_PROFILE_CACHE_KEY); if (icc_cache) { profile = g_hash_table_lookup (icc_cache, icc_profile_uri); if (profile) return gxps_color_new_for_icc_profile (profile, values, n_values, color); } profile = gxps_color_create_icc_profile (zip, icc_profile_uri); if (!profile) return FALSE; if (!icc_cache) { icc_cache = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)cmsCloseProfile); g_object_set_data_full (G_OBJECT (zip), ICC_PROFILE_CACHE_KEY, icc_cache, (GDestroyNotify)g_hash_table_destroy); } g_hash_table_insert (icc_cache, g_strdup (icc_profile_uri), profile); return gxps_color_new_for_icc_profile (profile, values, n_values, color); #else return FALSE; #endif } 0707010000001F000081A40000000000000000000000016447D41100000591000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-color.h/* GXPSColor * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_COLOR_H__ #define __GXPS_COLOR_H__ #include <glib.h> #include "gxps-archive.h" G_BEGIN_DECLS #define GXPS_COLOR_MAX_CHANNELS 8 typedef struct _GXPSColor { gdouble alpha; gdouble red; gdouble green; gdouble blue; } GXPSColor; gboolean gxps_color_new_for_icc (GXPSArchive *zip, const gchar *icc_profile_uri, gdouble *values, guint n_values, GXPSColor *color); G_END_DECLS #endif /* __GXPS_COLOR_H__ */ 07070100000020000081A40000000000000000000000016447D41100006339000000000000000000000000000000000000002F00000000libgxps-0.3.2+5/libgxps/gxps-core-properties.c/* GXPSCoreProperties * * Copyright (C) 2013 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-core-properties.h" #include "gxps-private.h" #include "gxps-error.h" #include <string.h> #include <errno.h> /** * SECTION:gxps-core-properties * @Short_description: XPS Core Properties * @Title: GXPSCoreProperties * @See_also: #GXPSFile * * #GXPSCoreProperties represents the metadata of a #GXPSFile. * #GXPSCoreProperties objects can not be created directly, they * are retrieved from a #GXPSFile with gxps_file_get_core_properties(). * * Since: 0.2.3 */ enum { PROP_0, PROP_ARCHIVE, PROP_SOURCE }; struct _GXPSCorePropertiesPrivate { GXPSArchive *zip; gchar *source; gboolean initialized; GError *init_error; gchar *category; gchar *content_status; gchar *content_type; time_t created; gchar *creator; gchar *description; gchar *identifier; gchar *keywords; gchar *language; gchar *last_modified_by; time_t last_printed; time_t modified; gchar *revision; gchar *subject; gchar *title; gchar *version; }; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (GXPSCoreProperties, gxps_core_properties, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) /* CoreProperties parser */ typedef enum { CP_UNKNOWN, CP_CATEGORY, CP_CONTENT_STATUS, CP_CONTENT_TYPE, CP_CREATED, CP_CREATOR, CP_DESCRIPTION, CP_IDENTIFIER, CP_KEYWORDS, CP_LANGUAGE, CP_LAST_MODIFIED_BY, CP_LAST_PRINTED, CP_MODIFIED, CP_REVISION, CP_SUBJECT, CP_TITLE, CP_VERSION } CoreProperty; typedef struct _CorePropsParserData { GXPSCoreProperties *core_props; CoreProperty property; GString *buffer; } CorePropsParserData; static gchar * parse_int (const gchar *value, gint *int_value) { gint64 result; gchar *endptr = NULL; *int_value = -1; if (!value) return NULL; errno = 0; result = g_ascii_strtoll (value, &endptr, 10); if (errno || endptr == value || result > G_MAXINT || result < G_MININT) return NULL; *int_value = result; return endptr; } static gboolean parse_date (const gchar *date, gint *year, gint *mon, gint *day, gint *hour, gint *min, gint *sec, gint *tz) { gint value; const gchar *str = date; /* Year: * YYYY (eg 1997) * Year and month: * YYYY-MM (eg 1997-07) * Complete date: * YYYY-MM-DD (eg 1997-07-16) * Complete date plus hours and minutes: * YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00) * Complete date plus hours, minutes and seconds: * YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00) * Complete date plus hours, minutes, seconds and a decimal fraction of a second * YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00) * where: * * YYYY = four-digit year * MM = two-digit month (01=January, etc.) * DD = two-digit day of month (01 through 31) * hh = two digits of hour (00 through 23) (am/pm NOT allowed) * mm = two digits of minute (00 through 59) * ss = two digits of second (00 through 59) * s = one or more digits representing a decimal fraction of a second * TZD = time zone designator (Z or +hh:mm or -hh:mm) */ /* Year */ str = parse_int (str, &value); *year = value; if (!str) return value != -1; if (*str != '-') return FALSE; str++; /* Month */ str = parse_int (str, &value); *mon = value; if (!str) return value != -1; if (*str != '-') return FALSE; str++; /* Day */ str = parse_int (str, &value); *day = value; if (!str) return value != -1; if (*str != 'T') return FALSE; str++; /* Hour */ str = parse_int (str, &value); *hour = value; if (!str) return value != -1; if (*str != ':') return FALSE; str++; /* Minute */ str = parse_int (str, &value); *min = value; if (!str) return value != -1; if (*str != ':') return FALSE; str++; /* Second */ str = parse_int (str, &value); *sec = value; if (!str) return value != -1; /* Fraction of a second */ if (*str == '.') { str = parse_int (++str, &value); if (!str) return TRUE; } /* Time Zone */ if (*str == '+' || *str == '-') { gint tz_hour = -1, tz_min = -1; gint sign = *str == '+' ? 1 : -1; str++; str = parse_int (str, &value); if (!str) return value != -1; tz_hour = value; if (*str != ':') return FALSE; str++; str = parse_int (str, &value); if (!str) return value != -1; tz_min = value; *tz = (tz_hour * 3600 + tz_min * 60) * sign; } else if (*str == 'Z') { *tz = 0; } return TRUE; } static time_t w3cdtf_to_time_t (const gchar *date) { struct tm stm; gint year = -1, mon = -1, day = -1; gint hour = -1, min = -1, sec = -1; gint tz = 0; time_t retval; if (!parse_date (date, &year, &mon, &day, &hour, &min, &sec, &tz)) return (time_t)-1; stm.tm_year = year - 1900; stm.tm_mon = mon - 1; stm.tm_mday = day; stm.tm_hour = hour; stm.tm_min = min; stm.tm_sec = sec; stm.tm_isdst = -1; retval = mktime (&stm); if (retval == (time_t)-1) return retval; return retval + tz; } static void core_props_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { CorePropsParserData *data = (CorePropsParserData *)user_data; data->buffer = g_string_new (NULL); if (strcmp (element_name, "category") == 0) data->property = CP_CATEGORY; else if (strcmp (element_name, "contentStatus") == 0) data->property = CP_CONTENT_STATUS; else if (strcmp (element_name, "contentType") == 0) data->property = CP_CONTENT_TYPE; else if (strcmp (element_name, "dcterms:created") == 0) data->property = CP_CREATED; else if (strcmp (element_name, "dc:creator") == 0) data->property = CP_CREATOR; else if (strcmp (element_name, "dc:description") == 0) data->property = CP_DESCRIPTION; else if (strcmp (element_name, "dc:identifier") == 0) data->property = CP_IDENTIFIER; else if (strcmp (element_name, "keywords") == 0) data->property = CP_KEYWORDS; else if (strcmp (element_name, "dc:language") == 0) data->property = CP_LANGUAGE; else if (strcmp (element_name, "lastModifiedBy") == 0) data->property = CP_LAST_MODIFIED_BY; else if (strcmp (element_name, "lastPrinted") == 0) data->property = CP_LAST_PRINTED; else if (strcmp (element_name, "dcterms:modified") == 0) data->property = CP_MODIFIED; else if (strcmp (element_name, "revision") == 0) data->property = CP_REVISION; else if (strcmp (element_name, "dc:subject") == 0) data->property = CP_SUBJECT; else if (strcmp (element_name, "dc:title") == 0) data->property = CP_TITLE; else if (strcmp (element_name, "version") == 0) data->property = CP_VERSION; else if ((strcmp (element_name, "coreProperties") == 0) || (strcmp (element_name, "cp:coreProperties") == 0)) { /* Do nothing */ } else { gxps_parse_error (context, data->core_props->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void core_props_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { CorePropsParserData *data = (CorePropsParserData *)user_data; GXPSCorePropertiesPrivate *priv = data->core_props->priv; gchar *text; gsize text_len; if (!data->buffer) return; text_len = data->buffer->len; text = g_string_free (data->buffer, FALSE); data->buffer = NULL; switch (data->property) { case CP_CATEGORY: priv->category = g_strndup (text, text_len); break; case CP_CONTENT_STATUS: priv->content_status = g_strndup (text, text_len); break; case CP_CONTENT_TYPE: priv->content_type = g_strndup (text, text_len); break; case CP_CREATED: priv->created = w3cdtf_to_time_t (text); break; case CP_CREATOR: priv->creator = g_strndup (text, text_len); break; case CP_DESCRIPTION: priv->description = g_strndup (text, text_len); break; case CP_IDENTIFIER: priv->identifier = g_strndup (text, text_len); break; case CP_KEYWORDS: priv->keywords = g_strndup (text, text_len); break; case CP_LANGUAGE: priv->language = g_strndup (text, text_len); break; case CP_LAST_MODIFIED_BY: priv->last_modified_by = g_strndup (text, text_len); break; case CP_LAST_PRINTED: priv->last_printed = w3cdtf_to_time_t (text); break; case CP_MODIFIED: priv->modified = w3cdtf_to_time_t (text); break; case CP_REVISION: priv->revision = g_strndup (text, text_len); break; case CP_SUBJECT: priv->subject = g_strndup (text, text_len); break; case CP_TITLE: priv->title = g_strndup (text, text_len); break; case CP_VERSION: priv->version = g_strndup (text, text_len); break; case CP_UNKNOWN: break; } data->property = CP_UNKNOWN; g_free (text); } static void core_props_text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { CorePropsParserData *data = (CorePropsParserData *)user_data; if (!data->buffer) return; g_string_append_len (data->buffer, text, text_len); } static const GMarkupParser core_props_parser = { core_props_start_element, core_props_end_element, core_props_text, NULL, NULL }; static gboolean gxps_core_properties_parse (GXPSCoreProperties *core_props, GError **error) { GInputStream *stream; GMarkupParseContext *ctx; CorePropsParserData parser_data; stream = gxps_archive_open (core_props->priv->zip, core_props->priv->source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "CoreProperties source %s not found in archive", core_props->priv->source); return FALSE; } parser_data.core_props = core_props; parser_data.property = 0; parser_data.buffer = NULL; ctx = g_markup_parse_context_new (&core_props_parser, 0, &parser_data, NULL); gxps_parse_stream (ctx, stream, error); g_object_unref (stream); g_markup_parse_context_free (ctx); return (*error != NULL) ? FALSE : TRUE; } static void gxps_core_properties_finalize (GObject *object) { GXPSCoreProperties *core_props = GXPS_CORE_PROPERTIES (object); g_clear_object (&core_props->priv->zip); g_clear_pointer (&core_props->priv->source, g_free); g_clear_error (&core_props->priv->init_error); G_OBJECT_CLASS (gxps_core_properties_parent_class)->finalize (object); } static void gxps_core_properties_init (GXPSCoreProperties *core_props) { core_props->priv = G_TYPE_INSTANCE_GET_PRIVATE (core_props, GXPS_TYPE_CORE_PROPERTIES, GXPSCorePropertiesPrivate); } static void gxps_core_properties_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GXPSCoreProperties *core_props = GXPS_CORE_PROPERTIES (object); switch (prop_id) { case PROP_ARCHIVE: core_props->priv->zip = g_value_dup_object (value); break; case PROP_SOURCE: core_props->priv->source = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gxps_core_properties_class_init (GXPSCorePropertiesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = gxps_core_properties_set_property; object_class->finalize = gxps_core_properties_finalize; g_object_class_install_property (object_class, PROP_ARCHIVE, g_param_spec_object ("archive", "Archive", "The archive", GXPS_TYPE_ARCHIVE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SOURCE, g_param_spec_string ("source", "Source", "The Core Properties Source File", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (GXPSCorePropertiesPrivate)); } static gboolean gxps_core_properties_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { GXPSCoreProperties *core_props = GXPS_CORE_PROPERTIES (initable); if (core_props->priv->initialized) { if (core_props->priv->init_error) { g_propagate_error (error, g_error_copy (core_props->priv->init_error)); return FALSE; } return TRUE; } core_props->priv->initialized = TRUE; if (!gxps_core_properties_parse (core_props, &core_props->priv->init_error)) { g_propagate_error (error, g_error_copy (core_props->priv->init_error)); return FALSE; } return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = gxps_core_properties_initable_init; } GXPSCoreProperties * _gxps_core_properties_new (GXPSArchive *zip, const gchar *source, GError **error) { return g_initable_new (GXPS_TYPE_CORE_PROPERTIES, NULL, error, "archive", zip, "source", source, NULL); } /** * gxps_core_properties_get_title: * @core_props: a #GXPSCoreProperties * * Get the title. * * Returns: a string containing the title or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_title (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->title; } /** * gxps_core_properties_get_creator: * @core_props: a #GXPSCoreProperties * * Get the creator. * * Returns: a string containing the creator or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_creator (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->creator; } /** * gxps_core_properties_get_description: * @core_props: a #GXPSCoreProperties * * Get the description. * * Returns: a string containing the description or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_description (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->description; } /** * gxps_core_properties_get_subject: * @core_props: a #GXPSCoreProperties * * Get the subject. * * Returns: a string containing the subject or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_subject (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->subject; } /** * gxps_core_properties_get_keywords: * @core_props: a #GXPSCoreProperties * * Get the keywords. * * Returns: a string containing the keywords or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_keywords (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->keywords; } /** * gxps_core_properties_get_version: * @core_props: a #GXPSCoreProperties * * Get the version number. * * Returns: a string containing the version number or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_version (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->version; } /** * gxps_core_properties_get_revision: * @core_props: a #GXPSCoreProperties * * Get the revision number. * * Returns: a string containing the revision number or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_revision (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->revision; } /** * gxps_core_properties_get_identifier: * @core_props: a #GXPSCoreProperties * * Get the unique identifier. * * Returns: a string containing the identifier or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_identifier (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->identifier; } /** * gxps_core_properties_get_language: * @core_props: a #GXPSCoreProperties * * Get the language. * * Returns: a string containing the language or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_language (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->language; } /** * gxps_core_properties_get_category: * @core_props: a #GXPSCoreProperties * * Get the category. * * Returns: a string containing the category or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_category (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->category; } /** * gxps_core_properties_get_content_status: * @core_props: a #GXPSCoreProperties * * Get the status of the content (e.g. Draft, Reviewed, Final) * * Returns: a string containing the status of the content or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_content_status (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->content_status; } /** * gxps_core_properties_get_content_type: * @core_props: a #GXPSCoreProperties * * Get the type of content represented, generally defined by a * specific use and intended audience. This is not the MIME-Type. * * Returns: a string containing the type of content or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_content_type (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->content_type; } /** * gxps_core_properties_get_created: * @core_props: a #GXPSCoreProperties * * Get the creating date. * * Returns: the creating date as a <type>time_t</type> or -1. * * Since: 0.2.3 */ time_t gxps_core_properties_get_created (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), -1); return core_props->priv->created; } /** * gxps_core_properties_get_last_modified_by: * @core_props: a #GXPSCoreProperties * * Get the user who performed the last modification. * * Returns: a string containing the user who performed the * last modification or %NULL * * Since: 0.2.3 */ const gchar * gxps_core_properties_get_last_modified_by (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), NULL); return core_props->priv->last_modified_by; } /** * gxps_core_properties_get_modified: * @core_props: a #GXPSCoreProperties * * Get the last modification date. * * Returns: the modification date as a <type>time_t</type> or -1. * * Since: 0.2.3 */ time_t gxps_core_properties_get_modified (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), -1); return core_props->priv->modified; } /** * gxps_core_properties_get_last_printed: * @core_props: a #GXPSCoreProperties * * Get the date of the last printing. * * Returns: the date of the last printing as a <type>time_t</type> or -1. * * Since: 0.2.3 */ time_t gxps_core_properties_get_last_printed (GXPSCoreProperties *core_props) { g_return_val_if_fail (GXPS_IS_CORE_PROPERTIES (core_props), -1); return core_props->priv->last_printed; } 07070100000021000081A40000000000000000000000016447D411000010AE000000000000000000000000000000000000002F00000000libgxps-0.3.2+5/libgxps/gxps-core-properties.h/* GXPSCoreProperties * * Copyright (C) 2013 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_CORE_PROPERTIES_H__ #define __GXPS_CORE_PROPERTIES_H__ #include <glib-object.h> #include <libgxps/gxps-version.h> G_BEGIN_DECLS #define GXPS_TYPE_CORE_PROPERTIES (gxps_core_properties_get_type ()) #define GXPS_CORE_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_CORE_PROPERTIES, GXPSCoreProperties)) #define GXPS_IS_CORE_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_CORE_PROPERTIES)) #define GXPS_CORE_PROPERTIES_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_CORE_PROPERTIES, GXPSCorePropertiesClass)) #define GXPS_IS_CORE_PROPERTIES_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_CORE_PROPERTIES)) #define GXPS_CORE_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_CORE_PROPERTIES, GXPSCorePropertiesClass)) typedef struct _GXPSCoreProperties GXPSCoreProperties; typedef struct _GXPSCorePropertiesClass GXPSCorePropertiesClass; typedef struct _GXPSCorePropertiesPrivate GXPSCorePropertiesPrivate; /** * GXPSCoreProperties: * * The <structname>GXPSCoreProperties</structname> struct contains * only private fields and should not be directly accessed. */ struct _GXPSCoreProperties { GObject parent; /*< private >*/ GXPSCorePropertiesPrivate *priv; }; struct _GXPSCorePropertiesClass { GObjectClass parent_class; }; GXPS_AVAILABLE_IN_ALL GType gxps_core_properties_get_type (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_title (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_creator (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_description (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_subject (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_keywords (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_version (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_revision (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_identifier (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_language (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_category (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_content_status (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_content_type (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL time_t gxps_core_properties_get_created (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL const gchar *gxps_core_properties_get_last_modified_by (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL time_t gxps_core_properties_get_modified (GXPSCoreProperties *core_props); GXPS_AVAILABLE_IN_ALL time_t gxps_core_properties_get_last_printed (GXPSCoreProperties *core_props); G_END_DECLS #endif /* __GXPS_CORE_PROPERTIES_H__ */ 07070100000022000081A40000000000000000000000016447D411000004AF000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-debug.c/* * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gxps-debug.h" #ifdef GXPS_ENABLE_DEBUG gboolean gxps_debug_enabled (void) { static gboolean debug_enabled = FALSE; static gsize initialized; if (g_once_init_enter (&initialized)) { debug_enabled = g_getenv ("GXPS_DEBUG") != NULL; g_once_init_leave (&initialized, 1); } return debug_enabled; } #endif 07070100000023000081A40000000000000000000000016447D41100000511000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-debug.h/* * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_DEBUG_H__ #define __GXPS_DEBUG_H__ #include <glib.h> #include "gxps-version.h" G_BEGIN_DECLS #ifdef GXPS_ENABLE_DEBUG GXPS_VAR gboolean gxps_debug_enabled (void); #define GXPS_DEBUG(action) G_STMT_START { \ if (gxps_debug_enabled ()) \ action; \ } G_STMT_END #else #define GXPS_DEBUG(action) #endif G_END_DECLS #endif /* __GXPS_DEBUG_H__ */ 07070100000024000081A40000000000000000000000016447D411000038DE000000000000000000000000000000000000003200000000libgxps-0.3.2+5/libgxps/gxps-document-structure.c/* GXPSDocumentStructure * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <string.h> #include "gxps-archive.h" #include "gxps-private.h" #include "gxps-error.h" /** * SECTION:gxps-document-structure * @Short_description: Structure of XPS Document * @Title: GXPSDocumentStructure * @See_also: #GXPSDocument, #GXPSLinkTarget * * #GXPSDocumentStructure represents the structural organization of * a XPS document. A #GXPSDocumentStructure can contain the document * outline, similar to a table of contents, containing hyperlinks. * To iterate over the outline items you can use #GXPSOutlineIter. * * #GXPSDocumentStructure objects can not be created directly, they * are retrieved from a #GXPSDocument with gxps_document_get_structure(). */ enum { PROP_0, PROP_ARCHIVE, PROP_SOURCE }; struct _GXPSDocumentStructurePrivate { GXPSArchive *zip; gchar *source; /* Outline */ GList *outline; }; typedef struct _OutlineNode OutlineNode; struct _OutlineNode { gchar *desc; gchar *target; guint level; OutlineNode *parent; GList *children; }; G_DEFINE_TYPE (GXPSDocumentStructure, gxps_document_structure, G_TYPE_OBJECT) static void outline_node_free (OutlineNode *node) { if (G_UNLIKELY (!node)) return; g_free (node->desc); g_free (node->target); g_list_free_full (node->children, (GDestroyNotify)outline_node_free); g_slice_free (OutlineNode, node); } static void gxps_document_structure_finalize (GObject *object) { GXPSDocumentStructure *structure = GXPS_DOCUMENT_STRUCTURE (object); g_clear_object (&structure->priv->zip); g_clear_pointer (&structure->priv->source, g_free); g_list_free_full (structure->priv->outline, (GDestroyNotify)outline_node_free); structure->priv->outline = NULL; G_OBJECT_CLASS (gxps_document_structure_parent_class)->finalize (object); } static void gxps_document_structure_init (GXPSDocumentStructure *structure) { structure->priv = G_TYPE_INSTANCE_GET_PRIVATE (structure, GXPS_TYPE_DOCUMENT_STRUCTURE, GXPSDocumentStructurePrivate); } static void gxps_document_structure_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GXPSDocumentStructure *structure = GXPS_DOCUMENT_STRUCTURE (object); switch (prop_id) { case PROP_ARCHIVE: structure->priv->zip = g_value_dup_object (value); break; case PROP_SOURCE: structure->priv->source = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gxps_document_structure_class_init (GXPSDocumentStructureClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = gxps_document_structure_set_property; object_class->finalize = gxps_document_structure_finalize; g_object_class_install_property (object_class, PROP_ARCHIVE, g_param_spec_object ("archive", "Archive", "The document archive", GXPS_TYPE_ARCHIVE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SOURCE, g_param_spec_string ("source", "Source", "The DocStructure Source File", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (GXPSDocumentStructurePrivate)); } GXPSDocumentStructure * _gxps_document_structure_new (GXPSArchive *zip, const gchar *source) { GXPSDocumentStructure *structure; structure = g_object_new (GXPS_TYPE_DOCUMENT_STRUCTURE, "archive", zip, "source", source, NULL); return GXPS_DOCUMENT_STRUCTURE (structure); } /* Document Outline */ typedef struct { GXPSDocumentStructure *structure; guint level; GList *prevs; GList *outline; } GXPSOutlineContext; static void outline_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSOutlineContext *ctx = (GXPSOutlineContext *)user_data; if (strcmp (element_name, "DocumentOutline") == 0) { } else if (strcmp (element_name, "OutlineEntry") == 0) { gint i; gint level = 1; const gchar *desc = NULL; const gchar *target = NULL; OutlineNode *node; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "OutlineLevel") == 0) { if (!gxps_value_get_int (values[i], &level)) level = 1; } else if (strcmp (names[i], "Description") == 0) { desc = values[i]; } else if (strcmp (names[i], "OutlineTarget") == 0) { target = values[i]; } else if (strcmp (names[i], "xml:lang") == 0) { /* TODO */ } } if (!desc || !target) { gxps_parse_error (context, ctx->structure->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, !desc ? "Description" : "OutlineTarget", NULL, error); return; } node = g_slice_new0 (OutlineNode); node->desc = g_strdup (desc); node->level = level; node->target = gxps_resolve_relative_path (ctx->structure->priv->source, target); if (ctx->level < level) { /* Higher level, make previous the parent of current one */ node->parent = ctx->prevs ? (OutlineNode *)ctx->prevs->data : NULL; } else if (ctx->level > level) { /* Lower level, pop previous nodes, reordering children list, * until previous node of the same level is found */ while (ctx->prevs) { OutlineNode *prev = (OutlineNode *)ctx->prevs->data; ctx->prevs = g_list_delete_link (ctx->prevs, ctx->prevs); prev->children = g_list_reverse (prev->children); if (prev->level == level) { node->parent = prev->parent; break; } } g_assert (level == 1 || (level > 1 && node->parent != NULL)); } else if (ctx->level == level) { /* Same level, parent must be the same as the previous node */ node->parent = (ctx->prevs) ? ((OutlineNode *)ctx->prevs->data)->parent : NULL; ctx->prevs = g_list_delete_link (ctx->prevs, ctx->prevs); } if (level == 1) { ctx->outline = g_list_prepend (ctx->outline, node); } else { g_assert (node->parent != NULL); node->parent->children = g_list_prepend (node->parent->children, node); } ctx->prevs = g_list_prepend (ctx->prevs, node); ctx->level = level; } } static void outline_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSOutlineContext *ctx = (GXPSOutlineContext *)user_data; if (strcmp (element_name, "DocumentOutline") == 0) { /* Reorder children of pending node and remove them */ while (ctx->prevs) { OutlineNode *prev = (OutlineNode *)ctx->prevs->data; ctx->prevs = g_list_delete_link (ctx->prevs, ctx->prevs); prev->children = g_list_reverse (prev->children); } ctx->outline = g_list_reverse (ctx->outline); } else if (strcmp (element_name, "OutlineEntry") == 0) { } } static const GMarkupParser outline_parser = { outline_start_element, outline_end_element, NULL, NULL, NULL }; static GList * gxps_document_structure_parse_outline (GXPSDocumentStructure *structure, GError **error) { GInputStream *stream; GXPSOutlineContext ctx; GMarkupParseContext *context; stream = gxps_archive_open (structure->priv->zip, structure->priv->source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Document Structure source %s not found in archive", structure->priv->source); return NULL; } ctx.structure = structure; ctx.prevs = NULL; ctx.level = 0; ctx.outline = NULL; context = g_markup_parse_context_new (&outline_parser, 0, &ctx, NULL); gxps_parse_stream (context, stream, error); g_object_unref (stream); g_markup_parse_context_free (context); return ctx.outline; } /* Try to know ASAP whether document has an outline */ static void check_outline_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { gboolean *has_outline = (gboolean *)user_data; if (*has_outline == TRUE) return; if (strcmp (element_name, "DocumentStructure.Outline") == 0) *has_outline = TRUE; } static const GMarkupParser check_outline_parser = { check_outline_start_element, NULL, NULL, NULL, NULL }; /** * gxps_document_structure_has_outline: * @structure: a #GXPSDocumentStructure * * Whether @structure has an outline or not. * * Returns: %TRUE if @structure has an outline, %FALSE otherwise. */ gboolean gxps_document_structure_has_outline (GXPSDocumentStructure *structure) { GInputStream *stream; GMarkupParseContext *context; gboolean retval = FALSE; stream = gxps_archive_open (structure->priv->zip, structure->priv->source); if (!stream) return FALSE; context = g_markup_parse_context_new (&check_outline_parser, 0, &retval, NULL); gxps_parse_stream (context, stream, NULL); g_object_unref (stream); g_markup_parse_context_free (context); return retval; } typedef struct { GXPSDocumentStructure *structure; GList *current; } OutlineIter; /** * gxps_document_structure_outline_iter_init: * @iter: an uninitialized #GXPSOutlineIter * @structure: a #GXPSDocumentStructure * * Initializes @iter to the root item of the outline contained by @structure * and a associates it with @structure. * * Here is a simple example of some code that walks the full outline: * * <informalexample><programlisting> * static void * walk_outline (GXPSOutlineIter *iter) * { * do { * GXPSOutlineIter child_iter; * const gchar *description = gxps_outline_iter_get_description (iter); * GXPSLinkTarget *target = gxps_outline_iter_get_target (iter); * * /<!-- -->* Do something with description and taregt *<!-- -->/ * if (gxps_outline_iter_children (&child_iter, iter)) * walk_outline (&child_iter); * } while (gxps_outline_iter_next (iter)); * } * ... * { * GXPSOutlineIter iter; * if (gxps_document_structure_outline_iter_init (&iter, structure)) * walk_outline (&iter); * } * </programlisting></informalexample> * * Returns: %TRUE if @iter was successfully initialized to the root item, * %FALSE if it failed or @structure does not have an outline. */ gboolean gxps_document_structure_outline_iter_init (GXPSOutlineIter *iter, GXPSDocumentStructure *structure) { OutlineIter *oi = (OutlineIter *)iter; g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (GXPS_IS_DOCUMENT_STRUCTURE (structure), FALSE); oi->structure = structure; if (!structure->priv->outline) structure->priv->outline = gxps_document_structure_parse_outline (structure, NULL); oi->current = structure->priv->outline; return oi->current != NULL; } /** * gxps_outline_iter_next: * @iter: an initialized #GXPSOutlineIter * * Advances @iter to the next item at the current level. * See gxps_document_structure_outline_iter_init() for * more details. * * Returns: %TRUE if @iter was set to the next item, * %FALSE if the end of the current level has been reached */ gboolean gxps_outline_iter_next (GXPSOutlineIter *iter) { OutlineIter *oi = (OutlineIter *)iter; if (!oi->current) return FALSE; oi->current = g_list_next (oi->current); return oi->current != NULL; } /** * gxps_outline_iter_children: * @iter: an uninitialized #GXPSOutlineIter * @parent: an initialized #GXPSOutlineIter * * Initializes @iter to the first child item of @parent. * See gxps_document_structure_outline_iter_init() for * more details. * * Returns: %TRUE if @iter was set to the first child of @parent, * %FALSE if @parent does not have children. */ gboolean gxps_outline_iter_children (GXPSOutlineIter *iter, GXPSOutlineIter *parent) { OutlineIter *oi = (OutlineIter *)parent; OutlineIter *retval = (OutlineIter *)iter; OutlineNode *node; g_assert (oi->current != NULL); node = (OutlineNode *)oi->current->data; if (!node->children) return FALSE; retval->structure = oi->structure; retval->current = node->children; return TRUE; } /** * gxps_outline_iter_get_description: * @iter: an initialized #GXPSOutlineIter * * Gets the description of the outline item associated with @iter. * See gxps_document_structure_outline_iter_init() for * more details. * * Returns: the description of the outline item */ const gchar * gxps_outline_iter_get_description (GXPSOutlineIter *iter) { OutlineIter *oi = (OutlineIter *)iter; OutlineNode *node; g_assert (oi->current != NULL); node = (OutlineNode *)oi->current->data; return node->desc; } /** * gxps_outline_iter_get_target: * @iter: an initialized #GXPSOutlineIter * * Gets the #GXPSLinkTarget of the outline item associated with @iter. * See gxps_document_structure_outline_iter_init() for * more details. * * Returns: a new allocated #GXPSLinkTarget. * Free the returned object with gxps_link_target_free(). */ GXPSLinkTarget * gxps_outline_iter_get_target (GXPSOutlineIter *iter) { OutlineIter *oi = (OutlineIter *)iter; OutlineNode *node; g_assert (oi->current != NULL); node = (OutlineNode *)oi->current->data; return _gxps_link_target_new (oi->structure->priv->zip, node->target); } 07070100000025000081A40000000000000000000000016447D41100000E22000000000000000000000000000000000000003200000000libgxps-0.3.2+5/libgxps/gxps-document-structure.h/* GXPSDocumentStructure * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_DOCUMENT_STRUCTURE_H__ #define __GXPS_DOCUMENT_STRUCTURE_H__ #include <glib-object.h> #include "gxps-links.h" G_BEGIN_DECLS #define GXPS_TYPE_DOCUMENT_STRUCTURE (gxps_document_structure_get_type ()) #define GXPS_DOCUMENT_STRUCTURE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_DOCUMENT_STRUCTURE, GXPSDocumentStructure)) #define GXPS_DOCUMENT_STRUCTURE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_DOCUMENT_STRUCTURE, GXPSDocumentClassStructure)) #define GXPS_IS_DOCUMENT_STRUCTURE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_DOCUMENT_STRUCTURE)) #define GXPS_IS_DOCUMENT_STRUCTURE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_DOCUMENT_STRUCTURE)) #define GXPS_DOCUMENT_STRUCTURE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_DOCUMENT_STRUCTURE, GXPSDocumentStructureClass)) typedef struct _GXPSDocumentStructure GXPSDocumentStructure; typedef struct _GXPSDocumentStructureClass GXPSDocumentStructureClass; typedef struct _GXPSDocumentStructurePrivate GXPSDocumentStructurePrivate; /** * GXPSDocumentStructure: * * The <structname>GXPSDocumentStructure</structname> struct contains * only private fields and should not be directly accessed. */ struct _GXPSDocumentStructure { GObject parent; /*< private >*/ GXPSDocumentStructurePrivate *priv; }; struct _GXPSDocumentStructureClass { GObjectClass parent_class; }; /** * GXPSOutlineIter: * * GXPSOutlineIter represents an iterator that can be * used to iterate over the items of an outline * contained in a #GXPSDocumentStructure */ typedef struct _GXPSOutlineIter GXPSOutlineIter; struct _GXPSOutlineIter { /*< private >*/ gpointer dummy1; gpointer dummy2; }; GXPS_AVAILABLE_IN_ALL GType gxps_document_structure_get_type (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL gboolean gxps_document_structure_has_outline (GXPSDocumentStructure *structure); GXPS_AVAILABLE_IN_ALL gboolean gxps_document_structure_outline_iter_init (GXPSOutlineIter *iter, GXPSDocumentStructure *structure); GXPS_AVAILABLE_IN_ALL gboolean gxps_outline_iter_next (GXPSOutlineIter *iter); GXPS_AVAILABLE_IN_ALL gboolean gxps_outline_iter_children (GXPSOutlineIter *iter, GXPSOutlineIter *parent); GXPS_AVAILABLE_IN_ALL const gchar *gxps_outline_iter_get_description (GXPSOutlineIter *iter); GXPS_AVAILABLE_IN_ALL GXPSLinkTarget *gxps_outline_iter_get_target (GXPSOutlineIter *iter); G_END_DECLS #endif /* __GXPS_DOCUMENT_STRUCTURE_H__ */ 07070100000026000081A40000000000000000000000016447D411000039D2000000000000000000000000000000000000002800000000libgxps-0.3.2+5/libgxps/gxps-document.c/* GXPSDocument * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <stdlib.h> #include <string.h> #include "gxps-document.h" #include "gxps-archive.h" #include "gxps-links.h" #include "gxps-private.h" #include "gxps-error.h" /** * SECTION:gxps-document * @Short_description: XPS Documents * @Title: GXPSDocument * @See_also: #GXPSFile, #GXPSPage, #GXPSDocumentStructure * * #GXPSDocument represents a document in a #GXPSFile. #GXPSDocument * objects can not be created directly, they are retrieved from a * #GXPSFile with gxps_file_get_document(). */ enum { PROP_0, PROP_ARCHIVE, PROP_SOURCE }; typedef struct _Page { gchar *source; gint width; gint height; GList *links; } Page; struct _GXPSDocumentPrivate { GXPSArchive *zip; gchar *source; gboolean has_rels; gchar *structure; gboolean initialized; GError *init_error; Page **pages; guint n_pages; }; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (GXPSDocument, gxps_document, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) #define REL_DOCUMENT_STRUCTURE "http://schemas.microsoft.com/xps/2005/06/documentstructure" static Page * page_new (void) { return g_slice_new0 (Page); } static void page_free (Page *page) { g_free (page->source); g_list_foreach (page->links, (GFunc)g_free, NULL); g_list_free (page->links); g_slice_free (Page, page); } /* FixedDoc parser */ typedef struct _FixedDocParserData { GXPSDocument *doc; Page *page; guint n_pages; GList *pages; } FixedDocParserData; static void fixed_doc_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { FixedDocParserData *data = (FixedDocParserData *)user_data; gint i; if (strcmp (element_name, "PageContent") == 0) { gchar *source = NULL; gdouble width = -1, height = -1; for (i = 0; names[i]; i++) { if (strcmp (names[i], "Source") == 0) { source = gxps_resolve_relative_path (data->doc->priv->source, values[i]); } else if (strcmp (names[i], "Width") == 0) { if (!gxps_value_get_double_positive (values[i], &width)) width = 0; } else if (strcmp (names[i], "Height") == 0) { if (!gxps_value_get_double_positive (values[i], &height)) height = 0; } } if (!source) { gxps_parse_error (context, data->doc->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, "Source", NULL, error); return; } data->page = page_new (); data->page->source = source; data->page->width = width; data->page->height = height; } else if (strcmp (element_name, "LinkTarget") == 0) { if (!data->page) { /* TODO: error */ return; } for (i = 0; names[i]; i++) { if (strcmp (names[i], "Name") == 0) { data->page->links = g_list_prepend (data->page->links, g_strdup (values[i])); } } } else if (strcmp (element_name, "PageContent.LinkTargets") == 0) { } else if (strcmp (element_name, "FixedDocument") == 0) { /* Nothing to do */ } else { gxps_parse_error (context, data->doc->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void fixed_doc_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { FixedDocParserData *data = (FixedDocParserData *)user_data; if (strcmp (element_name, "PageContent") == 0) { data->n_pages++; data->pages = g_list_prepend (data->pages, data->page); data->page = NULL; } else if (strcmp (element_name, "PageContent.LinkTargets") == 0) { if (!data->page) { /* TODO: error */ return; } data->page->links = g_list_reverse (data->page->links); } else if (strcmp (element_name, "FixedDocument") == 0) { GList *l; data->doc->priv->n_pages = data->n_pages; if (data->doc->priv->n_pages > 0) { data->doc->priv->pages = g_new (Page *, data->n_pages); for (l = data->pages; l; l = g_list_next (l)) data->doc->priv->pages[--data->n_pages] = (Page *)l->data; } g_list_free (data->pages); } else if (strcmp (element_name, "LinkTarget") == 0) { /* Do Nothing */ } else { gxps_parse_error (context, data->doc->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static const GMarkupParser fixed_doc_parser = { fixed_doc_start_element, fixed_doc_end_element, NULL, NULL, NULL }; static gboolean gxps_document_parse_fixed_doc (GXPSDocument *doc, GError **error) { GInputStream *stream; GMarkupParseContext *ctx; FixedDocParserData *parser_data; stream = gxps_archive_open (doc->priv->zip, doc->priv->source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Document source %s not found in archive", doc->priv->source); return FALSE; } parser_data = g_new0 (FixedDocParserData, 1); parser_data->doc = doc; ctx = g_markup_parse_context_new (&fixed_doc_parser, 0, parser_data, NULL); gxps_parse_stream (ctx, stream, error); g_object_unref (stream); g_free (parser_data); g_markup_parse_context_free (ctx); return (*error != NULL) ? FALSE : TRUE; } static void doc_rels_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSDocument *doc = GXPS_DOCUMENT (user_data); if (strcmp (element_name, "Relationship") == 0) { const gchar *type = NULL; const gchar *target = NULL; gint i; for (i = 0; names[i]; i++) { if (strcmp (names[i], "Type") == 0) { type = values[i]; } else if (strcmp (names[i], "Target") == 0) { target = values[i]; } else if (strcmp (names[i], "Id") == 0) { /* Ignore ids for now */ } } if (g_strcmp0 (type, REL_DOCUMENT_STRUCTURE) == 0) { doc->priv->structure = target ? gxps_resolve_relative_path (doc->priv->source, target) : NULL; } } } static const GMarkupParser doc_rels_parser = { doc_rels_start_element, NULL, NULL, NULL, NULL }; static gboolean gxps_document_parse_rels (GXPSDocument *doc, GError **error) { GInputStream *stream; GMarkupParseContext *ctx; gchar *filename; gchar *rels, *doc_rels; gboolean retval; if (!doc->priv->has_rels) return FALSE; filename = g_path_get_basename (doc->priv->source); rels = g_strconcat ("_rels/", filename, ".rels", NULL); doc_rels = gxps_resolve_relative_path (doc->priv->source, rels); g_free (filename); g_free (rels); stream = gxps_archive_open (doc->priv->zip, doc_rels); if (!stream) { doc->priv->has_rels = FALSE; g_free (doc_rels); return FALSE; } ctx = g_markup_parse_context_new (&doc_rels_parser, 0, doc, NULL); retval = gxps_parse_stream (ctx, stream, error); g_object_unref (stream); g_free (doc_rels); g_markup_parse_context_free (ctx); return retval; } static void gxps_document_finalize (GObject *object) { GXPSDocument *doc = GXPS_DOCUMENT (object); g_clear_object (&doc->priv->zip); g_clear_pointer (&doc->priv->source, g_free); g_clear_pointer (&doc->priv->structure, g_free); if (doc->priv->pages) { gint i; for (i = 0; i < doc->priv->n_pages; i++) page_free (doc->priv->pages[i]); g_free (doc->priv->pages); doc->priv->pages = NULL; } g_clear_error (&doc->priv->init_error); G_OBJECT_CLASS (gxps_document_parent_class)->finalize (object); } static void gxps_document_init (GXPSDocument *doc) { doc->priv = G_TYPE_INSTANCE_GET_PRIVATE (doc, GXPS_TYPE_DOCUMENT, GXPSDocumentPrivate); doc->priv->has_rels = TRUE; } static void gxps_document_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GXPSDocument *doc = GXPS_DOCUMENT (object); switch (prop_id) { case PROP_ARCHIVE: doc->priv->zip = g_value_dup_object (value); break; case PROP_SOURCE: doc->priv->source = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gxps_document_class_init (GXPSDocumentClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = gxps_document_set_property; object_class->finalize = gxps_document_finalize; g_object_class_install_property (object_class, PROP_ARCHIVE, g_param_spec_object ("archive", "Archive", "The document archive", GXPS_TYPE_ARCHIVE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SOURCE, g_param_spec_string ("source", "Source", "The Document Source File", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (GXPSDocumentPrivate)); } static gboolean gxps_document_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { GXPSDocument *doc = GXPS_DOCUMENT (initable); if (doc->priv->initialized) { if (doc->priv->init_error) { g_propagate_error (error, g_error_copy (doc->priv->init_error)); return FALSE; } return TRUE; } doc->priv->initialized = TRUE; if (!gxps_document_parse_fixed_doc (doc, &doc->priv->init_error)) { g_propagate_error (error, g_error_copy (doc->priv->init_error)); return FALSE; } return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = gxps_document_initable_init; } GXPSDocument * _gxps_document_new (GXPSArchive *zip, const gchar *source, GError **error) { return g_initable_new (GXPS_TYPE_DOCUMENT, NULL, error, "archive", zip, "source", source, NULL); } /** * gxps_document_get_n_pages: * @doc: a #GXPSDocument * * Gets the number of pages in @doc. * * Returns: the number of pages. */ guint gxps_document_get_n_pages (GXPSDocument *doc) { g_return_val_if_fail (GXPS_IS_DOCUMENT (doc), 0); return doc->priv->n_pages; } /** * gxps_document_get_page: * @doc: a #GXPSDocument * @n_page: the index of the page to get * @error: #GError for error reporting, or %NULL to ignore * * Creates a new #GXPSPage representing the page at * index @n_doc in @doc document. * * Returns: (transfer full): a new #GXPSPage or %NULL on error. * Free the returned object with g_object_unref(). */ GXPSPage * gxps_document_get_page (GXPSDocument *doc, guint n_page, GError **error) { const gchar *source; g_return_val_if_fail (GXPS_IS_DOCUMENT (doc), NULL); g_return_val_if_fail (n_page < doc->priv->n_pages, NULL); source = doc->priv->pages[n_page]->source; g_assert (source != NULL); return _gxps_page_new (doc->priv->zip, source, error); } /** * gxps_document_get_page_size: * @doc: a #GXPSDocument * @n_page: the index of a page in @doc * @width: (out) (allow-none): return location for the width of @n_page * @height: (out) (allow-none): return location for the height of @n_page * * Gets the typical size of the page at index @n_page in @doc document. * This function is useful to get the advisory size of pages in a document * without creating #GXPSPage objects. This page size might be different than * the actual page size so page dimension might need to be updated once the * page is loaded. Advisory page sizes are not always available in @doc, * in which case this function returns %FALSE. * To get the authoritative size of a page you should use gxps_page_get_size() * instead. * * Returns: %TRUE if the page size information is available in @doc, * %FALSE otherwise. */ gboolean gxps_document_get_page_size (GXPSDocument *doc, guint n_page, gdouble *width, gdouble *height) { Page *page; g_return_val_if_fail (GXPS_IS_DOCUMENT (doc), FALSE); g_return_val_if_fail (n_page < doc->priv->n_pages, FALSE); page = doc->priv->pages[n_page]; if (page->width == 0 || page->height == 0) return FALSE; if (width) *width = page->width; if (height) *height = page->height; return TRUE; } /** * gxps_document_get_page_for_anchor: * @doc: a #GXPSDocument * @anchor: the name of an anchor * * Gets the index of the page in @doc where the given * anchor is. * * Returns: the page index of the given anchor. */ gint gxps_document_get_page_for_anchor (GXPSDocument *doc, const gchar *anchor) { guint i; g_return_val_if_fail (GXPS_IS_DOCUMENT (doc), -1); g_return_val_if_fail (anchor != NULL, -1); for (i = 0; i < doc->priv->n_pages; i++) { if (g_list_find_custom (doc->priv->pages[i]->links, anchor, (GCompareFunc)strcmp)) return i; } return -1; } /** * gxps_document_get_structure: * @doc: a a #GXPSDocument * * Creates a new #GXPSDocumentStructure representing the document * structure of @doc. * * Returns: (transfer full): a new #GXPSDocumentStructure or %NULL if document doesn't have a structure. * Free the returned object with g_object_unref(). */ GXPSDocumentStructure * gxps_document_get_structure (GXPSDocument *doc) { g_return_val_if_fail (GXPS_IS_DOCUMENT (doc), NULL); if (!doc->priv->structure) { if (!gxps_document_parse_rels (doc, NULL)) return NULL; } if (!doc->priv->structure) return NULL; if (!gxps_archive_has_entry (doc->priv->zip, doc->priv->structure)) return NULL; return _gxps_document_structure_new (doc->priv->zip, doc->priv->structure); } 07070100000027000081A40000000000000000000000016447D41100000BE7000000000000000000000000000000000000002800000000libgxps-0.3.2+5/libgxps/gxps-document.h/* GXPSDocument * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_DOCUMENT_H__ #define __GXPS_DOCUMENT_H__ #include <glib-object.h> #include <gio/gio.h> #include <libgxps/gxps-version.h> #include "gxps-page.h" #include "gxps-document-structure.h" G_BEGIN_DECLS #define GXPS_TYPE_DOCUMENT (gxps_document_get_type ()) #define GXPS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_DOCUMENT, GXPSDocument)) #define GXPS_DOCUMENT_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_DOCUMENT, GXPSDocumentClass)) #define GXPS_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_DOCUMENT)) #define GXPS_IS_DOCUMENT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_DOCUMENT)) #define GXPS_DOCUMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_DOCUMENT, GXPSDocumentClass)) typedef struct _GXPSDocument GXPSDocument; typedef struct _GXPSDocumentClass GXPSDocumentClass; typedef struct _GXPSDocumentPrivate GXPSDocumentPrivate; /** * GXPSDocument: * * The <structname>GXPSDocument</structname> struct contains * only private fields and should not be directly accessed. */ struct _GXPSDocument { GObject parent; /*< private >*/ GXPSDocumentPrivate *priv; }; struct _GXPSDocumentClass { GObjectClass parent_class; }; GXPS_AVAILABLE_IN_ALL GType gxps_document_get_type (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL guint gxps_document_get_n_pages (GXPSDocument *doc); GXPS_AVAILABLE_IN_ALL GXPSPage *gxps_document_get_page (GXPSDocument *doc, guint n_page, GError **error); GXPS_AVAILABLE_IN_ALL gboolean gxps_document_get_page_size (GXPSDocument *doc, guint n_page, gdouble *width, gdouble *height); GXPS_AVAILABLE_IN_ALL gint gxps_document_get_page_for_anchor (GXPSDocument *doc, const gchar *anchor); GXPS_AVAILABLE_IN_ALL GXPSDocumentStructure *gxps_document_get_structure (GXPSDocument *doc); G_END_DECLS #endif /* __GXPS_DOCUMENT_H__ */ 07070100000028000081A40000000000000000000000016447D4110000041B000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-error.c/* GXPSError * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-error.h" /** * SECTION:gxps-error * @Short_description: GXPS Errors * @Title: GXPSError */ GQuark gxps_error_quark (void) { return g_quark_from_static_string ("gxps-error-quark"); } 07070100000029000081A40000000000000000000000016447D411000006BA000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-error.h/* GXPSError * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_ERROR_H__ #define __GXPS_ERROR_H__ #include <glib.h> #include <libgxps/gxps-version.h> G_BEGIN_DECLS /** * GXPS_ERROR: * * Error domain for GXPS. Errors in this domain will be from * the #GXPSError enumeration. * See #GError for more information on error domains. */ #define GXPS_ERROR (gxps_error_quark ()) /** * GXPSError: * @GXPS_ERROR_SOURCE_NOT_FOUND: Internal source file not found in XPS file * @GXPS_ERROR_FONT: Error loading fonts * @GXPS_ERROR_IMAGE: Error loading images * * Error codes returned by GXPS functions. */ typedef enum { GXPS_ERROR_SOURCE_NOT_FOUND, GXPS_ERROR_FONT, GXPS_ERROR_IMAGE } GXPSError; GXPS_AVAILABLE_IN_ALL GQuark gxps_error_quark (void) G_GNUC_CONST; G_END_DECLS #endif /* __GXPS_ERROR_H__ */ 0707010000002A000081A40000000000000000000000016447D41100003196000000000000000000000000000000000000002400000000libgxps-0.3.2+5/libgxps/gxps-file.c/* GXPSFile * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <string.h> #include "gxps-file.h" #include "gxps-archive.h" #include "gxps-private.h" #include "gxps-error.h" #include "gxps-debug.h" /** * SECTION:gxps-file * @Short_description: XPS Files * @Title: GXPSFile * @See_also: #GXPSDocument, #GXPSLinkTarget * * #GXPSFile represents a XPS file. A #GXPSFile is a set of one or more * documents, you can get the amount of documents contained in the set * with gxps_file_get_n_documents(). Documents can be retrieved by their * index in the set with gxps_file_get_document(). */ enum { PROP_0, PROP_FILE }; struct _GXPSFilePrivate { GFile *file; GXPSArchive *zip; GPtrArray *docs; gboolean initialized; GError *init_error; gchar *fixed_repr; gchar *thumbnail; gchar *core_props; }; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (GXPSFile, gxps_file, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) GQuark gxps_file_error_quark (void) { return g_quark_from_static_string ("gxps-file-error-quark"); } #define REL_METATADA_CORE_PROPS "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" #define REL_METATADA_THUMBNAIL "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail" #define REL_FIXED_REPRESENTATION "http://schemas.microsoft.com/xps/2005/06/fixedrepresentation" #define REL_OXPS_FIXED_REPRESENTATION "http://schemas.openxps.org/oxps/v1.0/fixedrepresentation" /* Relationship parser */ static void rels_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSFile *xps = GXPS_FILE (user_data); if (strcmp (element_name, "Relationship") == 0) { const gchar *type = NULL; const gchar *target = NULL; gint i; for (i = 0; names[i]; i++) { if (strcmp (names[i], "Type") == 0) { type = values[i]; } else if (strcmp (names[i], "Target") == 0) { target = values[i]; } else if (strcmp (names[i], "Id") == 0) { /* Ignore ids for now */ } } if (!type || !target) { gxps_parse_error (context, "_rels/.rels", G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, !type ? "Type" : "Target", NULL, error); return; } if (strcmp (type, REL_FIXED_REPRESENTATION) == 0 || strcmp (type, REL_OXPS_FIXED_REPRESENTATION) == 0) { xps->priv->fixed_repr = g_strdup (target); } else if (strcmp (type, REL_METATADA_THUMBNAIL) == 0) { xps->priv->thumbnail = g_strdup (target); } else if (strcmp (type, REL_METATADA_CORE_PROPS) == 0) { xps->priv->core_props = g_strdup (target); } else { GXPS_DEBUG (g_debug ("Unsupported attribute of %s, %s=%s", element_name, type, target)); } } else if (strcmp (element_name, "Relationships") == 0) { /* Nothing to do */ } else { gxps_parse_error (context, "_rels/.rels", G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static const GMarkupParser rels_parser = { rels_start_element, NULL, NULL, NULL, NULL }; static gboolean gxps_file_parse_rels (GXPSFile *xps, GError **error) { GInputStream *stream; GMarkupParseContext *ctx; stream = gxps_archive_open (xps->priv->zip, "_rels/.rels"); if (!stream) { g_set_error_literal (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Source _rels/.rels not found in archive"); return FALSE; } ctx = g_markup_parse_context_new (&rels_parser, 0, xps, NULL); gxps_parse_stream (ctx, stream, error); g_object_unref (stream); g_markup_parse_context_free (ctx); return (*error != NULL) ? FALSE : TRUE; } /* FixedRepresentation parser */ static void fixed_repr_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSFile *xps = GXPS_FILE (user_data); if (strcmp (element_name, "DocumentReference") == 0) { gint i; for (i = 0; names[i]; i++) { if (strcmp (names[i], "Source") == 0) { g_ptr_array_add (xps->priv->docs, gxps_resolve_relative_path (xps->priv->fixed_repr, values[i])); } } } else if (strcmp (element_name, "FixedDocumentSequence") == 0) { /* Nothing to do */ } else { gxps_parse_error (context, xps->priv->fixed_repr, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static const GMarkupParser fixed_repr_parser = { fixed_repr_start_element, NULL, NULL, NULL, NULL }; static gboolean gxps_file_parse_fixed_repr (GXPSFile *xps, GError **error) { GInputStream *stream; GMarkupParseContext *ctx; stream = gxps_archive_open (xps->priv->zip, xps->priv->fixed_repr); if (!stream) { g_set_error_literal (error, GXPS_FILE_ERROR, GXPS_FILE_ERROR_INVALID, "Invalid XPS File: cannot open fixedrepresentation"); return FALSE; } ctx = g_markup_parse_context_new (&fixed_repr_parser, 0, xps, NULL); gxps_parse_stream (ctx, stream, error); g_object_unref (stream); g_markup_parse_context_free (ctx); return (*error != NULL) ? FALSE : TRUE; } static void gxps_file_finalize (GObject *object) { GXPSFile *xps = GXPS_FILE (object); g_clear_object (&xps->priv->zip); g_clear_object (&xps->priv->file); g_clear_pointer (&xps->priv->docs, g_ptr_array_unref); g_clear_pointer (&xps->priv->fixed_repr, g_free); g_clear_pointer (&xps->priv->thumbnail, g_free); g_clear_pointer (&xps->priv->core_props, g_free); g_clear_error (&xps->priv->init_error); G_OBJECT_CLASS (gxps_file_parent_class)->finalize (object); } static void gxps_file_init (GXPSFile *xps) { xps->priv = G_TYPE_INSTANCE_GET_PRIVATE (xps, GXPS_TYPE_FILE, GXPSFilePrivate); } static void gxps_file_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GXPSFile *xps = GXPS_FILE (object); switch (prop_id) { case PROP_FILE: xps->priv->file = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gxps_file_class_init (GXPSFileClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = gxps_file_set_property; object_class->finalize = gxps_file_finalize; g_object_class_install_property (object_class, PROP_FILE, g_param_spec_object ("file", "File", "The file file", G_TYPE_FILE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (GXPSFilePrivate)); } static gboolean gxps_file_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { GXPSFile *xps = GXPS_FILE (initable); if (xps->priv->initialized) { if (xps->priv->init_error) { g_propagate_error (error, g_error_copy (xps->priv->init_error)); return FALSE; } return TRUE; } xps->priv->initialized = TRUE; xps->priv->docs = g_ptr_array_new_with_free_func (g_free); xps->priv->zip = gxps_archive_new (xps->priv->file, &xps->priv->init_error); if (!xps->priv->zip) { g_propagate_error (error, g_error_copy (xps->priv->init_error)); return FALSE; } if (!gxps_file_parse_rels (xps, &xps->priv->init_error)) { g_propagate_error (error, g_error_copy (xps->priv->init_error)); return FALSE; } if (!xps->priv->fixed_repr) { g_set_error_literal (&xps->priv->init_error, GXPS_FILE_ERROR, GXPS_FILE_ERROR_INVALID, "Invalid XPS File: fixedrepresentation not found"); g_propagate_error (error, g_error_copy (xps->priv->init_error)); return FALSE; } if (!gxps_file_parse_fixed_repr (xps, &xps->priv->init_error)) { g_propagate_error (error, g_error_copy (xps->priv->init_error)); return FALSE; } if (xps->priv->docs->len == 0) { g_set_error_literal (&xps->priv->init_error, GXPS_FILE_ERROR, GXPS_FILE_ERROR_INVALID, "Invalid XPS File: no documents found"); g_propagate_error (error, g_error_copy (xps->priv->init_error)); return FALSE; } return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = gxps_file_initable_init; } /** * gxps_file_new: * @filename: a #GFile * @error: #GError for error reporting, or %NULL to ignore * * Creates a new #GXPSFile for the given #GFile. * * Returns: a #GXPSFile or %NULL on error. */ GXPSFile * gxps_file_new (GFile *filename, GError **error) { g_return_val_if_fail (G_IS_FILE (filename), NULL); return g_initable_new (GXPS_TYPE_FILE, NULL, error, "file", filename, NULL); } /** * gxps_file_get_n_documents: * @xps: a #GXPSFile * * Gets the number of documents in @xps. * * Returns: the number of documents. */ guint gxps_file_get_n_documents (GXPSFile *xps) { g_return_val_if_fail (GXPS_IS_FILE (xps), 0); return xps->priv->docs->len; } /** * gxps_file_get_document: * @xps: a #GXPSFile * @n_doc: the index of the document to get * @error: #GError for error reporting, or %NULL to ignore * * Creates a new #GXPSDocument representing the document at * index @n_doc in @xps file. * * Returns: (transfer full): a new #GXPSDocument or %NULL on error. * Free the returned object with g_object_unref(). */ GXPSDocument * gxps_file_get_document (GXPSFile *xps, guint n_doc, GError **error) { const gchar *source; g_return_val_if_fail (GXPS_IS_FILE (xps), NULL); g_return_val_if_fail (n_doc < xps->priv->docs->len, NULL); source = g_ptr_array_index (xps->priv->docs, n_doc); g_assert (source != NULL); return _gxps_document_new (xps->priv->zip, source, error); } /** * gxps_file_get_document_for_link_target: * @xps: a #GXPSFile * @target: a #GXPSLinkTarget * * Gets the index of the document in @xps pointed by @target. * If the #GXPSLinkTarget does not reference a document, or * referenced document is not found in @xps file -1 will be * returned. In this case you can look for the page pointed by * the link target by calling gxps_document_get_page_for_anchor() * with the anchor of the #GXPSLinkTarget for every document in * @xps. * * Returns: the index of the document pointed by the given * #GXPSLinkTarget or -1. */ gint gxps_file_get_document_for_link_target (GXPSFile *xps, GXPSLinkTarget *target) { guint i; const gchar *uri; g_return_val_if_fail (GXPS_IS_FILE (xps), -1); g_return_val_if_fail (target != NULL, -1); uri = gxps_link_target_get_uri (target); for (i = 0; i < xps->priv->docs->len; ++i) { if (g_ascii_strcasecmp (uri, (gchar *)xps->priv->docs->pdata[i]) == 0) return i; } return -1; } /** * gxps_file_get_core_properties: * @xps: a #GXPSFile * @error: #GError for error reporting, or %NULL to ignore * * Create a #GXPSCoreProperties object containing the metadata * of @xpsm, or %NULL in case of error or if the #GXPSFile * doesn't contain core properties. * * Returns: (transfer full): a new #GXPSCoreProperties or %NULL. * Free the returned object with g_object_unref(). */ GXPSCoreProperties * gxps_file_get_core_properties (GXPSFile *xps, GError **error) { g_return_val_if_fail (GXPS_IS_FILE (xps), NULL); if (!xps->priv->core_props) return NULL; return _gxps_core_properties_new (xps->priv->zip, xps->priv->core_props, error); } 0707010000002B000081A40000000000000000000000016447D41100000E7D000000000000000000000000000000000000002400000000libgxps-0.3.2+5/libgxps/gxps-file.h/* GXPSFile * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_FILE_H__ #define __GXPS_FILE_H__ #include <glib-object.h> #include <gio/gio.h> #include "gxps-document.h" #include "gxps-links.h" #include "gxps-core-properties.h" G_BEGIN_DECLS #define GXPS_TYPE_FILE (gxps_file_get_type ()) #define GXPS_FILE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_FILE, GXPSFile)) #define GXPS_FILE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_FILE, GXPSFileClass)) #define GXPS_IS_FILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_FILE)) #define GXPS_IS_FILE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_FILE)) #define GXPS_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_FILE, GXPSFileClass)) /** * GXPS_FILE_ERROR: * * Error domain for #GXPSFile. Errors in this domain will be from * the #GXPSFileError enumeration. * See #GError for more information on error domains. */ #define GXPS_FILE_ERROR (gxps_file_error_quark ()) /** * GXPSFileError: * @GXPS_FILE_ERROR_INVALID: The XPS is invalid. * * Error codes returned by #GXPSFile functions. */ typedef enum { GXPS_FILE_ERROR_INVALID } GXPSFileError; typedef struct _GXPSFile GXPSFile; typedef struct _GXPSFileClass GXPSFileClass; typedef struct _GXPSFilePrivate GXPSFilePrivate; /** * GXPSFile: * * The <structname>GXPSFile</structname> struct contains * only private fields and should not be directly accessed. */ struct _GXPSFile { GObject parent; /*< private >*/ GXPSFilePrivate *priv; }; struct _GXPSFileClass { GObjectClass parent_class; }; GXPS_AVAILABLE_IN_ALL GType gxps_file_get_type (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL GQuark gxps_file_error_quark (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL GXPSFile *gxps_file_new (GFile *filename, GError **error); GXPS_AVAILABLE_IN_ALL guint gxps_file_get_n_documents (GXPSFile *xps); GXPS_AVAILABLE_IN_ALL GXPSDocument *gxps_file_get_document (GXPSFile *xps, guint n_doc, GError **error); GXPS_AVAILABLE_IN_ALL gint gxps_file_get_document_for_link_target (GXPSFile *xps, GXPSLinkTarget *target); GXPS_AVAILABLE_IN_ALL GXPSCoreProperties *gxps_file_get_core_properties (GXPSFile *xps, GError **error); G_END_DECLS #endif /* __GXPS_FILE_H__ */ 0707010000002C000081A40000000000000000000000016447D41100001C24000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-fonts.c/* GXPSFonts * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <glib.h> #include <ft2build.h> #include FT_FREETYPE_H #include <cairo-ft.h> #include <string.h> #include "gxps-fonts.h" #include "gxps-error.h" #define FONTS_CACHE_KEY "gxps-fonts-cache" static gsize ft_font_face_cache = 0; static FT_Library ft_lib; static const cairo_user_data_key_t ft_cairo_key; static void init_ft_lib (void) { static gsize ft_init = 0; if (g_once_init_enter (&ft_init)) { FT_Init_FreeType (&ft_lib); g_once_init_leave (&ft_init, (gsize)1); } } typedef struct { guchar *font_data; gsize font_data_len; } FtFontFace; static FtFontFace * ft_font_face_new (guchar *font_data, gsize font_data_len) { FtFontFace *ff; ff = g_slice_new (FtFontFace); ff->font_data = font_data; ff->font_data_len = font_data_len; return ff; } static void ft_font_face_free (FtFontFace *font_face) { if (!font_face) return; g_slice_free (FtFontFace, font_face); } static guint ft_font_face_hash (gconstpointer v) { FtFontFace *ft_face = (FtFontFace *)v; guchar *bytes = ft_face->font_data; gssize len = ft_face->font_data_len; guint hash = 5381; while (len--) { guchar c = *bytes++; hash *= 33; hash ^= c; } return hash; } static gboolean ft_font_face_equal (gconstpointer v1, gconstpointer v2) { FtFontFace *ft_face_1 = (FtFontFace *)v1; FtFontFace *ft_face_2 = (FtFontFace *)v2; if (ft_face_1->font_data_len != ft_face_2->font_data_len) return FALSE; return memcmp (ft_face_1->font_data, ft_face_2->font_data, ft_face_1->font_data_len) == 0; } static GHashTable * get_ft_font_face_cache (void) { if (g_once_init_enter (&ft_font_face_cache)) { GHashTable *h; h = g_hash_table_new_full (ft_font_face_hash, ft_font_face_equal, (GDestroyNotify)ft_font_face_free, (GDestroyNotify)cairo_font_face_destroy); g_once_init_leave (&ft_font_face_cache, (gsize)h); } return (GHashTable *)ft_font_face_cache; } static gboolean hex_int (const gchar *spec, gint len, guint *c) { const gchar *end; *c = 0; for (end = spec + len; spec != end; spec++) { if (!g_ascii_isxdigit (*spec)) return FALSE; *c = g_ascii_xdigit_value (*spec); } return TRUE; } /* Obfuscated fonts? Based on okular code */ static gboolean parse_guid (gchar *string, unsigned short guid[16]) { // Maps bytes to positions in guidString static const int indexes[] = {6, 4, 2, 0, 11, 9, 16, 14, 19, 21, 24, 26, 28, 30, 32, 34}; int i; if (strlen (string) <= 35) { return FALSE; } for (i = 0; i < 16; i++) { guint hex1; guint hex2; if (!hex_int (string + indexes[i], 1, &hex1) || !hex_int (string + indexes[i] + 1, 1, &hex2)) return FALSE; guid[i] = hex1 * 16 + hex2; } return TRUE; } static gboolean gxps_fonts_new_ft_face (const gchar *font_uri, guchar *font_data, gsize font_data_len, FT_Face *face) { init_ft_lib (); if (FT_New_Memory_Face (ft_lib, font_data, font_data_len, 0, face)) { /* Failed to load, probably obfuscated font */ gchar *base_name; unsigned short guid[16]; base_name = g_path_get_basename (font_uri); if (!parse_guid (base_name, guid)) { g_warning ("Failed to parse guid for font %s\n", font_uri); g_free (base_name); return FALSE; } g_free (base_name); if (font_data_len >= 32) { // Obfuscation - xor bytes in font binary with bytes from guid (font's filename) static const gint mapping[] = {15, 14, 13, 12, 11, 10, 9, 8, 6, 7, 4, 5, 0, 1, 2, 3}; gint i; for (i = 0; i < 16; i++) { font_data[i] ^= guid[mapping[i]]; font_data[i + 16] ^= guid[mapping[i]]; } if (FT_New_Memory_Face (ft_lib, font_data, font_data_len, 0, face)) return FALSE; } else { g_warning ("Font file is too small\n"); return FALSE; } } return TRUE; } static cairo_font_face_t * gxps_fonts_new_font_face (GXPSArchive *zip, const gchar *font_uri, GError **error) { GHashTable *ft_cache; FtFontFace ft_face; FtFontFace *ft_font_face; FT_Face face; cairo_font_face_t *font_face; guchar *font_data; gsize font_data_len; if (!gxps_archive_read_entry (zip, font_uri, &font_data, &font_data_len, error)) { return NULL; } ft_face.font_data = font_data; ft_face.font_data_len = (gssize)font_data_len; ft_cache = get_ft_font_face_cache (); font_face = g_hash_table_lookup (ft_cache, &ft_face); if (font_face) { g_free (font_data); return font_face; } if (!gxps_fonts_new_ft_face (font_uri, font_data, font_data_len, &face)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_FONT, "Failed to load font %s", font_uri); g_free (font_data); return NULL; } font_face = cairo_ft_font_face_create_for_ft_face (face, 0); if (cairo_font_face_set_user_data (font_face, &ft_cairo_key, face, (cairo_destroy_func_t) FT_Done_Face)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_FONT, "Failed to load font %s: %s", font_uri, cairo_status_to_string (cairo_font_face_status (font_face))); cairo_font_face_destroy (font_face); FT_Done_Face (face); return NULL; } ft_font_face = ft_font_face_new (font_data, (gssize)font_data_len); g_hash_table_insert (ft_cache, ft_font_face, font_face); return font_face; } cairo_font_face_t * gxps_fonts_get_font (GXPSArchive *zip, const gchar *font_uri, GError **error) { GHashTable *fonts_cache; cairo_font_face_t *font_face = NULL; fonts_cache = g_object_get_data (G_OBJECT (zip), FONTS_CACHE_KEY); if (fonts_cache) { font_face = g_hash_table_lookup (fonts_cache, font_uri); if (font_face) return font_face; } font_face = gxps_fonts_new_font_face (zip, font_uri, error); if (font_face) { if (!fonts_cache) { fonts_cache = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)cairo_font_face_destroy); g_object_set_data_full (G_OBJECT (zip), FONTS_CACHE_KEY, fonts_cache, (GDestroyNotify)g_hash_table_destroy); } g_hash_table_insert (fonts_cache, g_strdup (font_uri), cairo_font_face_reference (font_face)); } return font_face; } 0707010000002D000081A40000000000000000000000016447D41100000446000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-fonts.h/* GXPSFonts * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_FONTS_H__ #define __GXPS_FONTS_H__ #include <cairo.h> #include "gxps-archive.h" G_BEGIN_DECLS cairo_font_face_t *gxps_fonts_get_font (GXPSArchive *zip, const gchar *font_uri, GError **error); G_END_DECLS #endif /* __GXPS_FONTS_H__ */ 0707010000002E000081A40000000000000000000000016447D4110000638D000000000000000000000000000000000000002600000000libgxps-0.3.2+5/libgxps/gxps-glyphs.c/* GXPSGlyphs * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <string.h> #include "gxps-glyphs.h" #include "gxps-brush.h" #include "gxps-matrix.h" #include "gxps-parse-utils.h" #include "gxps-debug.h" typedef enum { GI_TOKEN_INVALID, GI_TOKEN_NUMBER, GI_TOKEN_COMMA, GI_TOKEN_COLON, GI_TOKEN_SEMICOLON, GI_TOKEN_START_CLUSTER, GI_TOKEN_END_CLUSTER, GI_TOKEN_EOF } GlyphsIndicesTokenType; typedef struct { gchar *iter; gchar *end; GlyphsIndicesTokenType type; gdouble number; } GlyphsIndicesToken; GXPSGlyphs * gxps_glyphs_new (GXPSRenderContext *ctx, gchar *font_uri, gdouble font_size, gdouble origin_x, gdouble origin_y) { GXPSGlyphs *glyphs; glyphs = g_slice_new0 (GXPSGlyphs); glyphs->ctx = ctx; /* Required values */ glyphs->font_uri = font_uri; glyphs->em_size = font_size; glyphs->origin_x = origin_x; glyphs->origin_y = origin_y; glyphs->opacity = 1.0; return glyphs; } void gxps_glyphs_free (GXPSGlyphs *glyphs) { if (G_UNLIKELY (!glyphs)) return; g_free (glyphs->font_uri); g_free (glyphs->text); g_free (glyphs->indices); g_free (glyphs->clip_data); cairo_pattern_destroy (glyphs->fill_pattern); cairo_pattern_destroy (glyphs->opacity_mask); g_slice_free (GXPSGlyphs, glyphs); } static const gchar * glyphs_indices_token_type_to_string (GlyphsIndicesTokenType type) { switch (type) { case GI_TOKEN_INVALID: return "Invalid"; case GI_TOKEN_NUMBER: return "Number"; case GI_TOKEN_COMMA: return "Comma"; case GI_TOKEN_COLON: return "Colon"; case GI_TOKEN_SEMICOLON: return "Semicolon"; case GI_TOKEN_START_CLUSTER: return "StartCluster"; case GI_TOKEN_END_CLUSTER: return "EndCluster"; case GI_TOKEN_EOF: return "Eof"; default: g_assert_not_reached (); } return NULL; } static gboolean glyphs_indices_iter_next (GlyphsIndicesToken *token, GError **error) { gchar c; if (token->iter == token->end) { token->type = GI_TOKEN_EOF; return TRUE; } c = *token->iter; if (g_ascii_isdigit (c) || c == '+' || c == '-') { gchar *start; gchar *str; start = token->iter; gxps_parse_skip_number (&token->iter, token->end); str = g_strndup (start, token->iter - start); if (!gxps_value_get_double (str, &token->number)) { g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing glyphs indices: error converting token %s (%s) to double at %s", glyphs_indices_token_type_to_string (token->type), str, token->iter); g_free (str); return FALSE; } g_free (str); token->type = GI_TOKEN_NUMBER; } else if (c == '(') { token->type = GI_TOKEN_START_CLUSTER; token->iter++; } else if (c == ')') { token->type = GI_TOKEN_END_CLUSTER; token->iter++; } else if (c == ',') { token->type = GI_TOKEN_COMMA; token->iter++; } else if (c == ':') { token->type = GI_TOKEN_COLON; token->iter++; } else if (c == ';') { token->type = GI_TOKEN_SEMICOLON; token->iter++; } else { token->type = GI_TOKEN_INVALID; token->iter++; } return TRUE; } static void glyphs_indices_parse_error (GlyphsIndicesToken *token, GlyphsIndicesTokenType expected, GError **error) { if (expected == GI_TOKEN_INVALID) g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing glyphs indices: unexpected token %s at %s", glyphs_indices_token_type_to_string (token->type), token->iter); else g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing glyphs indices: expected token %s, but %s found at %s", glyphs_indices_token_type_to_string (token->type), glyphs_indices_token_type_to_string (expected), token->iter); } static gulong glyphs_lookup_index (cairo_scaled_font_t *scaled_font, const gchar *utf8) { cairo_status_t status; cairo_glyph_t stack_glyphs[1]; cairo_glyph_t *glyphs = stack_glyphs; int num_glyphs = 1; gulong index = 0; if (utf8 == NULL || *utf8 == '\0') return index; status = cairo_scaled_font_text_to_glyphs (scaled_font, 0, 0, utf8, g_utf8_next_char (utf8) - utf8, &glyphs, &num_glyphs, NULL, NULL, NULL); if (status == CAIRO_STATUS_SUCCESS) { index = glyphs[0].index; if (glyphs != stack_glyphs) cairo_glyph_free (glyphs); } return index; } static gboolean glyphs_indices_parse (const char *indices, cairo_scaled_font_t *scaled_font, gdouble x, gdouble y, const char *utf8, gint bidi_level, gboolean is_sideways, GArray *glyph_array, GArray *cluster_array, GError **error) { GlyphsIndicesToken token; cairo_text_cluster_t cluster; cairo_glyph_t glyph; gint cluster_pos = 1; gboolean have_index = FALSE; gdouble advance_width; gdouble advance_height; gboolean have_advance_width = FALSE; gdouble h_offset = 0; gdouble v_offset = 0; cairo_matrix_t font_matrix; cairo_font_extents_t font_extents; gboolean is_rtl = bidi_level % 2; gboolean eof = FALSE; cairo_scaled_font_get_font_matrix (scaled_font, &font_matrix); cairo_scaled_font_extents (scaled_font, &font_extents); cluster.num_glyphs = 1; cluster.num_bytes = 0; token.iter = (gchar *)indices; token.end = token.iter + strlen (indices); if (!glyphs_indices_iter_next (&token, error)) return FALSE; while (1) { switch (token.type) { case GI_TOKEN_START_CLUSTER: { gint num_code_units; const gchar *utf8_unit_end; if (!glyphs_indices_iter_next (&token, error)) return FALSE; if (token.type != GI_TOKEN_NUMBER) { glyphs_indices_parse_error (&token, GI_TOKEN_NUMBER, error); return FALSE; } /* Spec defines ClusterCodeUnitCount in terms of UTF-16 code units */ num_code_units = (gint)token.number; utf8_unit_end = utf8; while (utf8 && num_code_units > 0) { gunichar utf8_char = g_utf8_get_char (utf8_unit_end); if (*utf8_unit_end != '\0') utf8_unit_end = g_utf8_next_char (utf8_unit_end); num_code_units--; if (utf8_char > 0xFFFF) /* 2 code units */ num_code_units--; } cluster.num_bytes = utf8_unit_end - utf8; if (!glyphs_indices_iter_next (&token, error)) return FALSE; if (token.type == GI_TOKEN_END_CLUSTER) break; if (token.type != GI_TOKEN_COLON) { glyphs_indices_parse_error (&token, GI_TOKEN_COLON, error); return FALSE; } if (!glyphs_indices_iter_next (&token, error)) return FALSE; if (token.type != GI_TOKEN_NUMBER) { glyphs_indices_parse_error (&token, GI_TOKEN_NUMBER, error); return FALSE; } cluster.num_glyphs = (gint)token.number; cluster_pos = (gint)token.number; if (!glyphs_indices_iter_next (&token, error)) return FALSE; if (token.type != GI_TOKEN_END_CLUSTER) { glyphs_indices_parse_error (&token, GI_TOKEN_END_CLUSTER, error); return FALSE; } } break; case GI_TOKEN_NUMBER: glyph.index = (gint)token.number; have_index = TRUE; break; case GI_TOKEN_COMMA: if (!glyphs_indices_iter_next (&token, error)) return FALSE; if (token.type == GI_TOKEN_NUMBER) { advance_width = token.number / 100.0; have_advance_width = TRUE; if (!glyphs_indices_iter_next (&token, error)) return FALSE; } if (token.type != GI_TOKEN_COMMA) continue; if (!glyphs_indices_iter_next (&token, error)) return FALSE; if (token.type == GI_TOKEN_NUMBER) { h_offset = token.number / 100.0; if (!glyphs_indices_iter_next (&token, error)) return FALSE; } if (token.type != GI_TOKEN_COMMA) continue; if (!glyphs_indices_iter_next (&token, error)) return FALSE; if (token.type != GI_TOKEN_NUMBER) { glyphs_indices_parse_error (&token, GI_TOKEN_NUMBER, error); return FALSE; } v_offset = token.number / 100.0; break; case GI_TOKEN_EOF: eof = TRUE; /* fall through */ case GI_TOKEN_SEMICOLON: { cairo_text_extents_t extents; if (!have_index) glyph.index = glyphs_lookup_index (scaled_font, utf8); if (is_rtl) h_offset = -h_offset; if (is_sideways) { gdouble tmp = h_offset; h_offset = -v_offset; v_offset = tmp; } cairo_matrix_transform_distance (&font_matrix, &h_offset, &v_offset); glyph.x = x + h_offset; glyph.y = y - v_offset; cairo_scaled_font_glyph_extents (scaled_font, &glyph, 1, &extents); if (is_sideways) { glyph.x -= extents.x_bearing; glyph.y -= extents.y_advance / 2; } advance_height = 0; if (!have_advance_width) { advance_width = is_sideways ? -extents.x_bearing + font_extents.descent : extents.x_advance; } else { if (is_sideways) { advance_height = advance_width; advance_width = 0; } cairo_matrix_transform_distance (&font_matrix, &advance_width, &advance_height); } if (is_rtl) { glyph.x -= extents.x_advance; advance_width = -advance_width; } if (utf8 != NULL && *utf8 != '\0' && cluster.num_bytes == 0) cluster.num_bytes = g_utf8_next_char (utf8) - utf8; if (cluster_pos == 1) { utf8 += cluster.num_bytes; if (cluster_array) g_array_append_val (cluster_array, cluster); cluster.num_bytes = 0; cluster.num_glyphs = 1; } else { cluster_pos--; } x += advance_width; y += advance_height; have_index = FALSE; have_advance_width = FALSE; h_offset = 0; v_offset = 0; g_array_append_val (glyph_array, glyph); if (eof && (utf8 == NULL || *utf8 == '\0')) return TRUE; } break; case GI_TOKEN_INVALID: g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing glyphs indices: Invalid token at %s", token.iter); return FALSE; default: glyphs_indices_parse_error (&token, GI_TOKEN_INVALID, error); return FALSE; } if (!glyphs_indices_iter_next (&token, error)) return FALSE; } return TRUE; } gboolean gxps_glyphs_to_cairo_glyphs (GXPSGlyphs *gxps_glyphs, cairo_scaled_font_t *scaled_font, const gchar *utf8, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, GError **error) { GArray *glyph_array = g_array_new (FALSE, FALSE, sizeof (cairo_glyph_t)); GArray *cluster_array = clusters ? g_array_new (FALSE, FALSE, sizeof (cairo_text_cluster_t)) : NULL; gboolean success; if (!gxps_glyphs->indices) { cairo_glyph_t glyph; cairo_text_cluster_t cluster; gboolean is_rtl = gxps_glyphs->bidi_level % 2; gboolean is_sideways = gxps_glyphs->is_sideways; double x = gxps_glyphs->origin_x; double y = gxps_glyphs->origin_y; cairo_font_extents_t font_extents; if (utf8 == NULL || *utf8 == '\0') { g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing glyphs: Both UnicodeString and Indices are empty"); *num_glyphs = 0; *glyphs = NULL; g_array_free (glyph_array, TRUE); if (clusters) { *num_clusters = 0; *clusters = NULL; g_array_free (cluster_array, TRUE); } return FALSE; } cluster.num_glyphs = 1; cairo_scaled_font_extents (scaled_font, &font_extents); do { cairo_text_extents_t extents; gdouble advance_width; glyph.index = glyphs_lookup_index (scaled_font, utf8); glyph.x = x; glyph.y = y; cluster.num_bytes = g_utf8_next_char (utf8) - utf8; cairo_scaled_font_glyph_extents (scaled_font, &glyph, 1, &extents); if (is_sideways) { glyph.x -= extents.x_bearing; glyph.y -= extents.y_advance / 2; } advance_width = is_sideways ? -extents.x_bearing + font_extents.descent : extents.x_advance; if (is_rtl) { glyph.x -= extents.x_advance; advance_width = -advance_width; } x += advance_width; g_array_append_val (glyph_array, glyph); if (cluster_array) g_array_append_val (cluster_array, cluster); utf8 += cluster.num_bytes; } while (utf8 != NULL && *utf8 != '\0'); } else { success = glyphs_indices_parse (gxps_glyphs->indices, scaled_font, gxps_glyphs->origin_x, gxps_glyphs->origin_y, utf8, gxps_glyphs->bidi_level, gxps_glyphs->is_sideways, glyph_array, cluster_array, error); if (!success) { *num_glyphs = 0; *glyphs = NULL; g_array_free (glyph_array, TRUE); if (clusters) { *num_clusters = 0; *clusters = NULL; g_array_free (cluster_array, TRUE); } return FALSE; } } *num_glyphs = glyph_array->len; *glyphs = (cairo_glyph_t *)g_array_free (glyph_array, FALSE); if (clusters) { *num_clusters = cluster_array->len; *clusters = (cairo_text_cluster_t *)g_array_free (cluster_array, FALSE); } return TRUE; } static void glyphs_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSGlyphs *glyphs = (GXPSGlyphs *)user_data; if (strcmp (element_name, "Glyphs.RenderTransform") == 0) { GXPSMatrix *matrix; matrix = gxps_matrix_new (glyphs->ctx); gxps_matrix_parser_push (context, matrix); } else if (strcmp (element_name, "Glyphs.Clip") == 0) { } else if (strcmp (element_name, "Glyphs.Fill") == 0) { GXPSBrush *brush; brush = gxps_brush_new (glyphs->ctx); gxps_brush_parser_push (context, brush); } else if (strcmp (element_name, "Glyphs.OpacityMask") == 0) { GXPSBrush *brush; brush = gxps_brush_new (glyphs->ctx); gxps_brush_parser_push (context, brush); } else { } } static void glyphs_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSGlyphs *glyphs = (GXPSGlyphs *)user_data; if (strcmp (element_name, "Glyphs.RenderTransform") == 0) { GXPSMatrix *matrix; matrix = g_markup_parse_context_pop (context); GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix->matrix.xx, matrix->matrix.yx, matrix->matrix.xy, matrix->matrix.yy, matrix->matrix.x0, matrix->matrix.y0)); cairo_transform (glyphs->ctx->cr, &matrix->matrix); gxps_matrix_free (matrix); } else if (strcmp (element_name, "Glyphs.Clip") == 0) { } else if (strcmp (element_name, "Glyphs.Fill") == 0) { GXPSBrush *brush; brush = g_markup_parse_context_pop (context); glyphs->fill_pattern = cairo_pattern_reference (brush->pattern); gxps_brush_free (brush); } else if (strcmp (element_name, "Glyphs.OpacityMask") == 0) { GXPSBrush *brush; brush = g_markup_parse_context_pop (context); if (!glyphs->opacity_mask) { glyphs->opacity_mask = cairo_pattern_reference (brush->pattern); cairo_push_group (glyphs->ctx->cr); } gxps_brush_free (brush); } else { } } static void glyphs_error (GMarkupParseContext *context, GError *error, gpointer user_data) { GXPSGlyphs *glyphs = (GXPSGlyphs *)user_data; gxps_glyphs_free (glyphs); } static GMarkupParser glyphs_parser = { glyphs_start_element, glyphs_end_element, NULL, NULL, glyphs_error }; void gxps_glyphs_parser_push (GMarkupParseContext *context, GXPSGlyphs *glyphs) { g_markup_parse_context_push (context, &glyphs_parser, glyphs); } 0707010000002F000081A40000000000000000000000016447D41100000AD2000000000000000000000000000000000000002600000000libgxps-0.3.2+5/libgxps/gxps-glyphs.h/* GXPSGlyphs * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_GLYPHS_H__ #define __GXPS_GLYPHS_H__ #include <cairo.h> #include "gxps-page-private.h" G_BEGIN_DECLS typedef struct _GXPSGlyphs GXPSGlyphs; struct _GXPSGlyphs { GXPSRenderContext *ctx; gdouble em_size; gchar *font_uri; gdouble origin_x; gdouble origin_y; cairo_pattern_t *fill_pattern; gchar *text; gchar *indices; gchar *clip_data; gint bidi_level; gdouble opacity; cairo_pattern_t *opacity_mask; guint is_sideways : 1; guint italic : 1; }; GXPSGlyphs *gxps_glyphs_new (GXPSRenderContext *ctx, gchar *font_uri, gdouble font_size, gdouble origin_x, gdouble origin_y); void gxps_glyphs_free (GXPSGlyphs *glyphs); gboolean gxps_glyphs_to_cairo_glyphs (GXPSGlyphs *gxps_glyphs, cairo_scaled_font_t *scaled_font, const gchar *utf8, cairo_glyph_t **glyphs, int *num_glyphs, cairo_text_cluster_t **clusters, int *num_clusters, GError **error); void gxps_glyphs_parser_push (GMarkupParseContext *context, GXPSGlyphs *glyphs); G_END_DECLS #endif /* __GXPS_GLYPHS_H__ */ 07070100000030000081A40000000000000000000000016447D4110000889B000000000000000000000000000000000000002600000000libgxps-0.3.2+5/libgxps/gxps-images.c/* GXPSImages * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #ifdef HAVE_LIBPNG #include <png.h> #endif #ifdef HAVE_LIBJPEG #include <jpeglib.h> #include <setjmp.h> #endif #ifdef HAVE_LIBTIFF #include <tiffio.h> #endif #include "gxps-images.h" #include "gxps-error.h" #include "gxps-debug.h" #define METERS_PER_INCH 0.0254 #define CENTIMETERS_PER_INCH 2.54 #ifdef G_OS_WIN32 #define COBJMACROS #include <wincodec.h> #include <wincodecsdk.h> #include <combaseapi.h> #endif /* PNG */ #ifdef HAVE_LIBPNG static const cairo_user_data_key_t image_data_cairo_key; static void _read_png (png_structp png_ptr, png_bytep data, png_size_t len) { GInputStream *stream; stream = png_get_io_ptr (png_ptr); g_input_stream_read (stream, data, len, NULL, NULL); } static void png_error_callback (png_structp png_ptr, png_const_charp error_msg) { char **msg; msg = png_get_error_ptr (png_ptr); *msg = g_strdup (error_msg); longjmp (png_jmpbuf (png_ptr), 1); } static void png_warning_callback (png_structp png, png_const_charp error_msg) { } /* From cairo's cairo-png.c <http://cairographics.org> */ static inline int multiply_alpha (int alpha, int color) { int temp = (alpha * color) + 0x80; return ((temp + (temp >> 8)) >> 8); } /* Premultiplies data and converts RGBA bytes => native endian * From cairo's cairo-png.c <http://cairographics.org> */ static void premultiply_data (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *base = &data[i]; uint8_t alpha = base[3]; uint32_t p; if (alpha == 0) { p = 0; } else { uint8_t red = base[0]; uint8_t green = base[1]; uint8_t blue = base[2]; if (alpha != 0xff) { red = multiply_alpha (alpha, red); green = multiply_alpha (alpha, green); blue = multiply_alpha (alpha, blue); } p = (alpha << 24) | (red << 16) | (green << 8) | (blue << 0); } memcpy (base, &p, sizeof (uint32_t)); } } /* Converts RGBx bytes to native endian xRGB * From cairo's cairo-png.c <http://cairographics.org> */ static void convert_bytes_to_data (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *base = &data[i]; uint8_t red = base[0]; uint8_t green = base[1]; uint8_t blue = base[2]; uint32_t pixel; pixel = (0xff << 24) | (red << 16) | (green << 8) | (blue << 0); memcpy (base, &pixel, sizeof (uint32_t)); } } static void fill_png_error (GError **error, const gchar *image_uri, const gchar *msg) { if (msg) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading PNG image %s: %s", image_uri, msg); } else { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading PNG image %s", image_uri); } } #endif /* HAVE_LIBPNG */ /* Adapted from cairo's read_png in cairo-png.c * http://cairographics.org/ */ static GXPSImage * gxps_images_create_from_png (GXPSArchive *zip, const gchar *image_uri, GError **error) { #ifdef HAVE_LIBPNG GInputStream *stream; GXPSImage *image = NULL; char *png_err_msg = NULL; png_struct *png; png_info *info; png_byte *data = NULL; png_byte **row_pointers = NULL; png_uint_32 png_width, png_height; int depth, color_type, interlace, stride; unsigned int i; cairo_format_t format; cairo_status_t status; stream = gxps_archive_open (zip, image_uri); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Image source %s not found in archive", image_uri); return NULL; } png = png_create_read_struct (PNG_LIBPNG_VER_STRING, &png_err_msg, png_error_callback, png_warning_callback); if (png == NULL) { fill_png_error (error, image_uri, NULL); g_object_unref (stream); return NULL; } info = png_create_info_struct (png); if (info == NULL) { fill_png_error (error, image_uri, NULL); g_object_unref (stream); png_destroy_read_struct (&png, NULL, NULL); return NULL; } png_set_read_fn (png, stream, _read_png); if (setjmp (png_jmpbuf (png))) { fill_png_error (error, image_uri, png_err_msg); g_free (png_err_msg); g_object_unref (stream); png_destroy_read_struct (&png, &info, NULL); gxps_image_free (image); g_free (row_pointers); g_free (data); return NULL; } png_read_info (png, info); png_get_IHDR (png, info, &png_width, &png_height, &depth, &color_type, &interlace, NULL, NULL); /* convert palette/gray image to rgb */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb (png); /* expand gray bit depth if needed */ if (color_type == PNG_COLOR_TYPE_GRAY) png_set_expand_gray_1_2_4_to_8 (png); /* transform transparency to alpha */ if (png_get_valid (png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha (png); if (depth == 16) png_set_strip_16 (png); if (depth < 8) png_set_packing (png); /* convert grayscale to RGB */ if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb (png); if (interlace != PNG_INTERLACE_NONE) png_set_interlace_handling (png); png_set_filler (png, 0xff, PNG_FILLER_AFTER); /* recheck header after setting EXPAND options */ png_read_update_info (png, info); png_get_IHDR (png, info, &png_width, &png_height, &depth, &color_type, &interlace, NULL, NULL); if (depth != 8 || !(color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { fill_png_error (error, image_uri, NULL); g_object_unref (stream); png_destroy_read_struct (&png, &info, NULL); return NULL; } switch (color_type) { default: g_assert_not_reached(); /* fall-through just in case ;-) */ case PNG_COLOR_TYPE_RGB_ALPHA: format = CAIRO_FORMAT_ARGB32; png_set_read_user_transform_fn (png, premultiply_data); break; case PNG_COLOR_TYPE_RGB: format = CAIRO_FORMAT_RGB24; png_set_read_user_transform_fn (png, convert_bytes_to_data); break; } stride = cairo_format_stride_for_width (format, png_width); if (stride < 0 || png_height >= INT_MAX / stride) { fill_png_error (error, image_uri, NULL); g_object_unref (stream); png_destroy_read_struct (&png, &info, NULL); return NULL; } image = g_slice_new0 (GXPSImage); image->res_x = png_get_x_pixels_per_meter (png, info) * METERS_PER_INCH; if (image->res_x == 0) image->res_x = 96; image->res_y = png_get_y_pixels_per_meter (png, info) * METERS_PER_INCH; if (image->res_y == 0) image->res_y = 96; data = g_malloc (png_height * stride); row_pointers = g_new (png_byte *, png_height); for (i = 0; i < png_height; i++) row_pointers[i] = &data[i * stride]; png_read_image (png, row_pointers); png_read_end (png, info); png_destroy_read_struct (&png, &info, NULL); g_object_unref (stream); g_free (row_pointers); image->surface = cairo_image_surface_create_for_data (data, format, png_width, png_height, stride); if (cairo_surface_status (image->surface)) { fill_png_error (error, image_uri, NULL); gxps_image_free (image); g_free (data); return NULL; } status = cairo_surface_set_user_data (image->surface, &image_data_cairo_key, data, (cairo_destroy_func_t) g_free); if (status) { fill_png_error (error, image_uri, NULL); gxps_image_free (image); g_free (data); return NULL; } return image; #else return NULL; #endif /* HAVE_LIBPNG */ } /* JPEG */ #ifdef HAVE_LIBJPEG #define JPEG_PROG_BUF_SIZE 65536 struct _jpeg_src_mgr { struct jpeg_source_mgr pub; GInputStream *stream; JOCTET *buffer; jmp_buf setjmp_buffer; }; #ifdef GXPS_ENABLE_DEBUG static const gchar * _jpeg_color_space_name (const J_COLOR_SPACE jpeg_color_space) { switch (jpeg_color_space) { case JCS_UNKNOWN: return "UNKNOWN"; case JCS_GRAYSCALE: return "GRAYSCALE"; case JCS_RGB: return "RGB"; case JCS_YCbCr: return "YCbCr"; case JCS_CMYK: return "CMYK"; case JCS_YCCK: return "YCCK"; default: return "invalid"; } } #endif static void _jpeg_init_source (j_decompress_ptr cinfo) { } static boolean _jpeg_fill_input_buffer (j_decompress_ptr cinfo) { struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src; gssize num_bytes; num_bytes = g_input_stream_read (src->stream, src->buffer, JPEG_PROG_BUF_SIZE, NULL, NULL); if (num_bytes <= 0) { /* Insert a fake EOI marker */ src->buffer[0] = (JOCTET) 0xFF; src->buffer[1] = (JOCTET) JPEG_EOI; } src->pub.next_input_byte = src->buffer; src->pub.bytes_in_buffer = num_bytes; return TRUE; } static void _jpeg_skip_input_data (j_decompress_ptr cinfo, long num_bytes) { struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src; if (num_bytes > 0) { while (num_bytes > (long) src->pub.bytes_in_buffer) { num_bytes -= (long) src->pub.bytes_in_buffer; _jpeg_fill_input_buffer (cinfo); } src->pub.next_input_byte += (size_t) num_bytes; src->pub.bytes_in_buffer -= (size_t) num_bytes; } } static void _jpeg_term_source (j_decompress_ptr cinfo) { } static void _jpeg_error_exit (j_common_ptr error) { j_decompress_ptr cinfo = (j_decompress_ptr)error; struct _jpeg_src_mgr *src = (struct _jpeg_src_mgr *)cinfo->src; longjmp (src->setjmp_buffer, 1); } static unsigned read_uint16 (JOCTET *data, gboolean is_big_endian) { return is_big_endian ? (GETJOCTET (data[0]) << 8) | GETJOCTET (data[1]) : (GETJOCTET (data[1]) << 8) | GETJOCTET (data[0]); } static unsigned read_uint32 (JOCTET *data, gboolean is_big_endian) { return is_big_endian ? (GETJOCTET (data[0]) << 24) | (GETJOCTET (data[1]) << 16) | (GETJOCTET (data[2]) << 8) | GETJOCTET (data[3]) : (GETJOCTET (data[3]) << 24) | (GETJOCTET (data[2]) << 16) | (GETJOCTET (data[1]) << 8) | GETJOCTET (data[0]); } static gboolean _jpeg_read_exif_resolution (jpeg_saved_marker_ptr marker, int *res_x, int *res_y) { gboolean is_big_endian; guint offset; JOCTET *ifd; JOCTET *end; guint ifd_length; guint i; guint res_type; gdouble x_res = 0; gdouble y_res = 0; /* Exif marker must be the first one */ if (!(marker && marker->marker == JPEG_APP0 + 1 && marker->data_length >= 14 && marker->data[0] == 'E' && marker->data[1] == 'x' && marker->data[2] == 'i' && marker->data[3] == 'f' && marker->data[4] == '\0' && /* data[5] is a fill byte */ ((marker->data[6] == 'I' && marker->data[7] == 'I') || (marker->data[6] == 'M' && marker->data[7] == 'M')))) return FALSE; is_big_endian = marker->data[6] == 'M'; if (read_uint16 (marker->data + 8, is_big_endian) != 42) return FALSE; offset = read_uint32 (marker->data + 10, is_big_endian) + 6; if (offset >= marker->data_length) return FALSE; ifd = marker->data + offset; end = marker->data + marker->data_length; if (end - ifd < 2) return FALSE; ifd_length = read_uint16 (ifd, is_big_endian); ifd += 2; for (i = 0; i < ifd_length && end - ifd >= 12; i++, ifd += 12) { guint tag, type, count; gint value_offset; tag = read_uint16 (ifd, is_big_endian); type = read_uint16 (ifd + 2, is_big_endian); count = read_uint32 (ifd + 4, is_big_endian); value_offset = read_uint32 (ifd + 8, is_big_endian) + 6; switch (tag) { case 0x11A: if (type == 5 && value_offset > offset && value_offset <= marker->data_length - 8) x_res = (gdouble)read_uint32 (marker->data + value_offset, is_big_endian) / read_uint32 (marker->data + value_offset + 4, is_big_endian); break; case 0x11B: if (type == 5 && value_offset > offset && value_offset <= marker->data_length - 8) y_res = (gdouble)read_uint32 (marker->data + value_offset, is_big_endian) / read_uint32 (marker->data + value_offset + 4, is_big_endian); break; case 0x128: if (type == 3 && count == 1) res_type = read_uint16 (ifd + 8, is_big_endian); break; } } if (x_res <= 0 || y_res <= 0) return FALSE; switch (res_type) { case 2: *res_x = (int)x_res; *res_y = (int)y_res; break; case 3: *res_x = (int)(x_res * 254 / 100); *res_y = (int)(y_res * 254 / 100); break; default: *res_x = 0; *res_y = 0; } return TRUE; } #endif /* HAVE_LIBJPEG */ static GXPSImage * gxps_images_create_from_jpeg (GXPSArchive *zip, const gchar *image_uri, GError **error) { #ifdef HAVE_LIBJPEG GInputStream *stream; struct jpeg_error_mgr error_mgr; struct jpeg_decompress_struct cinfo; struct _jpeg_src_mgr src; GXPSImage *image; guchar *data; gint stride; JSAMPARRAY lines; gint jpeg_stride; gint i; int res_x, res_y; stream = gxps_archive_open (zip, image_uri); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Image source %s not found in archive", image_uri); return NULL; } jpeg_std_error (&error_mgr); error_mgr.error_exit = _jpeg_error_exit; jpeg_create_decompress (&cinfo); cinfo.err = &error_mgr; src.stream = stream; src.buffer = (JOCTET *) (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT, JPEG_PROG_BUF_SIZE * sizeof (JOCTET)); src.pub.init_source = _jpeg_init_source; src.pub.fill_input_buffer = _jpeg_fill_input_buffer; src.pub.skip_input_data = _jpeg_skip_input_data; src.pub.resync_to_restart = jpeg_resync_to_restart; src.pub.term_source = _jpeg_term_source; src.pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ src.pub.next_input_byte = NULL; /* until buffer loaded */ cinfo.src = (struct jpeg_source_mgr *)&src; if (setjmp (src.setjmp_buffer)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading JPEG image %s", image_uri); g_object_unref (stream); return NULL; } jpeg_save_markers (&cinfo, JPEG_APP0 + 1, 0xFFFF); jpeg_read_header (&cinfo, TRUE); cinfo.do_fancy_upsampling = FALSE; jpeg_start_decompress (&cinfo); image = g_slice_new (GXPSImage); image->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, cinfo.output_width, cinfo.output_height); image->res_x = 96; image->res_y = 96; if (cairo_surface_status (image->surface)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading JPEG image %s: %s", image_uri, cairo_status_to_string (cairo_surface_status (image->surface))); jpeg_destroy_decompress (&cinfo); gxps_image_free (image); g_object_unref (stream); return NULL; } data = cairo_image_surface_get_data (image->surface); stride = cairo_image_surface_get_stride (image->surface); jpeg_stride = cinfo.output_width * cinfo.out_color_components; lines = cinfo.mem->alloc_sarray((j_common_ptr) &cinfo, JPOOL_IMAGE, jpeg_stride, 4); while (cinfo.output_scanline < cinfo.output_height) { gint n_lines, x; n_lines = jpeg_read_scanlines (&cinfo, lines, cinfo.rec_outbuf_height); for (i = 0; i < n_lines; i++) { JSAMPLE *line = lines[i]; guchar *p = data; for (x = 0; x < cinfo.output_width; x++) { switch (cinfo.out_color_space) { case JCS_RGB: p[0] = line[2]; p[1] = line[1]; p[2] = line[0]; p[3] = 0xff; break; case JCS_GRAYSCALE: p[0] = line[0]; p[1] = line[0]; p[2] = line[0]; p[3] = 0xff; break; case JCS_CMYK: p[0] = line[2] * line[3] / 255; p[1] = line[1] * line[3] / 255; p[2] = line[0] * line[3] / 255; p[3] = 0xff; break; default: GXPS_DEBUG (g_message ("Unsupported jpeg color space %s", _jpeg_color_space_name (cinfo.out_color_space))); gxps_image_free (image); jpeg_destroy_decompress (&cinfo); g_object_unref (stream); return NULL; } line += cinfo.out_color_components; p += 4; } data += stride; } } if (_jpeg_read_exif_resolution (cinfo.marker_list, &res_x, &res_y)) { if (res_x > 0) image->res_x = res_x; if (res_y > 0) image->res_y = res_y; } else if (cinfo.density_unit == 1) { /* dots/inch */ image->res_x = cinfo.X_density; image->res_y = cinfo.Y_density; } else if (cinfo.density_unit == 2) { /* dots/cm */ image->res_x = cinfo.X_density * CENTIMETERS_PER_INCH; image->res_y = cinfo.Y_density * CENTIMETERS_PER_INCH; } jpeg_finish_decompress (&cinfo); jpeg_destroy_decompress (&cinfo); g_object_unref (stream); cairo_surface_mark_dirty (image->surface); if (cairo_surface_status (image->surface)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading JPEG image %s: %s", image_uri, cairo_status_to_string (cairo_surface_status (image->surface))); gxps_image_free (image); return NULL; } return image; #else return NULL; #endif /* HAVE_LIBJPEG */ } /* Tiff */ #ifdef HAVE_LIBTIFF static TIFFErrorHandler orig_error_handler = NULL; static TIFFErrorHandler orig_warning_handler = NULL; static gchar *_tiff_error = NULL; typedef struct { guchar *buffer; gsize buffer_len; guint pos; } TiffBuffer; static void fill_tiff_error (GError **error, const gchar *image_uri) { if (_tiff_error) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading TIFF image %s: %s", image_uri, _tiff_error); g_free (_tiff_error); _tiff_error = NULL; } else { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading TIFF image %s", image_uri); } } G_GNUC_PRINTF (2, 0) static void _tiff_error_handler (const char *mod, const char *fmt, va_list ap) { if (G_UNLIKELY (_tiff_error)) return; _tiff_error = g_strdup_vprintf (fmt, ap); } static void _tiff_push_handlers (void) { orig_error_handler = TIFFSetErrorHandler (_tiff_error_handler); orig_warning_handler = TIFFSetWarningHandler (NULL); } static void _tiff_pop_handlers (void) { TIFFSetErrorHandler (orig_error_handler); TIFFSetWarningHandler (orig_warning_handler); } static tsize_t _tiff_read (thandle_t handle, tdata_t buf, tsize_t size) { TiffBuffer *buffer = (TiffBuffer *)handle; if (buffer->pos + size > buffer->buffer_len) return 0; memcpy (buf, buffer->buffer + buffer->pos, size); buffer->pos += size; return size; } static tsize_t _tiff_write (thandle_t handle, tdata_t buf, tsize_t size) { return -1; } static toff_t _tiff_seek (thandle_t handle, toff_t offset, int whence) { TiffBuffer *buffer = (TiffBuffer *)handle; switch (whence) { case SEEK_SET: if (offset > buffer->buffer_len) return -1; buffer->pos = offset; break; case SEEK_CUR: if (offset + buffer->pos >= buffer->buffer_len) return -1; buffer->pos += offset; break; case SEEK_END: if (offset + buffer->buffer_len > buffer->buffer_len) return -1; buffer->pos = buffer->buffer_len + offset; break; default: return -1; } return buffer->pos; } static int _tiff_close (thandle_t context) { return 0; } static toff_t _tiff_size (thandle_t handle) { TiffBuffer *buffer = (TiffBuffer *)handle; return buffer->buffer_len; } static int _tiff_map_file (thandle_t handle, tdata_t *buf, toff_t *size) { TiffBuffer *buffer = (TiffBuffer *)handle; *buf = buffer->buffer; *size = buffer->buffer_len; return 0; } static void _tiff_unmap_file (thandle_t handle, tdata_t data, toff_t offset) { } #endif /* #ifdef HAVE_LIBTIFF */ static GXPSImage * gxps_images_create_from_tiff (GXPSArchive *zip, const gchar *image_uri, GError **error) { #ifdef HAVE_LIBTIFF TIFF *tiff; TiffBuffer buffer; GXPSImage *image; gint width, height; guint16 res_unit; float res_x, res_y; gint stride; guchar *data; guchar *p; if (!gxps_archive_read_entry (zip, image_uri, &buffer.buffer, &buffer.buffer_len, error)) { return NULL; } buffer.pos = 0; _tiff_push_handlers (); tiff = TIFFClientOpen ("libgxps-tiff", "r", &buffer, _tiff_read, _tiff_write, _tiff_seek, _tiff_close, _tiff_size, _tiff_map_file, _tiff_unmap_file); if (!tiff || _tiff_error) { fill_tiff_error (error, image_uri); if (tiff) TIFFClose (tiff); _tiff_pop_handlers (); g_free (buffer.buffer); return NULL; } if (!TIFFGetField (tiff, TIFFTAG_IMAGEWIDTH, &width) || _tiff_error) { fill_tiff_error (error, image_uri); TIFFClose (tiff); _tiff_pop_handlers (); g_free (buffer.buffer); return NULL; } if (!TIFFGetField (tiff, TIFFTAG_IMAGELENGTH, &height) || _tiff_error) { fill_tiff_error (error, image_uri); TIFFClose (tiff); _tiff_pop_handlers (); g_free (buffer.buffer); return NULL; } if (width <= 0 || height <= 0) { fill_tiff_error (error, image_uri); TIFFClose (tiff); _tiff_pop_handlers (); g_free (buffer.buffer); return NULL; } image = g_slice_new (GXPSImage); image->surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, width, height); image->res_x = 96; image->res_y = 96; if (!TIFFGetField (tiff, TIFFTAG_RESOLUTIONUNIT, &res_unit)) res_unit = 0; if (TIFFGetField (tiff, TIFFTAG_XRESOLUTION, &res_x)) { if (res_unit == 2) { /* inches */ image->res_x = res_x; } else if (res_unit == 3) { /* centimeters */ image->res_x = res_x * CENTIMETERS_PER_INCH; } } if (TIFFGetField (tiff, TIFFTAG_YRESOLUTION, &res_y)) { if (res_unit == 2) { /* inches */ image->res_y = res_y; } else if (res_unit == 3) { /* centimeters */ image->res_y = res_y * CENTIMETERS_PER_INCH; } } if (cairo_surface_status (image->surface)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error loading TIFF image %s: %s", image_uri, cairo_status_to_string (cairo_surface_status (image->surface))); gxps_image_free (image); TIFFClose (tiff); _tiff_pop_handlers (); g_free (buffer.buffer); return NULL; } data = cairo_image_surface_get_data (image->surface); if (!TIFFReadRGBAImageOriented (tiff, width, height, (uint32 *)data, ORIENTATION_TOPLEFT, 1) || _tiff_error) { fill_tiff_error (error, image_uri); gxps_image_free (image); TIFFClose (tiff); _tiff_pop_handlers (); g_free (buffer.buffer); return NULL; } TIFFClose (tiff); _tiff_pop_handlers (); g_free (buffer.buffer); stride = cairo_image_surface_get_stride (image->surface); p = data; while (p < data + (height * stride)) { guint32 *pixel = (guint32 *)p; guint8 r = TIFFGetR (*pixel); guint8 g = TIFFGetG (*pixel); guint8 b = TIFFGetB (*pixel); guint8 a = TIFFGetA (*pixel); *pixel = (a << 24) | (r << 16) | (g << 8) | b; p += 4; } cairo_surface_mark_dirty (image->surface); return image; #else return NULL; #endif /* #ifdef HAVE_LIBTIFF */ } #ifdef G_OS_WIN32 static GXPSImage * image_create_from_byte_array (BYTE *bytes, int width, int height, UINT buffer_size, GError **error) { int stride; guchar *data; GXPSImage *image; cairo_status_t status; data = g_try_malloc (buffer_size); if (data == NULL) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error allocating data buffer for cairo surface"); return NULL; } memcpy (data, bytes, buffer_size); stride = cairo_format_stride_for_width (CAIRO_FORMAT_ARGB32, width); image = g_slice_new0 (GXPSImage); image->res_x = 96; image->res_y = 96; image->surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, width, height, stride); if (cairo_surface_status (image->surface) != CAIRO_STATUS_SUCCESS) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error creating cairo surface"); gxps_image_free (image); g_free (data); return NULL; } status = cairo_surface_set_user_data (image->surface, &image_data_cairo_key, data, (cairo_destroy_func_t) g_free); if (status) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error setting surface user data"); gxps_image_free (image); g_free (data); return NULL; } return image; } static GXPSImage * gxps_images_create_from_wdp (GXPSArchive *zip, const gchar *image_uri, GError **error) { #define buffer_size 1024 GInputStream *stream; GXPSImage *image; IID iid_imaging_factory; HRESULT hr; IWICImagingFactory *image_factory; IWICBitmapDecoder *decoder; IWICBitmapFrameDecode *decoder_frame; IWICBitmap *bitmap; IWICBitmapLock *bitmap_lock; IStream *win_stream; UINT width; UINT height; guchar buffer[buffer_size]; gsize read_bytes; gsize nwritten; UINT written_bytes; UINT bytes_size = 0; BYTE *bytes = NULL; WICRect rc_lock; stream = gxps_archive_open (zip, image_uri); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Image source %s not found in archive", image_uri); return NULL; } /* Initialize COM. */ hr = CoInitializeEx (NULL, COINIT_MULTITHREADED); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error initializing COM, hr code: %d", HRESULT_CODE (hr)); g_object_unref (stream); return NULL; } else if (hr == S_FALSE) { g_warning ("COM was already initialized"); } /* Initialize IID IWICImagingFactory */ IIDFromString (L"{ec5ec8a9-c395-4314-9c77-54d7a935ff70}", &iid_imaging_factory); /* Create COM imaging factory. */ hr = CoCreateInstance (&CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, &iid_imaging_factory, (LPVOID)&image_factory); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error creating an instance of IWICImagingFactory, hr code: %d", HRESULT_CODE (hr)); g_object_unref (stream); CoUninitialize (); return NULL; } hr = CreateStreamOnHGlobal (NULL, TRUE, &win_stream); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error allocating IStream, hr code: %d", HRESULT_CODE (hr)); IWICImagingFactory_Release (image_factory); g_object_unref (stream); CoUninitialize (); return NULL; } /* Write GInputStream data into IStream */ do { read_bytes = g_input_stream_read (stream, buffer, sizeof (buffer), NULL, error); if (read_bytes < 0) { IWICImagingFactory_Release (image_factory); g_object_unref (stream); CoUninitialize (); return NULL; } nwritten = 0; while (nwritten < read_bytes) { IStream_Write (win_stream, buffer + nwritten, read_bytes - nwritten, &written_bytes); nwritten += written_bytes; } } while (read_bytes > 0); g_object_unref (stream); hr = IWICImagingFactory_CreateDecoderFromStream (image_factory, win_stream, NULL, WICDecodeMetadataCacheOnDemand, &decoder); IStream_Release (win_stream); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error creating decoder from stream, hr code: %d", HRESULT_CODE (hr)); IWICImagingFactory_Release (image_factory); CoUninitialize (); return NULL; } hr = IWICBitmapDecoder_GetFrame (decoder, 0, &decoder_frame); IWICBitmapDecoder_Release (decoder); if (!SUCCEEDED(hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error getting frame, hr code: %d", HRESULT_CODE (hr)); IWICImagingFactory_Release (image_factory); CoUninitialize (); return NULL; } hr = IWICBitmapFrameDecode_GetSize (decoder_frame, &width, &height); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error getting image size, hr code: %d", HRESULT_CODE (hr)); IWICImagingFactory_Release (image_factory); IWICBitmapFrameDecode_Release (decoder_frame); CoUninitialize (); return NULL; } hr = IWICImagingFactory_CreateBitmapFromSource (image_factory, (IWICBitmapSource *)decoder_frame, WICBitmapCacheOnDemand, &bitmap); IWICImagingFactory_Release (image_factory); IWICBitmapFrameDecode_Release (decoder_frame); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error creating bitmap, hr code: %d", HRESULT_CODE (hr)); CoUninitialize (); return NULL; } rc_lock.X = 0; rc_lock.Y = 0; rc_lock.Width = width; rc_lock.Height = height; hr = IWICBitmap_Lock (bitmap, &rc_lock, WICBitmapLockWrite, &bitmap_lock); IWICBitmap_Release (bitmap); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error locking bitmap, hr code: %d", HRESULT_CODE (hr)); CoUninitialize (); return NULL; } hr = IWICBitmapLock_GetDataPointer (bitmap_lock, &bytes_size, &bytes); if (!SUCCEEDED (hr)) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_IMAGE, "Error getting data pointer, hr code: %d", HRESULT_CODE(hr)); IWICBitmapLock_Release (bitmap_lock); CoUninitialize (); return NULL; } image = image_create_from_byte_array (bytes, width, height, bytes_size, error); IWICBitmapLock_Release (bitmap_lock); CoUninitialize (); return image; } #endif /* #ifdef G_OS_WIN32 */ static gchar * gxps_images_guess_content_type (GXPSArchive *zip, const gchar *image_uri) { GInputStream *stream; guchar buffer[1024]; gssize bytes_read; gchar *mime_type; stream = gxps_archive_open (zip, image_uri); if (!stream) return NULL; bytes_read = g_input_stream_read (stream, buffer, 1024, NULL, NULL); mime_type = g_content_type_guess (NULL, buffer, bytes_read, NULL); g_object_unref (stream); return mime_type; } GXPSImage * gxps_images_get_image (GXPSArchive *zip, const gchar *image_uri, GError **error) { GXPSImage *image = NULL; gchar *image_uri_lower; /* First try with extensions, * as it's recommended by the spec * (2.1.5 Image Parts) */ image_uri_lower = g_utf8_strdown (image_uri, -1); if (g_str_has_suffix (image_uri_lower, ".png")) { image = gxps_images_create_from_png (zip, image_uri, error); } else if (g_str_has_suffix (image_uri_lower, ".jpg")) { image = gxps_images_create_from_jpeg (zip, image_uri, error); } else if (g_str_has_suffix (image_uri_lower, ".tif")) { image = gxps_images_create_from_tiff (zip, image_uri, error); } else if (g_str_has_suffix (image_uri_lower, "wdp")) { #ifdef G_OS_WIN32 image = gxps_images_create_from_wdp (zip, image_uri, error); #else GXPS_DEBUG (g_message ("Unsupported image format windows media photo")); g_free (image_uri_lower); return NULL; #endif } g_free (image_uri_lower); if (!image) { gchar *mime_type; g_clear_error(error); mime_type = gxps_images_guess_content_type (zip, image_uri); if (g_strcmp0 (mime_type, "image/png") == 0) { image = gxps_images_create_from_png (zip, image_uri, error); } else if (g_strcmp0 (mime_type, "image/jpeg") == 0) { image = gxps_images_create_from_jpeg (zip, image_uri, error); } else if (g_strcmp0 (mime_type, "image/tiff") == 0) { image = gxps_images_create_from_tiff (zip, image_uri, error); } else { GXPS_DEBUG (g_message ("Unsupported image format: %s", mime_type)); } g_free (mime_type); } return image; } void gxps_image_free (GXPSImage *image) { if (G_UNLIKELY (!image)) return; if (G_LIKELY (image->surface)) cairo_surface_destroy (image->surface); g_slice_free (GXPSImage, image); } 07070100000031000081A40000000000000000000000016447D41100000545000000000000000000000000000000000000002600000000libgxps-0.3.2+5/libgxps/gxps-images.h/* GXPSImages * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_IMAGES_H__ #define __GXPS_IMAGES_H__ #include <cairo.h> #include "gxps-archive.h" G_BEGIN_DECLS typedef struct _GXPSImage GXPSImage; struct _GXPSImage { cairo_surface_t *surface; double res_x; double res_y; }; GXPSImage *gxps_images_get_image (GXPSArchive *zip, const gchar *image_uri, GError **error); void gxps_image_free (GXPSImage *image); G_END_DECLS #endif /* __GXPS_IMAGES_H__ */ 07070100000032000081A40000000000000000000000016447D411000018D8000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-links.c/* GXPSLinks * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <string.h> #include "gxps-links.h" #include "gxps-private.h" /** * SECTION:gxps-links * @Short_description: XPS Links * @Title: GXPS Links * @See_also: #GXPSFile, #GXPSPage, #GXPSDocumentStructure * * #GXPSLinkTarget is a hyperlink source that can point to a location in * any of the documents of the XPS file or to an external document. * Internal #GXPSLinkTarget<!-- -->s have an URI relative to the XPS file, * and a named destination represented by an anchor. External * #GXPSLinkTarget<!-- -->s have an absolute URI pointing to a location * in another XPS File and might optionally have an anchor. * To get the destination pointed by a in internal #GXPSLinkTarget you can use * gxps_file_get_document_for_link_target() to get the document within the file, * gxps_document_get_page_for_anchor() to get the page within the document, and * gxps_page_get_anchor_destination() to get the page area. * * #GXPSLink maps a location in a page to a #GXPSLinkTarget. To get the list of * link for a page you can use gxps_page_get_links(). */ struct _GXPSLink { GXPSLinkTarget *target; cairo_rectangle_t area; }; struct _GXPSLinkTarget { gboolean is_internal; gchar *uri; gchar *anchor; }; G_DEFINE_BOXED_TYPE (GXPSLink, gxps_link, gxps_link_copy, gxps_link_free) G_DEFINE_BOXED_TYPE (GXPSLinkTarget, gxps_link_target, gxps_link_target_copy, gxps_link_target_free) /** * gxps_link_copy: * @link: a #GXPSLink * * Creates a copy of a #GXPSLink. * * Returns: a copy of @link. * Free the returned object with gxps_link_free(). */ GXPSLink * gxps_link_copy (GXPSLink *link) { GXPSLink *link_copy; g_return_val_if_fail (link != NULL, NULL); link_copy = g_slice_new (GXPSLink); *link_copy = *link; if (link->target) link_copy->target = gxps_link_target_copy (link->target); return link_copy; } /** * gxps_link_free: * @link: a #GXPSLink * * Frees a #GXPSLink. */ void gxps_link_free (GXPSLink *link) { if (G_UNLIKELY (!link)) return; gxps_link_target_free (link->target); g_slice_free (GXPSLink, link); } /** * gxps_link_get_target: * @link: a #GXPSLink * * Gets the #GXPSLinkTarget mapped by @link. * * Returns: (transfer none): the #GXPSLinkTarget of @link. */ GXPSLinkTarget * gxps_link_get_target (GXPSLink *link) { g_return_val_if_fail (link != NULL, NULL); return link->target; } /** * gxps_link_get_area: * @link: a #GXPSLink * @area: (out): return location for page area * * Gets the rectangle of a page where the #GXPSLinkTarget * mapped by @link is. */ void gxps_link_get_area (GXPSLink *link, cairo_rectangle_t *area) { g_return_if_fail (link != NULL); g_return_if_fail (area != NULL); *area = link->area; } /** * gxps_link_target_copy: * @target: a #GXPSLinkTarget * * Creates a copy of a #GXPSLinkTarget * * Returns: a copy of @target. * Free the returned object with gxps_link_target_free() */ GXPSLinkTarget * gxps_link_target_copy (GXPSLinkTarget *target) { GXPSLinkTarget *target_copy; g_return_val_if_fail (target != NULL, NULL); target_copy = g_slice_new (GXPSLinkTarget); target_copy->is_internal = target->is_internal; target_copy->uri = g_strdup (target->uri); target_copy->anchor = target->anchor ? g_strdup (target->anchor) : NULL; return target_copy; } /** * gxps_link_target_free: * @target: a #GXPSLinkTarget * * Frees a #GXPSLinkTarget. */ void gxps_link_target_free (GXPSLinkTarget *target) { if (G_UNLIKELY (!target)) return; g_free (target->uri); g_free (target->anchor); g_slice_free (GXPSLinkTarget, target); } /** * gxps_link_target_is_internal: * @target: a #GXPSLinkTarget * * Gets whether @target destination is internal or not. * * Returns: %TRUE if the #GXPSLinkTarget points to an internal location, * %FALSE if it points to a external one. */ gboolean gxps_link_target_is_internal (GXPSLinkTarget *target) { g_return_val_if_fail (target != NULL, FALSE); return target->is_internal; } /** * gxps_link_target_get_anchor: * @target: a #GXPSLinkTarget * * Gets the anchor name @target links to. If @target is * an internal #GXPSLinkTarget this function always returns * and anchor, if it is external it might return %NULL if the * @target does not have an anchor. * * Returns: the name of the anchor of @target. */ const gchar * gxps_link_target_get_anchor (GXPSLinkTarget *target) { g_return_val_if_fail (target != NULL, NULL); return target->anchor; } /** * gxps_link_target_get_uri: * @target: a #GXPSLinkTarget * * Gets the URI @target links to. * * Returns: the URI of @target. */ const gchar * gxps_link_target_get_uri (GXPSLinkTarget *target) { g_return_val_if_fail (target != NULL, NULL); return target->uri; } GXPSLinkTarget * _gxps_link_target_new (GXPSArchive *zip, const gchar *uri) { GXPSLinkTarget *target; gchar *sep; target = g_slice_new (GXPSLinkTarget); sep = g_strrstr (uri, "#"); if (sep) { target->uri = g_strndup (uri, strlen (uri) - strlen (sep)); target->anchor = g_strdup (++sep); } else { target->uri = g_strdup (uri); target->anchor = NULL; } target->is_internal = gxps_archive_has_entry (zip, target->uri); return target; } GXPSLink * _gxps_link_new (GXPSArchive *zip, cairo_rectangle_t *area, const gchar *uri) { GXPSLink *link; link = g_slice_new (GXPSLink); link->area = *area; link->target = _gxps_link_target_new (zip, uri); return link; } 07070100000033000081A40000000000000000000000016447D41100000A22000000000000000000000000000000000000002500000000libgxps-0.3.2+5/libgxps/gxps-links.h/* GXPSLinks * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_LINKS_H__ #define __GXPS_LINKS_H__ #include <glib-object.h> #include <cairo.h> #include <libgxps/gxps-version.h> G_BEGIN_DECLS #define GXPS_TYPE_LINK (gxps_link_get_type ()) #define GXPS_TYPE_LINK_TARGET (gxps_link_target_get_type ()) /** * GXPSLink: * * GXPSLink maps a location in a page to a #GXPSLinkTarget. */ typedef struct _GXPSLink GXPSLink; /** * GXPSLinkTarget: * * GXPSLinkTarget represents a hyperlink source. */ typedef struct _GXPSLinkTarget GXPSLinkTarget; GXPS_AVAILABLE_IN_ALL GType gxps_link_get_type (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL GXPSLink *gxps_link_copy (GXPSLink *link); GXPS_AVAILABLE_IN_ALL void gxps_link_free (GXPSLink *link); GXPS_AVAILABLE_IN_ALL GXPSLinkTarget *gxps_link_get_target (GXPSLink *link); GXPS_AVAILABLE_IN_ALL void gxps_link_get_area (GXPSLink *link, cairo_rectangle_t *area); GXPS_AVAILABLE_IN_ALL GType gxps_link_target_get_type (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL GXPSLinkTarget *gxps_link_target_copy (GXPSLinkTarget *target); GXPS_AVAILABLE_IN_ALL void gxps_link_target_free (GXPSLinkTarget *target); GXPS_AVAILABLE_IN_ALL gboolean gxps_link_target_is_internal (GXPSLinkTarget *target); GXPS_AVAILABLE_IN_ALL const gchar *gxps_link_target_get_anchor (GXPSLinkTarget *target); GXPS_AVAILABLE_IN_ALL const gchar *gxps_link_target_get_uri (GXPSLinkTarget *target); G_END_DECLS #endif /* __GXPS_LINKS_H__ */ 07070100000034000081A40000000000000000000000016447D41100001116000000000000000000000000000000000000002600000000libgxps-0.3.2+5/libgxps/gxps-matrix.c/* GXPSMatrix * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <string.h> #include "gxps-matrix.h" #include "gxps-parse-utils.h" GXPSMatrix * gxps_matrix_new (GXPSRenderContext *ctx) { GXPSMatrix *matrix; matrix = g_slice_new0 (GXPSMatrix); matrix->ctx = ctx; cairo_matrix_init_identity (&matrix->matrix); return matrix; } void gxps_matrix_free (GXPSMatrix *matrix) { if (G_UNLIKELY (!matrix)) return; g_slice_free (GXPSMatrix, matrix); } gboolean gxps_matrix_parse (const gchar *data, cairo_matrix_t *matrix) { gchar **items; gdouble mm[6]; guint i; items = g_strsplit (data, ",", 6); if (g_strv_length (items) != 6) { g_strfreev (items); return FALSE; } for (i = 0; i < 6; i++) { if (!gxps_value_get_double (items[i], &mm[i])) { g_strfreev (items); return FALSE; } } g_strfreev (items); cairo_matrix_init (matrix, mm[0], mm[1], mm[2], mm[3], mm[4], mm[5]); return TRUE; } static void matrix_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSMatrix *matrix = (GXPSMatrix *)user_data; if (strcmp (element_name, "MatrixTransform") == 0) { gint i; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Matrix") == 0) { if (!gxps_matrix_parse (values[i], &matrix->matrix)) { gxps_parse_error (context, matrix->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "MatrixTransform", "Matrix", values[i], error); } } else { gxps_parse_error (context, matrix->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "MatrixTransform", names[i], NULL, error); } } } else { gxps_parse_error (context, matrix->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void matrix_error (GMarkupParseContext *context, GError *error, gpointer user_data) { GXPSMatrix *matrix = (GXPSMatrix *)user_data; gxps_matrix_free (matrix); } static GMarkupParser matrix_parser = { matrix_start_element, NULL, NULL, NULL, matrix_error }; void gxps_matrix_parser_push (GMarkupParseContext *context, GXPSMatrix *matrix) { g_markup_parse_context_push (context, &matrix_parser, matrix); } 07070100000035000081A40000000000000000000000016447D411000005EF000000000000000000000000000000000000002600000000libgxps-0.3.2+5/libgxps/gxps-matrix.h/* GXPSMatrix * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_MATRIX_H__ #define __GXPS_MATRIX_H__ #include <cairo.h> #include "gxps-page-private.h" G_BEGIN_DECLS typedef struct _GXPSMatrix GXPSMatrix; struct _GXPSMatrix { GXPSRenderContext *ctx; cairo_matrix_t matrix; }; GXPSMatrix *gxps_matrix_new (GXPSRenderContext *ctx); void gxps_matrix_free (GXPSMatrix *matrix); gboolean gxps_matrix_parse (const gchar *data, cairo_matrix_t *matrix); void gxps_matrix_parser_push (GMarkupParseContext *context, GXPSMatrix *matrix); G_END_DECLS #endif /* __GXPS_MATRIX_H__ */ 07070100000036000081A40000000000000000000000016447D41100000825000000000000000000000000000000000000002C00000000libgxps-0.3.2+5/libgxps/gxps-page-private.h/* GXPSPage * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PAGE_PRIVATE_H__ #define __GXPS_PAGE_PRIVATE_H__ #include <glib.h> #include <cairo.h> #include "gxps-page.h" #include "gxps-archive.h" #include "gxps-images.h" #include "gxps-resources.h" G_BEGIN_DECLS typedef struct _GXPSRenderContext GXPSRenderContext; typedef struct _GXPSBrushVisual GXPSBrushVisual; struct _GXPSPagePrivate { GXPSArchive *zip; gchar *source; gboolean initialized; GError *init_error; gdouble width; gdouble height; gchar *lang; gchar *name; /* Images */ GHashTable *image_cache; /* Anchors */ gboolean has_anchors; GHashTable *anchors; }; struct _GXPSRenderContext { GXPSPage *page; cairo_t *cr; GXPSBrushVisual *visual; }; GXPSImage *gxps_page_get_image (GXPSPage *page, const gchar *image_uri, GError **error); void gxps_page_render_parser_push (GMarkupParseContext *context, GXPSRenderContext *ctx); G_END_DECLS #endif /* __GXPS_PAGE_PRIVATE_H__ */ 07070100000037000081A40000000000000000000000016447D4110000E79D000000000000000000000000000000000000002400000000libgxps-0.3.2+5/libgxps/gxps-page.c/* GXPSPage * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <stdlib.h> #include <string.h> #include "gxps-page-private.h" #include "gxps-matrix.h" #include "gxps-brush.h" #include "gxps-path.h" #include "gxps-glyphs.h" #include "gxps-fonts.h" #include "gxps-links.h" #include "gxps-images.h" #include "gxps-color.h" #include "gxps-private.h" #include "gxps-error.h" #include "gxps-debug.h" /** * SECTION:gxps-page * @Short_description: Page of XPS document * @Title: GXPSPage * @See_also: #GXPSDocument, #GXPSLink, #GXPSLinkTarget * * #GXPSPage represents a page in a XPS document. #GXPSPage<!-- -->s * can be rendered into a cairo context with gxps_page_render(). * #GXPSPage objects can not be created directly, they are retrieved * from a #GXPSDocument with gxps_document_get_page(). */ enum { PROP_0, PROP_ARCHIVE, PROP_SOURCE }; static void render_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error); static void render_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error); static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (GXPSPage, gxps_page, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) GQuark gxps_page_error_quark (void) { return g_quark_from_static_string ("gxps-page-error-quark"); } /* Images */ GXPSImage * gxps_page_get_image (GXPSPage *page, const gchar *image_uri, GError **error) { GXPSImage *image; if (page->priv->image_cache) { image = g_hash_table_lookup (page->priv->image_cache, image_uri); if (image) return image; } image = gxps_images_get_image (page->priv->zip, image_uri, error); if (!image) return NULL; if (!page->priv->image_cache) { page->priv->image_cache = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)gxps_image_free); } g_hash_table_insert (page->priv->image_cache, g_strdup (image_uri), image); return image; } /* FixedPage parser */ static void fixed_page_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSPage *page = GXPS_PAGE (user_data); gint i; if (strcmp (element_name, "FixedPage") == 0) { for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Width") == 0) { if (!gxps_value_get_double_positive (values[i], &page->priv->width)) { gxps_parse_error (context, page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, "Width", NULL, error); return; } } else if (strcmp (names[i], "Height") == 0) { if (!gxps_value_get_double_positive (values[i], &page->priv->height)) { gxps_parse_error (context, page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, "Height", NULL, error); return; } } else if (strcmp (names[i], "xml:lang") == 0) { page->priv->lang = g_strdup (values[i]); } else if (strcmp (names[i], "ContentBox") == 0) { /* TODO */ } else if (strcmp (names[i], "BleedBox") == 0) { /* TODO */ } else if (strcmp (names[i], "Name") == 0) { page->priv->name = g_strdup (values[i]); } } } } static const GMarkupParser fixed_page_parser = { fixed_page_start_element, NULL, NULL, NULL }; static gboolean gxps_page_parse_fixed_page (GXPSPage *page, GError **error) { GInputStream *stream; GMarkupParseContext *ctx; stream = gxps_archive_open (page->priv->zip, page->priv->source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Page source %s not found in archive", page->priv->source); return FALSE; } ctx = g_markup_parse_context_new (&fixed_page_parser, 0, page, NULL); gxps_parse_stream (ctx, stream, error); g_object_unref (stream); g_markup_parse_context_free (ctx); return (*error != NULL) ? FALSE : TRUE; } /* Page Render Parser */ static GMarkupParser render_parser = { render_start_element, render_end_element, NULL, NULL }; void gxps_page_render_parser_push (GMarkupParseContext *context, GXPSRenderContext *ctx) { g_markup_parse_context_push (context, &render_parser, ctx); } static gboolean gxps_dash_array_parse (const gchar *dash, gdouble **dashes_out, guint *num_dashes_out) { gchar **items; gchar *stripped_dash; guint i; gdouble *dashes; guint num_dashes; stripped_dash = g_strstrip (g_strdup (dash)); items = g_strsplit (stripped_dash, " ", -1); g_free (stripped_dash); if (!items) return FALSE; num_dashes = g_strv_length (items); if (num_dashes % 2 != 0) { g_strfreev (items); return FALSE; } dashes = g_malloc (num_dashes * sizeof (gdouble)); for (i = 0; i < num_dashes; i++) { if (!gxps_value_get_double_non_negative (items[i], &dashes[i])) { g_free (dashes); g_strfreev (items); return FALSE; } } g_strfreev (items); *dashes_out = dashes; *num_dashes_out = num_dashes; return TRUE; } static cairo_line_cap_t gxps_line_cap_parse (const gchar *cap) { if (strcmp (cap, "Flat") == 0) return CAIRO_LINE_CAP_BUTT; else if (strcmp (cap, "Round") == 0) return CAIRO_LINE_CAP_ROUND; else if (strcmp (cap, "Square") == 0) return CAIRO_LINE_CAP_SQUARE; else if (strcmp (cap, "Triangle") == 0) GXPS_DEBUG (g_debug ("Unsupported dash cap Triangle")); return CAIRO_LINE_CAP_BUTT; } static cairo_line_join_t gxps_line_join_parse (const gchar *join) { if (strcmp (join, "Miter") == 0) return CAIRO_LINE_JOIN_MITER; else if (strcmp (join, "Bevel") == 0) return CAIRO_LINE_JOIN_BEVEL; else if (strcmp (join, "Round") == 0) return CAIRO_LINE_JOIN_ROUND; return CAIRO_LINE_JOIN_MITER; } typedef struct { GXPSRenderContext *ctx; gdouble opacity; cairo_pattern_t *opacity_mask; gboolean pop_resource_dict; } GXPSCanvas; static GXPSCanvas * gxps_canvas_new (GXPSRenderContext *ctx) { GXPSCanvas *canvas; canvas = g_slice_new0 (GXPSCanvas); canvas->ctx = ctx; /* Default values */ canvas->opacity = 1.0; canvas->pop_resource_dict = FALSE; return canvas; } static void gxps_canvas_free (GXPSCanvas *canvas) { if (G_UNLIKELY (!canvas)) return; cairo_pattern_destroy (canvas->opacity_mask); g_slice_free (GXPSCanvas, canvas); } static void canvas_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSCanvas *canvas = (GXPSCanvas *)user_data; if (strcmp (element_name, "Canvas.RenderTransform") == 0) { GXPSMatrix *matrix; matrix = gxps_matrix_new (canvas->ctx); gxps_matrix_parser_push (context, matrix); } else if (strcmp (element_name, "Canvas.OpacityMask") == 0) { GXPSBrush *brush; brush = gxps_brush_new (canvas->ctx); gxps_brush_parser_push (context, brush); } else if (strcmp (element_name, "Canvas.Resources") == 0) { GXPSResources *resources; if (canvas->pop_resource_dict) { gxps_parse_error (context, canvas->ctx->page->priv->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); return; } resources = gxps_archive_get_resources (canvas->ctx->page->priv->zip); gxps_resources_push_dict (resources); canvas->pop_resource_dict = TRUE; gxps_resources_parser_push (context, resources, canvas->ctx->page->priv->source); } else { render_start_element (context, element_name, names, values, canvas->ctx, error); } } static void canvas_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSCanvas *canvas = (GXPSCanvas *)user_data; if (strcmp (element_name, "Canvas.RenderTransform") == 0) { GXPSMatrix *matrix; matrix = g_markup_parse_context_pop (context); GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix->matrix.xx, matrix->matrix.yx, matrix->matrix.xy, matrix->matrix.yy, matrix->matrix.x0, matrix->matrix.y0)); cairo_transform (canvas->ctx->cr, &matrix->matrix); gxps_matrix_free (matrix); } else if (strcmp (element_name, "Canvas.OpacityMask") == 0) { GXPSBrush *brush; brush = g_markup_parse_context_pop (context); if (!canvas->opacity_mask) { canvas->opacity_mask = cairo_pattern_reference (brush->pattern); cairo_push_group (canvas->ctx->cr); } gxps_brush_free (brush); } else if (strcmp (element_name, "Canvas.Resources") == 0) { gxps_resources_parser_pop (context); } else { render_end_element (context, element_name, canvas->ctx, error); } } static void canvas_error (GMarkupParseContext *context, GError *error, gpointer user_data) { GXPSCanvas *canvas = (GXPSCanvas *)user_data; gxps_canvas_free (canvas); } static GMarkupParser canvas_parser = { canvas_start_element, canvas_end_element, NULL, NULL, canvas_error }; static void resource_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { if (strcmp (element_name, "PathGeometry") == 0) { GXPSPath *path = (GXPSPath *)user_data; gxps_path_parser_push (context, path); } else if (g_str_has_suffix (element_name, "Brush")) { GXPSPath *path = (GXPSPath *)user_data; GXPSBrush *brush; brush = gxps_brush_new (path->ctx); gxps_brush_parser_push (context, brush); } } static void resource_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { if (strcmp (element_name, "PathGeometry") == 0) { g_markup_parse_context_pop (context); } else if (g_str_has_suffix (element_name, "Brush")) { GXPSPath *path = (GXPSPath *)user_data; GXPSBrush *brush = g_markup_parse_context_pop (context); path->fill_pattern = cairo_pattern_reference (brush->pattern); gxps_brush_free (brush); } } static GMarkupParser resource_parser = { resource_start_element, resource_end_element, NULL, NULL, NULL }; static gboolean expand_resource (GXPSPage *page, const gchar *data, gpointer user_data) { gchar *resource_key; gchar *p; gsize len; GXPSResources *resources; const gchar *resource; GMarkupParseContext *context; gboolean ret = TRUE; if (!g_str_has_prefix (data, "{StaticResource ")) return FALSE; p = strstr (data, "}"); if (p == NULL) return FALSE; len = strlen ("{StaticResource "); resource_key = g_strndup (data + len, p - (data + len)); if (!resource_key || *resource_key == '\0') { g_free (resource_key); return FALSE; } resources = gxps_archive_get_resources (page->priv->zip); resource = gxps_resources_get_resource (resources, resource_key); g_free (resource_key); if (!resource) return FALSE; context = g_markup_parse_context_new (&resource_parser, 0, user_data, NULL); ret = g_markup_parse_context_parse (context, resource, strlen (resource), NULL) && g_markup_parse_context_end_parse (context, NULL); g_markup_parse_context_free (context); return ret; } static void render_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSRenderContext *ctx = (GXPSRenderContext *)user_data; if (strcmp (element_name, "Path") == 0) { GXPSPath *path; gint i; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); path = gxps_path_new (ctx); for (i = 0; names[i] != NULL; i++) { /* FIXME: if the resource gets expanded, that specific * resource will be already handled leading to a different * behavior of what we are actually doing without resources. * In an ideal world we would handle the resource without * special casing */ if (expand_resource (ctx->page, values[i], path)) { GXPS_DEBUG (g_message ("expanded resource: %s", names[i])); } else if (strcmp (names[i], "Data") == 0) { path->data = g_strdup (values[i]); } else if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "RenderTransform", values[i], error); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); } else if (strcmp (names[i], "Clip") == 0) { path->clip_data = g_strdup (values[i]); } else if (strcmp (names[i], "Fill") == 0) { if (!gxps_brush_solid_color_parse (values[i], ctx->page->priv->zip, 1., &path->fill_pattern)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "Fill", values[i], error); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("set_fill_pattern (solid)")); } else if (strcmp (names[i], "Stroke") == 0) { GXPS_DEBUG (g_message ("set_stroke_pattern (solid)")); if (!gxps_brush_solid_color_parse (values[i], ctx->page->priv->zip, 1., &path->stroke_pattern)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "Stroke", values[i], error); gxps_path_free (path); return; } } else if (strcmp (names[i], "StrokeThickness") == 0) { if (!gxps_value_get_double (values[i], &path->line_width)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "StrokeThickness", values[i], error); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("set_line_width (%f)", path->line_width)); } else if (strcmp (names[i], "StrokeDashArray") == 0) { if (!gxps_dash_array_parse (values[i], &path->dash, &path->dash_len)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "StrokeDashArray", values[i], error); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("set_dash")); } else if (strcmp (names[i], "StrokeDashOffset") == 0) { if (!gxps_value_get_double (values[i], &path->dash_offset)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "StrokeDashOffset", values[i], error); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("set_dash_offset (%f)", path->dash_offset)); } else if (strcmp (names[i], "StrokeDashCap") == 0) { path->line_cap = gxps_line_cap_parse (values[i]); GXPS_DEBUG (g_message ("set_line_cap (%s)", values[i])); } else if (strcmp (names[i], "StrokeLineJoin") == 0) { path->line_join = gxps_line_join_parse (values[i]); GXPS_DEBUG (g_message ("set_line_join (%s)", values[i])); } else if (strcmp (names[i], "StrokeMiterLimit") == 0) { if (!gxps_value_get_double (values[i], &path->miter_limit)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "StrokeMiterLimit", values[i], error); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("set_miter_limit (%f)", path->miter_limit)); } else if (strcmp (names[i], "Opacity") == 0) { if (!gxps_value_get_double (values[i], &path->opacity)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "Opacity", values[i], error); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("set_opacity (%f)", path->opacity)); } } if (path->opacity != 1.0) cairo_push_group (ctx->cr); gxps_path_parser_push (context, path); } else if (strcmp (element_name, "Glyphs") == 0) { GXPSGlyphs *glyphs; gchar *font_uri = NULL; gdouble font_size = -1; gdouble x = -1, y = -1; const gchar *text = NULL; const gchar *fill_color = NULL; const gchar *indices = NULL; const gchar *clip_data = NULL; gint bidi_level = 0; gboolean is_sideways = FALSE; gboolean italic = FALSE; gdouble opacity = 1.0; gint i; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "FontRenderingEmSize") == 0) { if (!gxps_value_get_double (values[i], &font_size)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "FontRenderingEmSize", values[i], error); g_free (font_uri); return; } } else if (strcmp (names[i], "FontUri") == 0) { font_uri = gxps_resolve_relative_path (ctx->page->priv->source, values[i]); } else if (strcmp (names[i], "OriginX") == 0) { if (!gxps_value_get_double (values[i], &x)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "OriginX", values[i], error); g_free (font_uri); return; } } else if (strcmp (names[i], "OriginY") == 0) { if (!gxps_value_get_double (values[i], &y)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "OriginY", values[i], error); g_free (font_uri); return; } } else if (strcmp (names[i], "UnicodeString") == 0) { text = values[i]; } else if (strcmp (names[i], "Fill") == 0) { fill_color = values[i]; } else if (strcmp (names[i], "Indices") == 0) { indices = values[i]; } else if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "RenderTransform", values[i], error); g_free (font_uri); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); } else if (strcmp (names[i], "Clip") == 0) { clip_data = values[i]; } else if (strcmp (names[i], "BidiLevel") == 0) { if (!gxps_value_get_int (values[i], &bidi_level)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "BidiLevel", values[i], error); g_free (font_uri); return; } } else if (strcmp (names[i], "IsSideways") == 0) { if (!gxps_value_get_boolean (values[i], &is_sideways)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "IsSideways", values[i], error); g_free (font_uri); return; } } else if (strcmp (names[i], "Opacity") == 0) { if (!gxps_value_get_double (values[i], &opacity)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "Opacity", values[i], error); g_free (font_uri); return; } } else if (strcmp (names[i], "StyleSimulations") == 0) { if (strcmp (values[i], "ItalicSimulation") == 0) { italic = TRUE; } } } if (!font_uri || font_size == -1 || x == -1 || y == -1) { if (!font_uri) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, "FontUri", NULL, error); } else if (font_size == -1) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, "FontRenderingEmSize", NULL, error); } else if (x == -1 || y == -1) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, (x == -1) ? "OriginX" : "OriginY", NULL, error); } g_free (font_uri); return; } /* GXPSGlyphs takes ownership of font_uri */ glyphs = gxps_glyphs_new (ctx, font_uri, font_size, x, y); glyphs->text = g_strdup (text); glyphs->indices = g_strdup (indices); glyphs->clip_data = g_strdup (clip_data); glyphs->bidi_level = bidi_level; glyphs->is_sideways = is_sideways; glyphs->italic = italic; glyphs->opacity = opacity; if (fill_color) { GXPS_DEBUG (g_message ("set_fill_pattern (solid)")); gxps_brush_solid_color_parse (fill_color, ctx->page->priv->zip, 1., &glyphs->fill_pattern); } if (glyphs->opacity != 1.0) cairo_push_group (glyphs->ctx->cr); gxps_glyphs_parser_push (context, glyphs); } else if (strcmp (element_name, "Canvas") == 0) { GXPSCanvas *canvas; gint i; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); canvas = gxps_canvas_new (ctx); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Canvas", "RenderTransform", values[i], error); gxps_canvas_free (canvas); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); } else if (strcmp (names[i], "Opacity") == 0) { if (!gxps_value_get_double (values[i], &canvas->opacity)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Canvas", "Opacity", values[i], error); gxps_canvas_free (canvas); return; } GXPS_DEBUG (g_message ("set_opacity (%f)", canvas->opacity)); } else if (strcmp (names[i], "Clip") == 0) { if (!gxps_path_parse (values[i], ctx->cr, error)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Canvas", "Clip", values[i], error); gxps_canvas_free (canvas); return; } GXPS_DEBUG (g_message ("clip")); cairo_clip (ctx->cr); } } if (canvas->opacity != 1.0) cairo_push_group (canvas->ctx->cr); g_markup_parse_context_push (context, &canvas_parser, canvas); } else if (strcmp (element_name, "FixedPage.Resources") == 0) { GXPSResources *resources; resources = gxps_archive_get_resources (ctx->page->priv->zip); gxps_resources_parser_push (context, resources, ctx->page->priv->source); } else if (strcmp (element_name, "FixedPage") == 0) { /* Do Nothing */ } else { /* TODO: error */ } } static void render_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSRenderContext *ctx = (GXPSRenderContext *)user_data; if (strcmp (element_name, "Path") == 0) { GXPSPath *path; path = g_markup_parse_context_pop (context); if (!path->data) { GXPS_DEBUG (g_message ("restore")); /* Something may have been drawn in a PathGeometry */ if (path->opacity != 1.0) { cairo_pop_group_to_source (ctx->cr); cairo_paint_with_alpha (ctx->cr, path->opacity); } cairo_restore (ctx->cr); gxps_path_free (path); return; } cairo_set_fill_rule (ctx->cr, path->fill_rule); if (path->clip_data) { if (!gxps_path_parse (path->clip_data, ctx->cr, error)) { if (path->opacity != 1.0) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); gxps_path_free (path); return; } GXPS_DEBUG (g_message ("clip")); cairo_clip (ctx->cr); } if (!gxps_path_parse (path->data, ctx->cr, error)) { if (path->opacity != 1.0) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); gxps_path_free (path); return; } if (path->stroke_pattern) { cairo_set_line_width (ctx->cr, path->line_width); if (path->dash && path->dash_len > 0) cairo_set_dash (ctx->cr, path->dash, path->dash_len, path->dash_offset); /* FIXME: square cap doesn't work with dashed lines */ // cairo_set_line_cap (ctx->cr, path->line_cap); cairo_set_line_join (ctx->cr, path->line_join); cairo_set_miter_limit (ctx->cr, path->miter_limit); } if (path->opacity_mask) { gdouble x1 = 0, y1 = 0, x2 = 0, y2 = 0; cairo_path_t *cairo_path; if (path->stroke_pattern) cairo_stroke_extents (ctx->cr, &x1, &y1, &x2, &y2); else if (path->fill_pattern) cairo_fill_extents (ctx->cr, &x1, &y1, &x2, &y2); cairo_path = cairo_copy_path (ctx->cr); cairo_new_path (ctx->cr); cairo_rectangle (ctx->cr, x1, y1, x2 - x1, y2 - y1); cairo_clip (ctx->cr); cairo_push_group (ctx->cr); cairo_append_path (ctx->cr, cairo_path); cairo_path_destroy (cairo_path); } if (path->fill_pattern) { GXPS_DEBUG (g_message ("fill")); cairo_set_source (ctx->cr, path->fill_pattern); if (path->stroke_pattern) cairo_fill_preserve (ctx->cr); else cairo_fill (ctx->cr); } if (path->stroke_pattern) { GXPS_DEBUG (g_message ("stroke")); cairo_set_source (ctx->cr, path->stroke_pattern); cairo_stroke (ctx->cr); } if (path->opacity_mask) { cairo_pop_group_to_source (ctx->cr); cairo_mask (ctx->cr, path->opacity_mask); } if (path->opacity != 1.0) { cairo_pop_group_to_source (ctx->cr); cairo_paint_with_alpha (ctx->cr, path->opacity); } gxps_path_free (path); GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Glyphs") == 0) { GXPSGlyphs *glyphs; gchar *utf8; cairo_text_cluster_t *cluster_list = NULL; gint num_clusters; cairo_glyph_t *glyph_list = NULL; gint num_glyphs; cairo_matrix_t ctm, font_matrix; cairo_font_face_t *font_face; cairo_font_options_t *font_options; cairo_scaled_font_t *scaled_font; gboolean use_show_text_glyphs; gboolean success; glyphs = g_markup_parse_context_pop (context); font_face = gxps_fonts_get_font (ctx->page->priv->zip, glyphs->font_uri, error); if (!font_face) { if (glyphs->opacity_mask) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); if (glyphs->opacity != 1.0) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); gxps_glyphs_free (glyphs); GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); return; } if (glyphs->clip_data) { if (!gxps_path_parse (glyphs->clip_data, ctx->cr, error)) { if (glyphs->opacity_mask) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); if (glyphs->opacity != 1.0) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); gxps_glyphs_free (glyphs); GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); return; } GXPS_DEBUG (g_message ("clip")); cairo_clip (ctx->cr); } font_options = cairo_font_options_create (); cairo_get_font_options (ctx->cr, font_options); cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF); cairo_matrix_init_identity (&font_matrix); cairo_matrix_scale (&font_matrix, glyphs->em_size, glyphs->em_size); cairo_get_matrix (ctx->cr, &ctm); /* italics is 20 degrees slant. 0.342 = sin(20 deg) */ if (glyphs->italic) font_matrix.xy = glyphs->em_size * -0.342; if (glyphs->is_sideways) cairo_matrix_rotate (&font_matrix, -G_PI_2); scaled_font = cairo_scaled_font_create (font_face, &font_matrix, &ctm, font_options); cairo_font_options_destroy (font_options); /* UnicodeString may begin with escape sequence "{}" */ utf8 = glyphs->text; if (utf8 && g_str_has_prefix (utf8, "{}")) utf8 += 2; use_show_text_glyphs = cairo_surface_has_show_text_glyphs (cairo_get_target (ctx->cr)); success = gxps_glyphs_to_cairo_glyphs (glyphs, scaled_font, utf8, &glyph_list, &num_glyphs, use_show_text_glyphs ? &cluster_list : NULL, use_show_text_glyphs ? &num_clusters : NULL, error); if (!success) { if (glyphs->opacity_mask) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); if (glyphs->opacity != 1.0) cairo_pattern_destroy (cairo_pop_group (ctx->cr)); gxps_glyphs_free (glyphs); cairo_scaled_font_destroy (scaled_font); GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); return; } if (glyphs->fill_pattern) cairo_set_source (ctx->cr, glyphs->fill_pattern); GXPS_DEBUG (g_message ("show_text (%s)", glyphs->text)); cairo_set_scaled_font (ctx->cr, scaled_font); if (use_show_text_glyphs) { cairo_show_text_glyphs (ctx->cr, utf8, -1, glyph_list, num_glyphs, cluster_list, num_clusters, 0); g_free (cluster_list); } else { cairo_show_glyphs (ctx->cr, glyph_list, num_glyphs); } if (glyphs->opacity_mask) { cairo_pop_group_to_source (ctx->cr); cairo_mask (ctx->cr, glyphs->opacity_mask); } if (glyphs->opacity != 1.0) { cairo_pop_group_to_source (ctx->cr); cairo_paint_with_alpha (ctx->cr, glyphs->opacity); } g_free (glyph_list); gxps_glyphs_free (glyphs); cairo_scaled_font_destroy (scaled_font); GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Canvas") == 0) { GXPSCanvas *canvas; canvas = g_markup_parse_context_pop (context); if (canvas->opacity_mask) { cairo_pop_group_to_source (ctx->cr); cairo_mask (ctx->cr, canvas->opacity_mask); } if (canvas->opacity != 1.0) { cairo_pop_group_to_source (ctx->cr); cairo_paint_with_alpha (ctx->cr, canvas->opacity); } cairo_restore (ctx->cr); GXPS_DEBUG (g_message ("restore")); if (canvas->pop_resource_dict) { GXPSResources *resources; resources = gxps_archive_get_resources (ctx->page->priv->zip); gxps_resources_pop_dict (resources); } gxps_canvas_free (canvas); } else if (strcmp (element_name, "FixedPage.Resources") == 0) { gxps_resources_parser_pop (context); } else if (strcmp (element_name, "FixedPage") == 0) { /* Do Nothing */ } else { /* TODO: error */ } } static gboolean gxps_page_parse_for_rendering (GXPSPage *page, cairo_t *cr, GError **error) { GInputStream *stream; GMarkupParseContext *context; GXPSRenderContext ctx; GError *err = NULL; stream = gxps_archive_open (page->priv->zip, page->priv->source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Page source %s not found in archive", page->priv->source); return FALSE; } ctx.page = page; ctx.cr = cr; context = g_markup_parse_context_new (&render_parser, 0, &ctx, NULL); gxps_parse_stream (context, stream, &err); g_object_unref (stream); g_markup_parse_context_free (context); if (g_error_matches (err, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER)) { g_propagate_error (error, err); } else if (err) { g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error rendering page %s: %s", page->priv->source, err->message); g_error_free (err); } return (*error != NULL) ? FALSE : TRUE; } /* Links */ typedef struct { GXPSPage *page; cairo_t *cr; GList *st; GList *links; gboolean do_transform; } GXPSLinksContext; typedef struct { gchar *data; gchar *uri; } GXPSPathLink; static void links_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSLinksContext *ctx = (GXPSLinksContext *)user_data; if (strcmp (element_name, "Canvas") == 0) { gint i; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Canvas", "RenderTransform", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); return; } else if (strcmp (names[i], "Clip") == 0) { /* FIXME: do we really need clips? */ if (!gxps_path_parse (values[i], ctx->cr, error)) return; GXPS_DEBUG (g_message ("clip")); cairo_clip (ctx->cr); } } } else if (strcmp (element_name, "Path") == 0) { gint i; GXPSPathLink *path_link; const gchar *data = NULL; const gchar *link_uri = NULL; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Data") == 0) { data = values[i]; } else if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "RenderTransform", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); } else if (strcmp (names[i], "FixedPage.NavigateUri") == 0) { link_uri = values[i]; } } path_link = g_slice_new0 (GXPSPathLink); if (link_uri) { path_link->data = data ? g_strdup (data) : NULL; path_link->uri = gxps_resolve_relative_path (ctx->page->priv->source, link_uri); } ctx->st = g_list_prepend (ctx->st, path_link); } else if (strcmp (element_name, "Glyphs") == 0) { gint i; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "RenderTransform", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); } else if (strcmp (names[i], "FixedPage.NavigateUri") == 0) { /* TODO */ } } } else if (strcmp (element_name, "Canvas.RenderTransform") == 0 || strcmp (element_name, "Path.RenderTransform") == 0 || strcmp (element_name, "Glyphs.RenderTransform") == 0 ) { ctx->do_transform = TRUE; } else if (strcmp (element_name, "MatrixTransform") == 0) { gint i; if (!ctx->do_transform) { return; } for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Matrix") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "MatrixTransform", "Matrix", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); return; } } } } static void links_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSLinksContext *ctx = (GXPSLinksContext *)user_data; if (strcmp (element_name, "Canvas") == 0) { GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Path") == 0) { GXPSPathLink *path_link; path_link = (GXPSPathLink *)ctx->st->data; ctx->st = g_list_delete_link (ctx->st, ctx->st); if (path_link->uri) { GXPSLink *link; gdouble x1, y1, x2, y2; cairo_rectangle_t area; if (path_link->data) gxps_path_parse (path_link->data, ctx->cr, error); cairo_path_extents (ctx->cr, &x1, &y1, &x2, &y2); cairo_user_to_device (ctx->cr, &x1, &y1); cairo_user_to_device (ctx->cr, &x2, &y2); area.x = x1; area.y = y1; area.width = x2 - x1; area.height = y2 - y1; link = _gxps_link_new (ctx->page->priv->zip, &area, path_link->uri); ctx->links = g_list_prepend (ctx->links, link); g_free (path_link->uri); } g_free (path_link->data); g_slice_free (GXPSPathLink, path_link); cairo_new_path (ctx->cr); GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Glyphs") == 0) { GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Canvas.RenderTransform") == 0 || strcmp (element_name, "Path.RenderTransform") == 0 || strcmp (element_name, "Glyphs.RenderTransform") == 0 ) { ctx->do_transform = FALSE; } } static const GMarkupParser links_parser = { links_start_element, links_end_element, NULL, NULL, NULL }; static GList * gxps_page_parse_links (GXPSPage *page, cairo_t *cr, GError **error) { GInputStream *stream; GXPSLinksContext ctx; GMarkupParseContext *context; stream = gxps_archive_open (page->priv->zip, page->priv->source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Page source %s not found in archive", page->priv->source); return FALSE; } ctx.cr = cr; ctx.page = page; ctx.st = NULL; ctx.links = NULL; context = g_markup_parse_context_new (&links_parser, 0, &ctx, NULL); gxps_parse_stream (context, stream, error); g_object_unref (stream); g_markup_parse_context_free (context); return ctx.links; } typedef struct { GXPSPage *page; cairo_t *cr; GList *st; GHashTable *anchors; gboolean do_transform; } GXPSAnchorsContext; typedef struct { gchar *data; gchar *name; } GXPSPathAnchor; static void anchors_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSAnchorsContext *ctx = (GXPSAnchorsContext *)user_data; if (strcmp (element_name, "Canvas") == 0) { gint i; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Canvas", "RenderTransform", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); return; } } } else if (strcmp (element_name, "Path") == 0) { gint i; GXPSPathAnchor *path_anchor; const gchar *data = NULL; const gchar *name = NULL; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Data") == 0) { data = values[i]; } else if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Path", "RenderTransform", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); } else if (strcmp (names[i], "Name") == 0) { name = values[i]; } } path_anchor = g_slice_new0 (GXPSPathAnchor); if (name) { path_anchor->data = data ? g_strdup (data) : NULL; path_anchor->name = g_strdup (name); } ctx->st = g_list_prepend (ctx->st, path_anchor); } else if (strcmp (element_name, "Glyphs") == 0) { gint i; GXPS_DEBUG (g_message ("save")); cairo_save (ctx->cr); for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "RenderTransform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "Glyphs", "RenderTransform", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); } else if (strcmp (names[i], "Name") == 0) { /* TODO */ } } } else if (strcmp (element_name, "Canvas.RenderTransform") == 0 || strcmp (element_name, "Path.RenderTransform") == 0 || strcmp (element_name, "Glyphs.RenderTransform") == 0 ) { ctx->do_transform = TRUE; } else if (strcmp (element_name, "MatrixTransform") == 0) { gint i; if (!ctx->do_transform) { return; } for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Matrix") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "MatrixTransform", "Matrix", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (ctx->cr, &matrix); return; } } } } static void anchors_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSAnchorsContext *ctx = (GXPSAnchorsContext *)user_data; if (strcmp (element_name, "Canvas") == 0) { GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Path") == 0) { GXPSPathAnchor *path_anchor; path_anchor = (GXPSPathAnchor *)ctx->st->data; ctx->st = g_list_delete_link (ctx->st, ctx->st); if (path_anchor->name) { gdouble x1, y1, x2, y2; cairo_rectangle_t *rect; if (path_anchor->data) gxps_path_parse (path_anchor->data, ctx->cr, error); cairo_path_extents (ctx->cr, &x1, &y1, &x2, &y2); cairo_user_to_device (ctx->cr, &x1, &y1); cairo_user_to_device (ctx->cr, &x2, &y2); rect = g_slice_new (cairo_rectangle_t); rect->x = x1; rect->y = y1; rect->width = x2 - x1; rect->height = y2 - y1; g_hash_table_insert (ctx->anchors, path_anchor->name, rect); } g_free (path_anchor->data); g_slice_free (GXPSPathAnchor, path_anchor); cairo_new_path (ctx->cr); GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Glyphs") == 0) { GXPS_DEBUG (g_message ("restore")); cairo_restore (ctx->cr); } else if (strcmp (element_name, "Canvas.RenderTransform") == 0 || strcmp (element_name, "Path.RenderTransform") == 0 || strcmp (element_name, "Glyphs.RenderTransform") == 0 ) { ctx->do_transform = FALSE; } } static const GMarkupParser anchors_parser = { anchors_start_element, anchors_end_element, NULL, NULL, NULL }; static void anchor_area_free (cairo_rectangle_t *area) { g_slice_free (cairo_rectangle_t, area); } static gboolean gxps_page_parse_anchors (GXPSPage *page, cairo_t *cr, GError **error) { GInputStream *stream; GXPSAnchorsContext ctx; GMarkupParseContext *context; stream = gxps_archive_open (page->priv->zip, page->priv->source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Page source %s not found in archive", page->priv->source); return FALSE; } ctx.cr = cr; ctx.page = page; ctx.st = NULL; ctx.anchors = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)anchor_area_free); context = g_markup_parse_context_new (&anchors_parser, 0, &ctx, NULL); gxps_parse_stream (context, stream, error); g_object_unref (stream); g_markup_parse_context_free (context); if (g_hash_table_size (ctx.anchors) > 0) { page->priv->has_anchors = TRUE; page->priv->anchors = ctx.anchors; } else { page->priv->has_anchors = FALSE; g_hash_table_destroy (ctx.anchors); } return TRUE; } static void gxps_page_finalize (GObject *object) { GXPSPage *page = GXPS_PAGE (object); g_clear_object (&page->priv->zip); g_clear_pointer (&page->priv->source, g_free); g_clear_error (&page->priv->init_error); g_clear_pointer (&page->priv->lang, g_free); g_clear_pointer (&page->priv->name, g_free); g_clear_pointer (&page->priv->image_cache, g_hash_table_destroy); g_clear_pointer (&page->priv->anchors, g_hash_table_destroy); page->priv->has_anchors = FALSE; G_OBJECT_CLASS (gxps_page_parent_class)->finalize (object); } static void gxps_page_init (GXPSPage *page) { page->priv = G_TYPE_INSTANCE_GET_PRIVATE (page, GXPS_TYPE_PAGE, GXPSPagePrivate); page->priv->has_anchors = TRUE; } static void gxps_page_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GXPSPage *page = GXPS_PAGE (object); switch (prop_id) { case PROP_ARCHIVE: page->priv->zip = g_value_dup_object (value); break; case PROP_SOURCE: page->priv->source = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gxps_page_class_init (GXPSPageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = gxps_page_set_property; object_class->finalize = gxps_page_finalize; g_object_class_install_property (object_class, PROP_ARCHIVE, g_param_spec_object ("archive", "Archive", "The document archive", GXPS_TYPE_ARCHIVE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SOURCE, g_param_spec_string ("source", "Source", "The Page Source File", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_type_class_add_private (klass, sizeof (GXPSPagePrivate)); } static gboolean gxps_page_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { GXPSPage *page = GXPS_PAGE (initable); if (page->priv->initialized) { if (page->priv->init_error) { g_propagate_error (error, g_error_copy (page->priv->init_error)); return FALSE; } return TRUE; } page->priv->initialized = TRUE; if (!gxps_page_parse_fixed_page (page, &page->priv->init_error)) { g_propagate_error (error, g_error_copy (page->priv->init_error)); return FALSE; } if (!page->priv->lang || page->priv->width == -1 || page->priv->height == -1) { if (!page->priv->lang) { g_set_error_literal (&page->priv->init_error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_INVALID, "Missing required attribute xml:lang"); } else { g_set_error_literal (&page->priv->init_error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_INVALID, "Missing page size"); } g_propagate_error (error, g_error_copy (page->priv->init_error)); return FALSE; } return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = gxps_page_initable_init; } GXPSPage * _gxps_page_new (GXPSArchive *zip, const gchar *source, GError **error) { return g_initable_new (GXPS_TYPE_PAGE, NULL, error, "archive", zip, "source", source, NULL); } /** * gxps_page_get_size: * @page: a #GXPSPage * @width: (out) (allow-none): return location for the page width * @height: (out) (allow-none): return location for the page height * * Gets the size of the page. */ void gxps_page_get_size (GXPSPage *page, gdouble *width, gdouble *height) { g_return_if_fail (GXPS_IS_PAGE (page)); if (width) *width = page->priv->width; if (height) *height = page->priv->height; } /** * gxps_page_render: * @page: a #GXPSPage * @cr: a cairo context to render to * @error: #GError for error reporting, or %NULL to ignore * * Render the page to the given cairo context. In case of * error, %FALSE is returned and @error is filled with * information about error. * * Returns: %TRUE if page was successfully rendered, * %FALSE otherwise. */ gboolean gxps_page_render (GXPSPage *page, cairo_t *cr, GError **error) { g_return_val_if_fail (GXPS_IS_PAGE (page), FALSE); g_return_val_if_fail (cr != NULL, FALSE); return gxps_page_parse_for_rendering (page, cr, error); } /** * gxps_page_get_links: * @page: a #GXPSPage * @error: #GError for error reporting, or %NULL to ignore * * Gets a list of #GXPSLink items that map from a location * in @page to a #GXPSLinkTarget. Items in the list should * be freed with gxps_link_free() and the list itself with * g_list_free() when done. * * Returns: (element-type GXPS.Link) (transfer full): a #GList * of #GXPSLink items. */ GList * gxps_page_get_links (GXPSPage *page, GError **error) { cairo_surface_t *surface; cairo_t *cr; GList *links; cairo_rectangle_t extents; g_return_val_if_fail (GXPS_IS_PAGE (page), NULL); extents.x = extents.y = 0; extents.width = page->priv->width; extents.height = page->priv->height; surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR, &extents); cr = cairo_create (surface); cairo_surface_destroy (surface); links = gxps_page_parse_links (page, cr, error); cairo_destroy (cr); return links; } /** * gxps_page_get_anchor_destination: * @page: a #GXPSPage * @anchor: the name of an anchor in @page * @area: (out): return location for page area of @anchor * @error: #GError for error reporting, or %NULL to ignore * * Gets the rectangle of @page corresponding to the destination * of the given anchor. If @anchor is not found in @page, %FALSE * will be returned and @error will contain %GXPS_PAGE_ERROR_INVALID_ANCHOR * * Returns: %TRUE if the destination for the anchor was found in page * and @area contains the rectangle, %FALSE otherwise. */ gboolean gxps_page_get_anchor_destination (GXPSPage *page, const gchar *anchor, cairo_rectangle_t *area, GError **error) { cairo_rectangle_t *anchor_area; g_return_val_if_fail (GXPS_IS_PAGE (page), FALSE); g_return_val_if_fail (anchor != NULL, FALSE); g_return_val_if_fail (area != NULL, FALSE); if (!page->priv->has_anchors) return FALSE; if (!page->priv->anchors) { cairo_surface_t *surface; cairo_t *cr; cairo_rectangle_t extents; gboolean success; extents.x = extents.y = 0; extents.width = page->priv->width; extents.height = page->priv->height; surface = cairo_recording_surface_create (CAIRO_CONTENT_COLOR, &extents); cr = cairo_create (surface); cairo_surface_destroy (surface); success = gxps_page_parse_anchors (page, cr, error); cairo_destroy (cr); if (!success) return FALSE; } anchor_area = g_hash_table_lookup (page->priv->anchors, anchor); if (!anchor_area) { g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_INVALID_ANCHOR, "Invalid anchor '%s' for page", anchor); return FALSE; } *area = *anchor_area; return TRUE; } 07070100000038000081A40000000000000000000000016447D41100000DB8000000000000000000000000000000000000002400000000libgxps-0.3.2+5/libgxps/gxps-page.h/* GXPSPage * * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_PAGE_H__ #define __GXPS_PAGE_H__ #include <glib-object.h> #include <gio/gio.h> #include <cairo.h> #include <libgxps/gxps-version.h> G_BEGIN_DECLS #define GXPS_TYPE_PAGE (gxps_page_get_type ()) #define GXPS_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_PAGE, GXPSPage)) #define GXPS_PAGE_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_PAGE, GXPSPageClass)) #define GXPS_IS_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_PAGE)) #define GXPS_IS_PAGE_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_PAGE)) #define GXPS_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_PAGE, GXPSPageClass)) /** * GXPS_PAGE_ERROR: * * Error domain for #GXPSPage. Errors in this domain will be from * #GXPSPageError enumeration. * See #GError for more information on error domains. */ #define GXPS_PAGE_ERROR (gxps_page_error_quark ()) /** * GXPSPageError: * @GXPS_PAGE_ERROR_INVALID: The page is invalid. * @GXPS_PAGE_ERROR_RENDER: Error rendering the page. * @GXPS_PAGE_ERROR_INVALID_ANCHOR: Anchor is invalid for the page. * * Error codes returned by #GXPSPage functions */ typedef enum { GXPS_PAGE_ERROR_INVALID, GXPS_PAGE_ERROR_RENDER, GXPS_PAGE_ERROR_INVALID_ANCHOR } GXPSPageError; typedef struct _GXPSPage GXPSPage; typedef struct _GXPSPageClass GXPSPageClass; typedef struct _GXPSPagePrivate GXPSPagePrivate; /** * GXPSPage: * * The <structname>GXPSPage</structname> struct contains * only private fields and should not be directly accessed. */ struct _GXPSPage { GObject parent; /*< private >*/ GXPSPagePrivate *priv; }; struct _GXPSPageClass { GObjectClass parent_class; }; GXPS_AVAILABLE_IN_ALL GType gxps_page_get_type (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL GQuark gxps_page_error_quark (void) G_GNUC_CONST; GXPS_AVAILABLE_IN_ALL void gxps_page_get_size (GXPSPage *page, gdouble *width, gdouble *height); GXPS_AVAILABLE_IN_ALL gboolean gxps_page_render (GXPSPage *page, cairo_t *cr, GError **error); GXPS_AVAILABLE_IN_ALL GList *gxps_page_get_links (GXPSPage *page, GError **error); GXPS_AVAILABLE_IN_ALL gboolean gxps_page_get_anchor_destination (GXPSPage *page, const gchar *anchor, cairo_rectangle_t *area, GError **error); G_END_DECLS #endif /* __GXPS_PAGE_H__ */ 07070100000039000081A40000000000000000000000016447D411000030A5000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/libgxps/gxps-parse-utils.c/* * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <glib.h> #include "gxps-parse-utils.h" #include "gxps-private.h" #define BUFFER_SIZE 4096 /* GXPSCharsetConverter. Based on GeditSmartCharsetConverter */ typedef struct _GXPSCharsetConverter { GObject parent; GCharsetConverter *conv; gboolean is_utf8; } GXPSCharsetConverter; typedef struct _GXPSCharsetConverterClass { GObjectClass parent_class; } GXPSCharsetConverterClass; static GType gxps_charset_converter_get_type (void) G_GNUC_CONST; static void gxps_charset_converter_iface_init (GConverterIface *iface); #define GXPS_TYPE_CHARSET_CONVERTER (gxps_charset_converter_get_type()) #define GXPS_CHARSET_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_CHARSET_CONVERTER, GXPSCharsetConverter)) G_DEFINE_TYPE_WITH_CODE (GXPSCharsetConverter, gxps_charset_converter, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_CONVERTER, gxps_charset_converter_iface_init)) static void gxps_charset_converter_finalize (GObject *object) { GXPSCharsetConverter *conv = GXPS_CHARSET_CONVERTER (object); g_clear_object (&conv->conv); G_OBJECT_CLASS (gxps_charset_converter_parent_class)->finalize (object); } static void gxps_charset_converter_init (GXPSCharsetConverter *converter) { } static void gxps_charset_converter_class_init (GXPSCharsetConverterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxps_charset_converter_finalize; } static GConverterResult gxps_charset_converter_convert (GConverter *converter, const void *inbuf, gsize inbuf_size, void *outbuf, gsize outbuf_size, GConverterFlags flags, gsize *bytes_read, gsize *bytes_written, GError **error) { GXPSCharsetConverter *conv = GXPS_CHARSET_CONVERTER (converter); if (!conv->conv && !conv->is_utf8) { const gchar *end; if (g_utf8_validate (inbuf, inbuf_size, &end)) { conv->is_utf8 = TRUE; } else if ((inbuf_size - (end - (gchar *)inbuf)) < 6) { conv->is_utf8 = TRUE; } else { conv->conv = g_charset_converter_new ("UTF-8", "UTF-16", NULL); } } /* if the encoding is utf8 just redirect the input to the output */ if (conv->is_utf8) { gsize size; GConverterResult ret; size = MIN (inbuf_size, outbuf_size); memcpy (outbuf, inbuf, size); *bytes_read = size; *bytes_written = size; ret = G_CONVERTER_CONVERTED; if (flags & G_CONVERTER_INPUT_AT_END) ret = G_CONVERTER_FINISHED; else if (flags & G_CONVERTER_FLUSH) ret = G_CONVERTER_FLUSHED; return ret; } return g_converter_convert (G_CONVERTER (conv->conv), inbuf, inbuf_size, outbuf, outbuf_size, flags, bytes_read, bytes_written, error); } static void gxps_charset_converter_reset (GConverter *converter) { GXPSCharsetConverter *conv = GXPS_CHARSET_CONVERTER (converter); g_clear_object (&conv->conv); conv->is_utf8 = FALSE; } static void gxps_charset_converter_iface_init (GConverterIface *iface) { iface->convert = gxps_charset_converter_convert; iface->reset = gxps_charset_converter_reset; } static GXPSCharsetConverter * gxps_charset_converter_new (void) { return (GXPSCharsetConverter *)g_object_new (GXPS_TYPE_CHARSET_CONVERTER, NULL); } #define utf8_has_bom(x) (x[0] == 0xef && x[1] == 0xbb && x[2] == 0xbf) gboolean gxps_parse_stream (GMarkupParseContext *context, GInputStream *stream, GError **error) { GXPSCharsetConverter *converter; GInputStream *cstream; guchar buffer[BUFFER_SIZE]; gssize bytes_read; gboolean has_bom; gint line, column; gboolean retval = TRUE; converter = gxps_charset_converter_new (); cstream = g_converter_input_stream_new (stream, G_CONVERTER (converter)); g_object_unref (converter); do { bytes_read = g_input_stream_read (cstream, buffer, BUFFER_SIZE, NULL, error); if (bytes_read < 0) { retval = FALSE; break; } g_markup_parse_context_get_position (context, &line, &column); has_bom = line == 1 && column == 1 && bytes_read >= 3 && utf8_has_bom (buffer); if (!g_markup_parse_context_parse (context, has_bom ? (const gchar *)buffer + 3 : (const gchar *)buffer, has_bom ? bytes_read - 3 : bytes_read, error)) { retval = FALSE; break; } } while (bytes_read > 0); if (retval) g_markup_parse_context_end_parse (context, error); g_object_unref (cstream); return retval; } void gxps_parse_error (GMarkupParseContext *context, const gchar *source, GMarkupError error_type, const gchar *element_name, const gchar *attribute_name, const gchar *content, GError **error) { gint line, column; g_markup_parse_context_get_position (context, &line, &column); switch (error_type) { case G_MARKUP_ERROR_UNKNOWN_ELEMENT: g_set_error (error, G_MARKUP_ERROR, error_type, "%s:%d:%d invalid element '%s'", source, line, column, element_name); break; case G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE: g_set_error (error, G_MARKUP_ERROR, error_type, "%s:%d:%d unknown attribute '%s' of element '%s'", source, line, column, attribute_name, element_name); break; case G_MARKUP_ERROR_INVALID_CONTENT: if (attribute_name) { g_set_error (error, G_MARKUP_ERROR, error_type, "%s:%d:%d invalid content in attribute '%s' of element '%s': %s", source, line, column, attribute_name, element_name, content); } else { g_set_error (error, G_MARKUP_ERROR, error_type, "%s:%d:%d invalid content in element '%s': %s", source, line, column, element_name, content); } break; case G_MARKUP_ERROR_MISSING_ATTRIBUTE: g_set_error (error, G_MARKUP_ERROR, error_type, "%s:%d:%d missing attribute '%s' of element '%s'", source, line, column, attribute_name, element_name); break; default: break; } } gboolean gxps_value_get_int (const gchar *value, gint *int_value) { gint64 result; gchar *endptr; if (!value) return FALSE; errno = 0; result = g_ascii_strtoll (value, &endptr, 10); if (errno || endptr == value || result > G_MAXINT || result < G_MININT) return FALSE; *int_value = result; return TRUE; } gboolean gxps_value_get_double (const gchar *value, gdouble *double_value) { gdouble result; gchar *endptr; if (!value) return FALSE; errno = 0; result = g_ascii_strtod (value, &endptr); if (errno || endptr == value) return FALSE; *double_value = result; return TRUE; } gboolean gxps_value_get_boolean (const gchar *value, gboolean *boolean_value) { if (!value) return FALSE; if (strcmp (value, "true") == 0) { *boolean_value = TRUE; return TRUE; } else if (strcmp (value, "false") == 0) { *boolean_value = FALSE; return TRUE; } return FALSE; } gboolean gxps_value_get_double_positive (const gchar *value, gdouble *double_value) { if (!gxps_value_get_double (value, double_value)) return FALSE; return *double_value >= 1; } gboolean gxps_value_get_double_non_negative (const gchar *value, gdouble *double_value) { if (!gxps_value_get_double (value, double_value)) return FALSE; return *double_value >= 0; } gboolean gxps_point_parse (const gchar *point, gdouble *x, gdouble *y) { gchar *p; p = g_strrstr (point, ","); if (!p) return FALSE; if (x) { gchar *str; str = g_strndup (point, p - point); if (!gxps_value_get_double (str, x)) { g_free (str); return FALSE; } g_free (str); } if (y) { if (!gxps_value_get_double (++p, y)) return FALSE; } return TRUE; } void gxps_parse_skip_number (gchar **iter, const gchar *end) { gchar *p = *iter; p++; while (p != end && g_ascii_isdigit (*p)) p++; if (p == end) { *iter = p; return; } if (*p == '.') p++; while (p != end && g_ascii_isdigit (*p)) p++; if (p == end) { *iter = p; return; } if (*p == 'e' || *p == 'E') p++; if (p == end) { *iter = p; return; } if (*p == '+' || *p == '-') p++; while (p != end && g_ascii_isdigit (*p)) p++; *iter = p; } /* NOTE: Taken from glocalfile. Because we always need to use / on all platforms */ static char * canonicalize_filename (const char *filename) { char *canon, *start, *p, *q; char *cwd; int i; if (!g_path_is_absolute (filename)) { cwd = g_get_current_dir (); canon = g_build_path ("/", cwd, filename, NULL); g_free (cwd); } else canon = g_strdup (filename); start = (char *)g_path_skip_root (canon); if (start == NULL) { /* This shouldn't really happen, as g_get_current_dir() should * return an absolute pathname, but bug 573843 shows this is * not always happening */ g_free (canon); return g_build_path ("/", "/", filename, NULL); } /* POSIX allows double slashes at the start to * mean something special (as does windows too). * So, "//" != "/", but more than two slashes * is treated as "/". */ i = 0; for (p = start - 1; (p >= canon) && (*p == '/'); p--) i++; if (i > 2) { i -= 1; start -= i; memmove (start, start+i, strlen (start+i)+1); } /* Make sure we're using the canonical dir separator */ p++; while (p < start && *p == '/') *p++ = '/'; p = start; while (*p != 0) { if (p[0] == '.' && (p[1] == 0 || p[1] == '/')) { memmove (p, p+1, strlen (p+1)+1); } else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || p[2] == '/')) { q = p + 2; /* Skip previous separator */ p = p - 2; if (p < start) p = start; while (p > start && *p != '/') p--; if (*p == '/') *p++ = '/'; memmove (p, q, strlen (q)+1); } else { /* Skip until next separator */ while (*p != 0 && *p != '/') p++; if (*p != 0) { /* Canonicalize one separator */ *p++ = '/'; } } /* Remove additional separators */ q = p; while (*q && *q == '/') q++; if (p != q) memmove (p, q, strlen (q)+1); } /* Remove trailing slashes */ if (p > start && *(p-1) == '/') *(p-1) = 0; return canon; } gchar * gxps_resolve_relative_path (const gchar *source, const gchar *target) { gchar *dirname; gchar *abs_path; gchar *retval; if (target[0] == '/') return g_strdup (target); dirname = g_path_get_dirname (source); if (strlen (dirname) == 1 && dirname[0] == '.') dirname[0] = '/'; abs_path = g_build_path ("/", dirname, target, NULL); g_free (dirname); retval = canonicalize_filename (abs_path); g_free (abs_path); return retval; } 0707010000003A000081A40000000000000000000000016447D41100000BF0000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/libgxps/gxps-parse-utils.h/* * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PARSE_UTILS_H__ #define __GXPS_PARSE_UTILS_H__ #include "gxps-archive.h" #include "gxps-document.h" G_BEGIN_DECLS gboolean gxps_parse_stream (GMarkupParseContext *context, GInputStream *stream, GError **error); void gxps_parse_error (GMarkupParseContext *context, const gchar *source, GMarkupError error_type, const gchar *element_name, const gchar *attribute_name, const gchar *content, GError **error); gboolean gxps_value_get_int (const gchar *value, gint *int_value); gboolean gxps_value_get_double (const gchar *value, gdouble *double_value); gboolean gxps_value_get_double_positive (const gchar *value, gdouble *double_value); gboolean gxps_value_get_double_non_negative (const gchar *value, gdouble *double_value); gboolean gxps_value_get_boolean (const gchar *value, gboolean *boolean_value); gboolean gxps_point_parse (const gchar *point, gdouble *x, gdouble *y); void gxps_parse_skip_number (gchar **iter, const gchar *end); gchar *gxps_resolve_relative_path (const gchar *source, const gchar *target); G_END_DECLS #endif /* __GXPS_PARSE_UTILS_H__ */ 0707010000003B000081A40000000000000000000000016447D41100009D62000000000000000000000000000000000000002400000000libgxps-0.3.2+5/libgxps/gxps-path.c/* GXPSPath * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <string.h> #include "gxps-path.h" #include "gxps-matrix.h" #include "gxps-brush.h" #include "gxps-parse-utils.h" #include "gxps-debug.h" typedef enum { PD_TOKEN_INVALID, PD_TOKEN_NUMBER, PD_TOKEN_COMMA, PD_TOKEN_COMMAND, PD_TOKEN_EOF } PathDataTokenType; typedef struct { gchar *iter; gchar *end; PathDataTokenType type; gdouble number; gchar command; } PathDataToken; GXPSPath * gxps_path_new (GXPSRenderContext *ctx) { GXPSPath *path; path = g_slice_new0 (GXPSPath); path->ctx = ctx; /* Default values */ path->fill_rule = CAIRO_FILL_RULE_EVEN_ODD; path->line_width = 1.0; path->line_cap = CAIRO_LINE_CAP_BUTT; path->line_join = CAIRO_LINE_JOIN_MITER; path->miter_limit = 10.0; path->opacity = 1.0; path->is_filled = TRUE; path->is_stroked = TRUE; return path; } void gxps_path_free (GXPSPath *path) { if (G_UNLIKELY (!path)) return; g_free (path->data); g_free (path->clip_data); cairo_pattern_destroy (path->fill_pattern); cairo_pattern_destroy (path->stroke_pattern); cairo_pattern_destroy (path->opacity_mask); g_free (path->dash); g_slice_free (GXPSPath, path); } static const gchar * path_data_token_type_to_string (PathDataTokenType type) { switch (type) { case PD_TOKEN_INVALID: return "Invalid"; case PD_TOKEN_NUMBER: return "Number"; case PD_TOKEN_COMMA: return "Comma"; case PD_TOKEN_COMMAND: return "Command"; case PD_TOKEN_EOF: return "Eof"; default: g_assert_not_reached (); } return NULL; } #ifdef GXPS_ENABLE_DEBUG static void print_token (PathDataToken *token) { switch (token->type) { case PD_TOKEN_INVALID: g_debug ("Invalid token"); break; case PD_TOKEN_NUMBER: g_debug ("Token number: %f", token->number); break; case PD_TOKEN_COMMA: g_debug ("Token comma"); break; case PD_TOKEN_COMMAND: g_debug ("Token command %c", token->command); break; case PD_TOKEN_EOF: g_debug ("Token EOF"); break; default: g_assert_not_reached (); } } #endif /* GXPS_ENABLE_DEBUG */ static inline gboolean advance_char (PathDataToken *token) { token->iter++; if (G_UNLIKELY (token->iter == token->end)) return FALSE; return TRUE; } static inline gboolean _isspace (char c) { return c == ' ' || c == '\t'; } static void skip_spaces (PathDataToken *token) { do { if (!_isspace (*token->iter)) return; } while (advance_char (token)); } static gboolean path_data_iter_next (PathDataToken *token, GError **error) { gchar c; skip_spaces (token); if (token->iter == token->end) { token->type = PD_TOKEN_EOF; GXPS_DEBUG (print_token (token)); return TRUE; } c = *token->iter; if (g_ascii_isdigit (c) || c == '+' || c == '-') { gchar *start; gchar *str; start = token->iter; gxps_parse_skip_number (&token->iter, token->end); str = g_strndup (start, token->iter - start); if (!gxps_value_get_double (str, &token->number)) { g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing abreviated path: error converting token %s (%s) to double at %s", path_data_token_type_to_string (token->type), str, token->iter); g_free (str); return FALSE; } g_free (str); token->type = PD_TOKEN_NUMBER; } else if (c == ',') { token->type = PD_TOKEN_COMMA; token->iter++; } else if (g_ascii_isalpha (c)) { token->command = c; token->type = PD_TOKEN_COMMAND; token->iter++; } else { token->type = PD_TOKEN_INVALID; token->iter++; } GXPS_DEBUG (print_token (token)); return TRUE; } static void path_data_parse_error (PathDataToken *token, PathDataTokenType expected, GError **error) { if (expected == PD_TOKEN_INVALID) g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing abreviated path: unexpected token %s at %s", path_data_token_type_to_string (token->type), token->iter); else g_set_error (error, GXPS_PAGE_ERROR, GXPS_PAGE_ERROR_RENDER, "Error parsing abreviated path: expected token %s, but %s found at %s", path_data_token_type_to_string (token->type), path_data_token_type_to_string (expected), token->iter); } static gboolean path_data_get_point (PathDataToken *token, gdouble *x, gdouble *y, GError **error) { *x = token->number; if (!path_data_iter_next (token, error)) return FALSE; if (token->type != PD_TOKEN_COMMA) { path_data_parse_error (token, PD_TOKEN_COMMA, error); return FALSE; } if (!path_data_iter_next (token, error)) return FALSE; if (token->type != PD_TOKEN_NUMBER) { path_data_parse_error (token, PD_TOKEN_NUMBER, error); return FALSE; } *y = token->number; return TRUE; } gboolean gxps_path_parse (const gchar *data, cairo_t *cr, GError **error) { PathDataToken token; gdouble control_point_x; gdouble control_point_y; token.iter = (gchar *)data; token.end = token.iter + strlen (data); if (!path_data_iter_next (&token, error)) return FALSE; if (G_UNLIKELY (token.type != PD_TOKEN_COMMAND)) return TRUE; control_point_x = control_point_y = 0; do { gchar command = token.command; gboolean is_rel = FALSE; if (!path_data_iter_next (&token, error)) return FALSE; switch (command) { /* Move */ case 'm': is_rel = TRUE; case 'M': while (token.type == PD_TOKEN_NUMBER) { gdouble x, y; if (!path_data_get_point (&token, &x, &y, error)) return FALSE; GXPS_DEBUG (g_message ("%s (%f, %f)", is_rel ? "rel_move_to" : "move_to", x, y)); if (is_rel) cairo_rel_move_to (cr, x, y); else cairo_move_to (cr, x, y); if (!path_data_iter_next (&token, error)) return FALSE; } control_point_x = control_point_y = 0; break; /* Line */ case 'l': is_rel = TRUE; case 'L': while (token.type == PD_TOKEN_NUMBER) { gdouble x, y; if (!path_data_get_point (&token, &x, &y, error)) return FALSE; GXPS_DEBUG (g_message ("%s (%f, %f)", is_rel ? "rel_line_to" : "line_to", x, y)); if (is_rel) cairo_rel_line_to (cr, x, y); else cairo_line_to (cr, x, y); if (!path_data_iter_next (&token, error)) return FALSE; } control_point_x = control_point_y = 0; break; /* Horizontal Line */ case 'h': is_rel = TRUE; case 'H': while (token.type == PD_TOKEN_NUMBER) { gdouble x, y; gdouble offset; offset = token.number; GXPS_DEBUG (g_message ("%s (%f)", is_rel ? "rel_hline_to" : "hline_to", offset)); cairo_get_current_point (cr, &x, &y); x = is_rel ? x + offset : offset; cairo_line_to (cr, x, y); if (!path_data_iter_next (&token, error)) return FALSE; } control_point_x = control_point_y = 0; break; /* Vertical Line */ case 'v': is_rel = TRUE; case 'V': while (token.type == PD_TOKEN_NUMBER) { gdouble x, y; gdouble offset; offset = token.number; GXPS_DEBUG (g_message ("%s (%f)", is_rel ? "rel_vline_to" : "vline_to", offset)); cairo_get_current_point (cr, &x, &y); y = is_rel ? y + offset : offset; cairo_line_to (cr, x, y); if (!path_data_iter_next (&token, error)) return FALSE; } control_point_x = control_point_y = 0; break; /* Cubic Bézier curve */ case 'c': is_rel = TRUE; case 'C': while (token.type == PD_TOKEN_NUMBER) { gdouble x1, y1, x2, y2, x3, y3; if (!path_data_get_point (&token, &x1, &y1, error)) return FALSE; if (!path_data_iter_next (&token, error)) return FALSE; if (!path_data_get_point (&token, &x2, &y2, error)) return FALSE; if (!path_data_iter_next (&token, error)) return FALSE; if (!path_data_get_point (&token, &x3, &y3, error)) return FALSE; GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f)", is_rel ? "rel_curve_to" : "curve_to", x1, y1, x2, y2, x3, y3)); if (is_rel) cairo_rel_curve_to (cr, x1, y1, x2, y2, x3, y3); else cairo_curve_to (cr, x1, y1, x2, y2, x3, y3); control_point_x = x3 - x2; control_point_y = y3 - y2; if (!path_data_iter_next (&token, error)) return FALSE; } break; /* Quadratic Bézier curve */ case 'q': is_rel = TRUE; case 'Q': while (token.type == PD_TOKEN_NUMBER) { gdouble x1, y1, x2, y2; gdouble x, y; if (!path_data_get_point (&token, &x1, &y1, error)) return FALSE; if (!path_data_iter_next (&token, error)) return FALSE; if (!path_data_get_point (&token, &x2, &y2, error)) return FALSE; GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f)", is_rel ? "rel_quad_curve_to" : "quad_curve_to", x1, y1, x2, y2)); cairo_get_current_point (cr, &x, &y); x1 += is_rel ? x : 0; y1 += is_rel ? y : 0; x2 += is_rel ? x : 0; y2 += is_rel ? y : 0; cairo_curve_to (cr, 2.0 / 3.0 * x1 + 1.0 / 3.0 * x, 2.0 / 3.0 * y1 + 1.0 / 3.0 * y, 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2, 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2, x2, y2); if (!path_data_iter_next (&token, error)) return FALSE; } control_point_x = control_point_y = 0; break; /* Smooth Cubic Bézier curve */ case 's': is_rel = TRUE; case 'S': while (token.type == PD_TOKEN_NUMBER) { gdouble x2, y2, x3, y3; if (!path_data_get_point (&token, &x2, &y2, error)) return FALSE; if (!path_data_iter_next (&token, error)) return FALSE; if (!path_data_get_point (&token, &x3, &y3, error)) return FALSE; GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f)", is_rel ? "rel_smooth_curve_to" : "smooth_curve_to", control_point_x, control_point_y, x2, y2, x3, y3)); if (is_rel) { cairo_rel_curve_to (cr, control_point_x, control_point_y, x2, y2, x3, y3); } else { gdouble x, y; cairo_get_current_point (cr, &x, &y); cairo_curve_to (cr, x + control_point_x, y + control_point_y, x2, y2, x3, y3); } control_point_x = x3 - x2; control_point_y = y3 - y2; if (!path_data_iter_next (&token, error)) return FALSE; } break; /* Elliptical Arc */ case 'a': is_rel = TRUE; case 'A': while (token.type == PD_TOKEN_NUMBER) { gdouble xr, yr, x, y; #ifdef GXPS_ENABLE_DEBUG /* TODO: for now these variables are only used * in debug mode. */ gdouble rx, farc, fsweep; #endif if (!path_data_get_point (&token, &xr, &yr, error)) return FALSE; if (!path_data_iter_next (&token, error)) return FALSE; if (token.type != PD_TOKEN_NUMBER) { path_data_parse_error (&token, PD_TOKEN_NUMBER, error); return FALSE; } #ifdef GXPS_ENABLE_DEBUG rx = token.number; #endif if (!path_data_iter_next (&token, error)) return FALSE; if (token.type != PD_TOKEN_NUMBER) { path_data_parse_error (&token, PD_TOKEN_NUMBER, error); return FALSE; } #ifdef GXPS_ENABLE_DEBUG farc = token.number; #endif if (!path_data_iter_next (&token, error)) return FALSE; if (token.type != PD_TOKEN_NUMBER) { path_data_parse_error (&token, PD_TOKEN_NUMBER, error); return FALSE; } #ifdef GXPS_ENABLE_DEBUG fsweep = token.number; #endif if (!path_data_iter_next (&token, error)) return FALSE; if (!path_data_get_point (&token, &x, &y, error)) return FALSE; GXPS_DEBUG (g_message ("%s (%f, %f, %f, %f, %f, %f, %f)", is_rel ? "rel_arc" : "arc", xr, yr, rx, farc, fsweep, x, y)); GXPS_DEBUG (g_debug ("Unsupported command in path: %c", command)); if (!path_data_iter_next (&token, error)) return FALSE; } control_point_x = control_point_y = 0; break; /* Close */ case 'z': is_rel = TRUE; case 'Z': cairo_close_path (cr); GXPS_DEBUG (g_message ("close_path")); control_point_x = control_point_y = 0; break; /* Fill Rule */ case 'F': { gint fill_rule; fill_rule = (gint)token.number; cairo_set_fill_rule (cr, (fill_rule == 0) ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); GXPS_DEBUG (g_message ("set_fill_rule (%s)", (fill_rule == 0) ? "EVEN_ODD" : "WINDING")); if (!path_data_iter_next (&token, error)) return FALSE; } control_point_x = control_point_y = 0; break; default: g_assert_not_reached (); } } while (token.type == PD_TOKEN_COMMAND); return TRUE; } static gboolean gxps_points_parse (const gchar *points, gdouble **coords, guint *n_points) { gchar **items; guint i, j = 0; gboolean retval = TRUE; *n_points = 0; items = g_strsplit (points, " ", -1); if (!items) return FALSE; for (i = 0; items[i] != NULL; i++) { if (*items[i] != '\0') /* don't count empty string */ (*n_points)++; } if (*n_points == 0) return FALSE; *coords = g_malloc (*n_points * 2 * sizeof (gdouble)); for (i = 0; items[i] != NULL; i++) { gdouble x, y; if (*items[i] == '\0') continue; if (!gxps_point_parse (items[i], &x, &y)) { g_free (*coords); retval = FALSE; break; } coords[0][j++] = x; coords[0][j++] = y; } g_strfreev (items); return retval; } static void path_geometry_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSPath *path = (GXPSPath *)user_data; if (strcmp (element_name, "PathGeometry.Transform") == 0) { GXPSMatrix *matrix; matrix = gxps_matrix_new (path->ctx); gxps_matrix_parser_push (context, matrix); } else if (strcmp (element_name, "PathFigure") == 0) { gint i; gboolean has_start_point = FALSE; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "StartPoint") == 0) { gdouble x, y; if (!gxps_point_parse (values[i], &x, &y)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PathFigure", "StartPoint", values[i], error); return; } GXPS_DEBUG (g_message ("move_to (%f, %f)", x, y)); cairo_move_to (path->ctx->cr, x, y); has_start_point = TRUE; } else if (strcmp (names[i], "IsClosed") == 0) { gboolean is_closed; if (!gxps_value_get_boolean (values[i], &is_closed)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PathFigure", "IsClosed", values[i], error); return; } path->is_closed = is_closed; } else if (strcmp (names[i], "IsFilled") == 0) { gboolean is_filled; if (!gxps_value_get_boolean (values[i], &is_filled)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PathFigure", "IsFilled", values[i], error); return; } path->is_filled = is_filled; } } if (!has_start_point) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "PathFigure", "StartPoint", NULL, error); return; } } else if (strcmp (element_name, "PolyLineSegment") == 0) { gint i, j; const gchar *points_str = NULL; gdouble *points = NULL; guint n_points; gboolean is_stroked = TRUE; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Points") == 0) { points_str = values[i]; } else if (strcmp (names[i], "IsStroked") == 0) { if (!gxps_value_get_boolean (values[i], &is_stroked)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PolyLineSegment", "IsStroked", points_str, error); return; } } } if (!is_stroked) return; if (!points_str) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "PolyLineSegment", "Points", NULL, error); return; } if (!gxps_points_parse (points_str, &points, &n_points)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PolyLineSegment", "Points", points_str, error); return; } for (j = 0; j < n_points * 2; j += 2) { GXPS_DEBUG (g_message ("line_to (%f, %f)", points[j], points[j + 1])); cairo_line_to (path->ctx->cr, points[j], points[j + 1]); } g_free (points); } else if (strcmp (element_name, "PolyBezierSegment") == 0) { gint i, j; const gchar *points_str = NULL; gdouble *points = NULL; guint n_points; gboolean is_stroked = TRUE; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Points") == 0) { points_str = values[i]; } else if (strcmp (names[i], "IsStroked") == 0) { if (!gxps_value_get_boolean (values[i], &is_stroked)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PolyBezierSegment", "IsStroked", points_str, error); return; } } } if (!is_stroked) return; if (!points_str) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "PolyBezierSegment", "Points", NULL, error); return; } if (!gxps_points_parse (points_str, &points, &n_points)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PolyBezierSegment", "Points", points_str, error); return; } for (j = 0; j < n_points * 2; j += 6) { GXPS_DEBUG (g_message ("curve_to (%f, %f, %f, %f, %f, %f)", points[j], points[j + 1], points[j + 2], points[j + 3], points[j + 4], points[j + 5])); cairo_curve_to (path->ctx->cr, points[j], points[j + 1], points[j + 2], points[j + 3], points[j + 4], points[j + 5]); } g_free (points); } else if (strcmp (element_name, "PolyQuadraticBezierSegment") == 0) { gint i, j; const gchar *points_str = NULL; gdouble *points = NULL; guint n_points; gboolean is_stroked = TRUE; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Points") == 0) { points_str = values[i]; } else if (strcmp (names[i], "IsStroked") == 0) { if (!gxps_value_get_boolean (values[i], &is_stroked)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PolyQuadraticBezierSegment", "IsStroked", points_str, error); return; } } } if (!is_stroked) return; if (!points_str) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, "PolyQuadraticBezierSegment", "Points", NULL, error); return; } if (!gxps_points_parse (points_str, &points, &n_points)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PolyQuadraticBezierSegment", "Points", points_str, error); return; } for (j = 0; j < n_points * 2; j += 4) { gdouble x1, y1, x2, y2; gdouble x, y; x1 = points[j]; y1 = points[j + 1]; x2 = points[j + 2]; y2 = points[j + 3]; GXPS_DEBUG (g_message ("quad_curve_to (%f, %f, %f, %f)", x1, y1, x2, y2)); cairo_get_current_point (path->ctx->cr, &x, &y); cairo_curve_to (path->ctx->cr, 2.0 / 3.0 * x1 + 1.0 / 3.0 * x, 2.0 / 3.0 * y1 + 1.0 / 3.0 * y, 2.0 / 3.0 * x1 + 1.0 / 3.0 * x2, 2.0 / 3.0 * y1 + 1.0 / 3.0 * y2, x2, y2); } g_free (points); } else if (strcmp (element_name, "ArcSegment") == 0) { GXPS_DEBUG (g_debug ("Unsupported PathGeometry: ArcSegment")); } } static void path_geometry_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSPath *path = (GXPSPath *)user_data; if (strcmp (element_name, "PathGeometry.Transform") == 0) { GXPSMatrix *matrix; matrix = g_markup_parse_context_pop (context); GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix->matrix.xx, matrix->matrix.yx, matrix->matrix.xy, matrix->matrix.yy, matrix->matrix.x0, matrix->matrix.y0)); cairo_transform (path->ctx->cr, &matrix->matrix); gxps_matrix_free (matrix); } else if (strcmp (element_name, "PathFigure") == 0) { if (path->is_closed) { GXPS_DEBUG (g_message ("close_path")); cairo_close_path (path->ctx->cr); } if (path->stroke_pattern) { cairo_set_line_width (path->ctx->cr, path->line_width); if (path->dash && path->dash_len > 0) cairo_set_dash (path->ctx->cr, path->dash, path->dash_len, path->dash_offset); cairo_set_line_join (path->ctx->cr, path->line_join); cairo_set_miter_limit (path->ctx->cr, path->miter_limit); } if (path->opacity_mask) { gdouble x1 = 0, y1 = 0, x2 = 0, y2 = 0; cairo_path_t *cairo_path; if (path->stroke_pattern) cairo_stroke_extents (path->ctx->cr, &x1, &y1, &x2, &y2); else if (path->fill_pattern) cairo_fill_extents (path->ctx->cr, &x1, &y1, &x2, &y2); cairo_path = cairo_copy_path (path->ctx->cr); cairo_new_path (path->ctx->cr); cairo_rectangle (path->ctx->cr, x1, y1, x2 - x1, y2 - y1); cairo_clip (path->ctx->cr); cairo_push_group (path->ctx->cr); cairo_append_path (path->ctx->cr, cairo_path); cairo_path_destroy (cairo_path); } if (path->is_filled && path->fill_pattern) { GXPS_DEBUG (g_message ("fill")); cairo_set_source (path->ctx->cr, path->fill_pattern); if (path->is_stroked && path->stroke_pattern) cairo_fill_preserve (path->ctx->cr); else cairo_fill (path->ctx->cr); } if (path->stroke_pattern) { GXPS_DEBUG (g_message ("stroke")); cairo_set_source (path->ctx->cr, path->stroke_pattern); cairo_stroke (path->ctx->cr); } if (path->opacity_mask) { cairo_pop_group_to_source (path->ctx->cr); cairo_mask (path->ctx->cr, path->opacity_mask); } } } static GMarkupParser path_geometry_parser = { path_geometry_start_element, path_geometry_end_element, NULL, NULL }; static cairo_fill_rule_t gxps_fill_rule_parse (const gchar *rule) { if (strcmp (rule, "EvenOdd") == 0) return CAIRO_FILL_RULE_EVEN_ODD; else if (strcmp (rule, "NonZero") == 0) return CAIRO_FILL_RULE_WINDING; return CAIRO_FILL_RULE_EVEN_ODD; } static void path_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSPath *path = (GXPSPath *)user_data; if (strcmp (element_name, "Path.Fill") == 0) { GXPSBrush *brush; brush = gxps_brush_new (path->ctx); gxps_brush_parser_push (context, brush); } else if (strcmp (element_name, "Path.Stroke") == 0) { GXPSBrush *brush; brush = gxps_brush_new (path->ctx); gxps_brush_parser_push (context, brush); } else if (strcmp (element_name, "Path.Data") == 0) { } else if (strcmp (element_name, "PathGeometry") == 0) { gint i; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Figures") == 0) { path->data = g_strdup (values[i]); } else if (strcmp (names[i], "FillRule") == 0) { path->fill_rule = gxps_fill_rule_parse (values[i]); GXPS_DEBUG (g_message ("set_fill_rule (%s)", values[i])); } else if (strcmp (names[i], "Transform") == 0) { cairo_matrix_t matrix; if (!gxps_matrix_parse (values[i], &matrix)) { gxps_parse_error (context, path->ctx->page->priv->source, G_MARKUP_ERROR_INVALID_CONTENT, "PathGeometry", "Transform", values[i], error); return; } GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix.xx, matrix.yx, matrix.xy, matrix.yy, matrix.x0, matrix.y0)); cairo_transform (path->ctx->cr, &matrix); } } if (!path->data) { cairo_set_fill_rule (path->ctx->cr, path->fill_rule); if (path->clip_data) { if (!gxps_path_parse (path->clip_data, path->ctx->cr, error)) return; GXPS_DEBUG (g_message ("clip")); cairo_clip (path->ctx->cr); } g_markup_parse_context_push (context, &path_geometry_parser, path); } } else if (strcmp (element_name, "Path.RenderTransform") == 0) { GXPSMatrix *matrix; matrix = gxps_matrix_new (path->ctx); gxps_matrix_parser_push (context, matrix); } else if (strcmp (element_name, "Path.OpacityMask") == 0) { GXPSBrush *brush; brush = gxps_brush_new (path->ctx); gxps_brush_parser_push (context, brush); } else { GXPS_DEBUG (g_debug ("Unsupported path child %s", element_name)); } } static void path_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSPath *path = (GXPSPath *)user_data; if (strcmp (element_name, "Path.Fill") == 0) { GXPSBrush *brush; brush = g_markup_parse_context_pop (context); path->fill_pattern = cairo_pattern_reference (brush->pattern); gxps_brush_free (brush); } else if (strcmp (element_name, "Path.Stroke") == 0) { GXPSBrush *brush; brush = g_markup_parse_context_pop (context); path->stroke_pattern = cairo_pattern_reference (brush->pattern); gxps_brush_free (brush); } else if (strcmp (element_name, "Path.Data") == 0) { } else if (strcmp (element_name, "PathGeometry") == 0) { if (!path->data) g_markup_parse_context_pop (context); } else if (strcmp (element_name, "Path.RenderTransform") == 0) { GXPSMatrix *matrix; matrix = g_markup_parse_context_pop (context); GXPS_DEBUG (g_message ("transform (%f, %f, %f, %f) [%f, %f]", matrix->matrix.xx, matrix->matrix.yx, matrix->matrix.xy, matrix->matrix.yy, matrix->matrix.x0, matrix->matrix.y0)); cairo_transform (path->ctx->cr, &matrix->matrix); gxps_matrix_free (matrix); } else if (strcmp (element_name, "Path.OpacityMask") == 0) { GXPSBrush *brush; brush = g_markup_parse_context_pop (context); if (!path->opacity_mask) path->opacity_mask = cairo_pattern_reference (brush->pattern); gxps_brush_free (brush); } else { } } static void path_error (GMarkupParseContext *context, GError *error, gpointer user_data) { GXPSPath *path = (GXPSPath *)user_data; gxps_path_free (path); } static GMarkupParser path_parser = { path_start_element, path_end_element, NULL, NULL, path_error }; void gxps_path_parser_push (GMarkupParseContext *context, GXPSPath *path) { g_markup_parse_context_push (context, &path_parser, path); } 0707010000003C000081A40000000000000000000000016447D41100000875000000000000000000000000000000000000002400000000libgxps-0.3.2+5/libgxps/gxps-path.h/* GXPSPath * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PATH_H__ #define __GXPS_PATH_H__ #include <cairo.h> #include "gxps-page-private.h" G_BEGIN_DECLS typedef struct _GXPSPath GXPSPath; struct _GXPSPath { GXPSRenderContext *ctx; gchar *data; gchar *clip_data; cairo_pattern_t *fill_pattern; cairo_pattern_t *stroke_pattern; cairo_fill_rule_t fill_rule; gdouble line_width; gdouble *dash; guint dash_len; gdouble dash_offset; cairo_line_cap_t line_cap; cairo_line_join_t line_join; gdouble miter_limit; gdouble opacity; cairo_pattern_t *opacity_mask; gboolean is_stroked : 1; gboolean is_filled : 1; gboolean is_closed : 1; }; GXPSPath *gxps_path_new (GXPSRenderContext *ctx); void gxps_path_free (GXPSPath *path); gboolean gxps_path_parse (const gchar *data, cairo_t *cr, GError **error); void gxps_path_parser_push (GMarkupParseContext *context, GXPSPath *path); G_END_DECLS #endif /* __GXPS_PATH_H__ */ 0707010000003D000081A40000000000000000000000016447D41100000819000000000000000000000000000000000000002700000000libgxps-0.3.2+5/libgxps/gxps-private.h/* * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PRIVATE_H__ #define __GXPS_PRIVATE_H__ #include <cairo.h> #include "gxps-archive.h" #include "gxps-document.h" #include "gxps-page.h" #include "gxps-parse-utils.h" #include "gxps-links.h" #include "gxps-document-structure.h" #include "gxps-core-properties.h" G_BEGIN_DECLS GXPSDocument *_gxps_document_new (GXPSArchive *zip, const gchar *source, GError **error); GXPSPage *_gxps_page_new (GXPSArchive *zip, const gchar *source, GError **error); GXPSLink *_gxps_link_new (GXPSArchive *zip, cairo_rectangle_t *area, const gchar *dest); GXPSLinkTarget *_gxps_link_target_new (GXPSArchive *zip, const gchar *uri); GXPSDocumentStructure *_gxps_document_structure_new (GXPSArchive *zip, const gchar *source); GXPSCoreProperties *_gxps_core_properties_new (GXPSArchive *zip, const gchar *source, GError **error); G_END_DECLS #endif /* __GXPS_PRIVATE_H__ */ 0707010000003E000081A40000000000000000000000016447D41100003537000000000000000000000000000000000000002900000000libgxps-0.3.2+5/libgxps/gxps-resources.c/* * Copyright (C) 2015 Jason Crain <jason@aquaticape.us> * Copyright (C) 2017 Ignacio Casal Quinteiro <icq@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "gxps-resources.h" #include "gxps-parse-utils.h" #include "gxps-error.h" #include <string.h> #define GXPS_RESOURCES_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_RESOURCES, GXPSResourcesClass)) #define GXPS_IS_RESOURCES_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_RESOURCES)) #define GXPS_RESOURCES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_RESOURCES, GXPSResourcesClass)) struct _GXPSResources { GObject parent_instance; GXPSArchive *zip; GQueue *queue; }; struct _GXPSResourcesClass { GObjectClass parent; }; typedef struct _GXPSResourcesClass GXPSResourcesClass; enum { PROP_0, PROP_ARCHIVE, LAST_PROP }; static GParamSpec *props[LAST_PROP]; G_DEFINE_TYPE (GXPSResources, gxps_resources, G_TYPE_OBJECT) static void gxps_resources_finalize (GObject *object) { GXPSResources *resources = GXPS_RESOURCES (object); g_queue_free_full (resources->queue, (GDestroyNotify)g_hash_table_destroy); g_object_unref (resources->zip); G_OBJECT_CLASS (gxps_resources_parent_class)->finalize (object); } static void gxps_resources_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GXPSResources *resources = GXPS_RESOURCES (object); switch (prop_id) { case PROP_ARCHIVE: resources->zip = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gxps_resources_class_init (GXPSResourcesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = gxps_resources_finalize; object_class->set_property = gxps_resources_set_property; props[PROP_ARCHIVE] = g_param_spec_object ("archive", "Archive", "The document archive", GXPS_TYPE_ARCHIVE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, LAST_PROP, props); } static void gxps_resources_init (GXPSResources *resources) { resources->queue = g_queue_new (); } void gxps_resources_push_dict (GXPSResources *resources) { GHashTable *ht; g_return_if_fail (GXPS_IS_RESOURCES (resources)); ht = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)g_free); g_queue_push_head (resources->queue, ht); } void gxps_resources_pop_dict (GXPSResources *resources) { GHashTable *ht; g_return_if_fail (GXPS_IS_RESOURCES (resources)); ht = g_queue_pop_head (resources->queue); g_hash_table_destroy (ht); } const gchar * gxps_resources_get_resource (GXPSResources *resources, const gchar *key) { GList *node; g_return_val_if_fail (GXPS_IS_RESOURCES (resources), NULL); for (node = resources->queue->head; node != NULL; node = node->next) { GHashTable *ht; gpointer data; ht = node->data; data = g_hash_table_lookup (ht, key); if (data) return data; } return NULL; } static gboolean gxps_resources_set (GXPSResources *resources, gchar *key, gchar *value) { GHashTable *ht; if (g_queue_get_length (resources->queue) == 0) gxps_resources_push_dict (resources); ht = g_queue_peek_head (resources->queue); if (g_hash_table_contains (ht, key)) { g_free (key); g_free (value); return FALSE; } g_hash_table_insert (ht, key, value); return TRUE; } typedef struct { GXPSResources *resources; gchar *source; gchar *key; GString *xml; } GXPSResourceDictContext; static GXPSResourceDictContext * gxps_resource_dict_context_new (GXPSResources *resources, const gchar *source) { GXPSResourceDictContext *resource_dict; resource_dict = g_slice_new0 (GXPSResourceDictContext); resource_dict->resources = g_object_ref (resources); resource_dict->source = g_strdup (source); return resource_dict; } static void gxps_resource_dict_context_free (GXPSResourceDictContext *resource_dict) { if (G_UNLIKELY (!resource_dict)) return; g_free (resource_dict->key); if (resource_dict->xml) g_string_free (resource_dict->xml, TRUE); g_object_unref (resource_dict->resources); g_slice_free (GXPSResourceDictContext, resource_dict); } static void resource_concat_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data; gint i; g_string_append_printf (resource_dict_ctx->xml, "<%s", element_name); for (i = 0; names[i] != NULL; i++) { g_string_append_printf (resource_dict_ctx->xml, " %s=\"%s\"", names[i], values[i]); } g_string_append (resource_dict_ctx->xml, ">\n"); } static void resource_concat_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data; g_string_append_printf (resource_dict_ctx->xml, "</%s>\n", element_name); } static GMarkupParser resource_concat_parser = { resource_concat_start_element, resource_concat_end_element, NULL, NULL, NULL }; static void resource_dict_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data; gint i; for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "x:Key") == 0) { resource_dict_ctx->key = g_strdup (values[i]); break; } } if (!resource_dict_ctx->key) { gxps_parse_error (context, resource_dict_ctx->source, G_MARKUP_ERROR_MISSING_ATTRIBUTE, element_name, "x:Key", NULL, error); return; } if (!resource_dict_ctx->xml) { resource_dict_ctx->xml = g_string_new (NULL); g_string_append_printf (resource_dict_ctx->xml, "<%s>\n", element_name); } g_string_append_printf (resource_dict_ctx->xml, "<%s", element_name); for (i = 0; names[i] != NULL; i++) { /* Skip key */ if (strcmp (names[i], "x:Key") != 0) { g_string_append_printf (resource_dict_ctx->xml, " %s=\"%s\"", names[i], values[i]); } } g_string_append (resource_dict_ctx->xml, ">\n"); g_markup_parse_context_push (context, &resource_concat_parser, resource_dict_ctx); } static void resource_dict_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data; g_string_append_printf (resource_dict_ctx->xml, "</%s>\n</%s>", element_name, element_name); gxps_resources_set (resource_dict_ctx->resources, resource_dict_ctx->key, g_string_free (resource_dict_ctx->xml, FALSE)); resource_dict_ctx->key = NULL; resource_dict_ctx->xml = NULL; g_markup_parse_context_pop (context); } static void resource_dict_error (GMarkupParseContext *context, GError *error, gpointer user_data) { GXPSResourceDictContext *resource_dict_ctx = (GXPSResourceDictContext *)user_data; gxps_resource_dict_context_free (resource_dict_ctx); } static GMarkupParser resource_dict_parser = { resource_dict_start_element, resource_dict_end_element, NULL, NULL, resource_dict_error }; typedef struct { GXPSResources *resources; gchar *source; gboolean remote; } GXPSResourceContext; static GXPSResourceContext * gxps_resource_context_new (GXPSResources *resources, const gchar *source) { GXPSResourceContext *context; context = g_slice_new0 (GXPSResourceContext); context->resources = g_object_ref (resources); context->source = g_strdup (source); return context; } static void gxps_resource_context_free (GXPSResourceContext *context) { g_object_unref (context->resources); g_free (context->source); g_slice_free (GXPSResourceContext, context); } static void push_resource_dict_context (GMarkupParseContext *context, GXPSResourceContext *rcontext) { GXPSResourceDictContext *resource_dict_ctx; resource_dict_ctx = gxps_resource_dict_context_new (rcontext->resources, rcontext->source); g_markup_parse_context_push (context, &resource_dict_parser, resource_dict_ctx); } static void pop_resource_dict_context (GMarkupParseContext *context) { GXPSResourceDictContext *resource_dict_ctx; resource_dict_ctx = g_markup_parse_context_pop (context); gxps_resource_dict_context_free (resource_dict_ctx); } static void remote_resource_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSResourceContext *rcontext = (GXPSResourceContext *)user_data; if (strcmp (element_name, "ResourceDictionary") == 0) { push_resource_dict_context (context, rcontext); } else { gxps_parse_error (context, rcontext->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void remote_resource_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { if (strcmp (element_name, "ResourceDictionary") == 0) { pop_resource_dict_context (context); } } static GMarkupParser remote_resource_parser = { remote_resource_start_element, remote_resource_end_element, NULL, NULL, NULL }; static void resources_start_element (GMarkupParseContext *context, const gchar *element_name, const gchar **names, const gchar **values, gpointer user_data, GError **error) { GXPSResourceContext *rcontext = (GXPSResourceContext *)user_data; const gchar *source = NULL; gint i; if (strcmp (element_name, "ResourceDictionary") == 0) { for (i = 0; names[i] != NULL; i++) { if (strcmp (names[i], "Source") == 0) source = values[i]; } rcontext->remote = source != NULL; if (rcontext->remote) { GInputStream *stream; gchar *abs_source; GMarkupParseContext *parse_ctx; abs_source = gxps_resolve_relative_path (rcontext->source, source); stream = gxps_archive_open (rcontext->resources->zip, abs_source); if (!stream) { g_set_error (error, GXPS_ERROR, GXPS_ERROR_SOURCE_NOT_FOUND, "Source %s not found in archive", abs_source); g_free (abs_source); return; } parse_ctx = g_markup_parse_context_new (&remote_resource_parser, 0, rcontext, NULL); gxps_parse_stream (parse_ctx, stream, error); g_object_unref (stream); g_markup_parse_context_free (parse_ctx); g_free (abs_source); } else { push_resource_dict_context (context, rcontext); } } else { gxps_parse_error (context, rcontext->source, G_MARKUP_ERROR_UNKNOWN_ELEMENT, element_name, NULL, NULL, error); } } static void resources_end_element (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { GXPSResourceContext *rcontext = (GXPSResourceContext *)user_data; if (strcmp (element_name, "ResourceDictionary") == 0) { if (!rcontext->remote) { pop_resource_dict_context (context); } else { rcontext->remote = FALSE; } } } static GMarkupParser resources_parser = { resources_start_element, resources_end_element, NULL, NULL, NULL }; void gxps_resources_parser_push (GMarkupParseContext *context, GXPSResources *resources, const gchar *source) { GXPSResourceContext *rcontext; rcontext = gxps_resource_context_new (resources, source); g_markup_parse_context_push (context, &resources_parser, rcontext); } void gxps_resources_parser_pop (GMarkupParseContext *context) { GXPSResourceContext *rcontext; rcontext = g_markup_parse_context_pop (context); gxps_resource_context_free (rcontext); } 0707010000003F000081A40000000000000000000000016447D411000007C6000000000000000000000000000000000000002900000000libgxps-0.3.2+5/libgxps/gxps-resources.h/* * Copyright (C) 2015 Jason Crain <jason@aquaticape.us> * Copyright (C) 2017 Ignacio Casal Quinteiro <icq@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GXPS_RESOURCES_H #define GXPS_RESOURCES_H #include <glib-object.h> G_BEGIN_DECLS #define GXPS_TYPE_RESOURCES (gxps_resources_get_type ()) #define GXPS_RESOURCES(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_RESOURCES, GXPSResources)) #define GXPS_IS_RESOURCES(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_RESOURCES)) typedef struct _GXPSResources GXPSResources; GType gxps_resources_get_type (void) G_GNUC_CONST; void gxps_resources_push_dict (GXPSResources *resources); void gxps_resources_pop_dict (GXPSResources *resources); const gchar *gxps_resources_get_resource (GXPSResources *resources, const gchar *key); void gxps_resources_parser_push (GMarkupParseContext *context, GXPSResources *resources, const gchar *source); void gxps_resources_parser_pop (GMarkupParseContext *context); G_END_DECLS #endif /* GXPS_RESOURCES_H */ 07070100000040000081A40000000000000000000000016447D41100000D71000000000000000000000000000000000000002A00000000libgxps-0.3.2+5/libgxps/gxps-version.h.in/* * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined (__GXPS_H_INSIDE__) && !defined (GXPS_COMPILATION) #error "Only <libgxps/gxps.h> can be included directly." #endif #ifndef __GXPS_VERSION_H__ #define __GXPS_VERSION_H__ /** * SECTION:gxps-version * @Short_description: Variables and functions to check the GXPS version * @Title: Version Information * * GXPS provides version information, primarily useful in configure checks * for builds that have a configure script. Applications will not typically * use the features described here. */ /** * GXPS_MAJOR_VERSION: * * The major version number of the GXPS header files (e.g. in GXPS version * 0.1.2 this is 0.) */ #define GXPS_MAJOR_VERSION (@GXPS_MAJOR_VERSION@) /** * GXPS_MINOR_VERSION: * * The major version number of the GXPS header files (e.g. in GXPS version * 0.1.2 this is 1.) */ #define GXPS_MINOR_VERSION (@GXPS_MINOR_VERSION@) /** * GXPS_MICRO_VERSION: * * The micro version number of the GXPS header files (e.g. in GXPS version * 0.1.2 this is 2.) */ #define GXPS_MICRO_VERSION (@GXPS_MICRO_VERSION@) /** * GXPS_VERSION_STRING: * * The version number of the GXPS library as a string * * Since: 0.2.1 */ #define GXPS_VERSION_STRING "@PACKAGE_VERSION@" /** * GXPS_CHECK_VERSION: * @major: major version (e.g. 0 for version 0.1.2) * @minor: minor version (e.g. 1 for version 0.1.2) * @micro: micro version (e.g. 2 for version 0.1.2) * * Checks the version fo the GXPS library * * Returns: %TRUE if the version of the GXPS header files is the same * as or newer than the passed-in version */ #define GXPS_CHECK_VERSION(major,minor,micro) \ (GXPS_MAJOR_VERSION > (major) || \ (GXPS_MAJOR_VERSION == (major) && GXPS_MINOR_VERSION > (minor)) || \ (GXPS_MAJOR_VERSION == (major) && GXPS_MINOR_VERSION == (minor) && GXPS_MICRO_VERSION >= (micro))) #ifndef _GXPS_EXTERN #define _GXPS_EXTERN extern #endif /* We prefix variable declarations so they can * properly get exported in Windows DLLs. */ #ifndef GXPS_VAR # ifdef G_PLATFORM_WIN32 # ifdef GXPS_COMPILATION # ifdef DLL_EXPORT # define GXPS_VAR __declspec(dllexport) # else /* !DLL_EXPORT */ # define GXPS_VAR extern # endif /* !DLL_EXPORT */ # else /* !GXPS_COMPILATION */ # define GXPS_VAR extern __declspec(dllimport) # endif /* !GXPS_COMPILATION */ # else /* !G_PLATFORM_WIN32 */ # define GXPS_VAR _GXPS_EXTERN # endif /* !G_PLATFORM_WIN32 */ #endif /* GXPS_VAR */ #define GXPS_AVAILABLE_IN_ALL _GXPS_EXTERN #endif /* __GXPS_VERSION_H__ */ 07070100000041000081A40000000000000000000000016447D41100000486000000000000000000000000000000000000001F00000000libgxps-0.3.2+5/libgxps/gxps.h/* * Copyright (C) 2010 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GXPS_H #define GXPS_H #define __GXPS_H_INSIDE__ #include <libgxps/gxps-document.h> #include <libgxps/gxps-document-structure.h> #include <libgxps/gxps-error.h> #include <libgxps/gxps-file.h> #include <libgxps/gxps-links.h> #include <libgxps/gxps-page.h> #include <libgxps/gxps-version.h> #undef __GXPS_H_INSIDE__ #endif /* GXPS_H */ 07070100000042000081A40000000000000000000000016447D41100000E88000000000000000000000000000000000000002400000000libgxps-0.3.2+5/libgxps/meson.buildheaders = [ 'gxps.h', 'gxps-core-properties.h', 'gxps-document.h', 'gxps-document-structure.h', 'gxps-error.h', 'gxps-file.h', 'gxps-links.h', 'gxps-page.h', ] private_headers = [ 'gxps-archive.h', 'gxps-brush.h', 'gxps-color.h', 'gxps-debug.h', 'gxps-fonts.h', 'gxps-glyphs.h', 'gxps-images.h', 'gxps-matrix.h', 'gxps-page-private.h', 'gxps-parse-utils.h', 'gxps-path.h', 'gxps-private.h', 'gxps-resources.h', ] introspection_sources = [ 'gxps-brush.c', 'gxps-color.c', 'gxps-core-properties.c', 'gxps-debug.c', 'gxps-document.c', 'gxps-document-structure.c', 'gxps-error.c', 'gxps-file.c', 'gxps-glyphs.c', 'gxps-links.c', 'gxps-matrix.c', 'gxps-page.c', 'gxps-path.c', ] sources = [ 'gxps-archive.c', 'gxps-fonts.c', 'gxps-images.c', 'gxps-parse-utils.c', 'gxps-resources.c', ] sources += introspection_sources version_data = configuration_data() version_data.set('GXPS_MAJOR_VERSION', gxps_major_version) version_data.set('GXPS_MINOR_VERSION', gxps_minor_version) version_data.set('GXPS_MICRO_VERSION', gxps_micro_version) version_data.set('PACKAGE_VERSION', meson.project_version()) gxps_version_h = configure_file(input: 'gxps-version.h.in', output: 'gxps-version.h', install_dir: gxps_headers_installdir, install: true, configuration: version_data) install_headers(headers, subdir: 'libgxps') gxps_deps = [ glib_dep, gobject_dep, gio_dep, cairo_dep, cairo_pdf_dep, cairo_ps_dep, cairo_svg_dep, archive_dep, freetype_dep, png_dep, lcms2_dep, jpeg_dep, tiff_dep, libm_dep ] common_ldflags = [] if host_system == 'linux' common_ldflags = [ '-Wl,-Bsymbolic' ] endif gxps = shared_library('gxps', include_directories: core_inc, sources: sources + headers + private_headers + [ gxps_version_h ], soversion: soversion, version: libversion, install: true, dependencies: gxps_deps, c_args: extra_args + common_flags + [ '-DG_LOG_DOMAIN="GXPS"', '-DGXPS_COMPILATION', ], link_args: common_ldflags) # Internal dependency, for tests gxps_inc = include_directories([ '.' ]) gxps_dep = declare_dependency(link_with: gxps, include_directories: [ gxps_inc, core_inc ], dependencies: gxps_deps) # Generate the pkg-config file pkgg = import('pkgconfig') cairo_dep_str = cairo_pc_found ? ', cairo >= ' + cairo_req : '' cairo_dep_libs = cairo_pc_found ? [] : cairo_dep pkgg.generate(libraries: [gxps, cairo_dep_libs], version: gxps_version, name: 'libgxps', description: 'XPS Documents library', requires: 'gobject-2.0 >= ' + glib_req + ', gio-2.0 >= ' + glib_req + ', libarchive >= ' + archive_req + cairo_dep_str) if build_gir gir_extra_args = [ '--identifier-prefix=GXPS', '--c-include=gxps.h' ] gnome.generate_gir(gxps, sources: introspection_sources + headers + [ gxps_version_h ], namespace: 'GXPS', nsversion: apiversion, identifier_prefix: 'Gxps', symbol_prefix: 'gxps', includes: [ 'GObject-2.0', 'GLib-2.0', 'Gio-2.0', 'cairo-1.0' ], install: true, extra_args: gir_extra_args + [ '-DGXPS_COMPILATION', ]) endif 07070100000043000081A40000000000000000000000016447D411000021C9000000000000000000000000000000000000001C00000000libgxps-0.3.2+5/meson.buildproject('libgxps', 'c', version: '0.3.2', default_options: [ 'buildtype=debugoptimized' ], license: 'LGPL2+', meson_version: '>= 0.50.0') gxps_version = meson.project_version() version_array = gxps_version.split('.') gxps_major_version = version_array[0].to_int() gxps_minor_version = version_array[1].to_int() gxps_micro_version = version_array[2].to_int() apiversion = '0.1' # Libtool versioning. # Before making a release, the libtool version should be modified. # The string is of the form C:R:A. # - If interfaces have been changed or added, but binary compatibility has # been preserved, change to C+1:0:A+1 # - If binary compatibility has been broken (eg removed or changed interfaces) # change to C+1:0:0 # - If the interface is the same as the previous version, change to C:R+1:A current = 4 revision = 4 age = 2 soversion = '@0@'.format(current - age) libversion = '@0@.@1@.@2@'.format(current - age, age, revision) gxps_prefix = get_option('prefix') gxps_libdir = join_paths(gxps_prefix, get_option('libdir')) gxps_includedir = join_paths(gxps_prefix, get_option('includedir')) gxps_headers_installdir = join_paths(gxps_includedir, 'libgxps') gxps_datadir = join_paths(gxps_prefix, get_option('datadir')) gxps_mandir = join_paths(get_option('prefix'), get_option('mandir')) cc = meson.get_compiler('c') host_system = host_machine.system() # Compiler flags if cc.get_id() == 'msvc' # Compiler options taken from msvc_recommended_pragmas.h # in GLib, based on _Win32_Programming_ by Rector and Newcomer common_flags = ['-FImsvc_recommended_pragmas.h'] else test_cflags = [ '-Wpointer-arith', '-Wmissing-declarations', '-Wformat=2', '-Wstrict-prototypes', '-Wmissing-prototypes', '-Wnested-externs', '-Wold-style-definition', '-Wdeclaration-after-statement', '-Wunused', '-Wno-uninitialized', '-Wshadow', '-Wcast-align', '-Wmissing-noreturn', '-Wmissing-format-attribute', '-Wlogical-op', '-Wno-discarded-qualifiers', '-Werror=implicit', '-Werror=nonnull', '-Werror=init-self', '-Werror=main', '-Werror=missing-braces', '-Werror=sequence-point', '-Werror=return-type', '-Werror=trigraphs', '-Werror=array-bounds', '-Werror=write-strings', '-Werror=address', '-Werror=int-to-pointer-cast', '-Werror=pointer-to-int-cast', '-fno-strict-aliasing', '-Wno-int-conversion', ] common_flags = cc.get_supported_arguments(test_cflags) endif extra_args= [] cdata = configuration_data() if host_system == 'windows' if cc.get_id() == 'msvc' cdata.set('_GXPS_EXTERN', '__declspec(dllexport) extern') else cdata.set('_GXPS_EXTERN', '__attribute__((visibility("default"))) __declspec(dllexport) extern') extra_args += ['-fvisibility=hidden'] endif else cdata.set('_GXPS_EXTERN', '__attribute__((visibility("default"))) extern') extra_args += ['-fvisibility=hidden'] endif core_inc = include_directories('.') # Required dependencies glib_req = '2.36.0' cairo_req = '1.10.0' archive_req = '2.8.0' glib_dep = dependency('glib-2.0', version: '>=' + glib_req) gobject_dep = dependency('gobject-2.0', version: '>=' + glib_req) gio_dep = dependency('gio-2.0', version: '>=' + glib_req) cairo_dep = dependency('cairo', version: '>=' + cairo_req, required: cc.get_id() != 'msvc') cairo_pdf_dep = dependency('cairo-pdf', version: '>=' + cairo_req, required: false) cairo_ps_dep = dependency('cairo-ps', version: '>=' + cairo_req, required: false) cairo_svg_dep = dependency('cairo-svg', version: '>=' + cairo_req, required: false) cairo_pc_found = cairo_dep.found() cairo_pdf_found = cairo_pdf_dep.found() cairo_ps_found = cairo_ps_dep.found() cairo_svg_found = cairo_svg_dep.found() if cc.get_id() == 'msvc' and not cairo_dep.found() if cc.has_header('cairo.h') cairo_dep = cc.find_library('cairo') if not cairo_pdf_dep.found() and cc.has_function('cairo_pdf_surface_create', prefix: '#include <cairo-pdf.h>', dependencies: cairo_dep) cairo_pdf_dep = cairo_dep cairo_pdf_found = true endif if not cairo_ps_dep.found() and cc.has_function('cairo_ps_surface_create', prefix: '#include <cairo-ps.h>', dependencies: cairo_dep) cairo_ps_dep = cairo_dep cairo_ps_found = true endif if not cairo_svg_dep.found() and cc.has_function('cairo_svg_surface_create', prefix: '#include <cairo-svg.h>', dependencies: cairo_dep) cairo_svg_dep = cairo_dep cairo_svg_found = true endif endif assert (cairo_dep.found(), 'Cairo is required') endif archive_dep = dependency('libarchive', version: '>=' + archive_req) freetype_dep = dependency('freetype2', required: cc.get_id() != 'msvc') if cc.get_id() == 'msvc' and not freetype_dep.found() if cc.has_header ('ft2build.h') freetype_dep = cc.find_library('freetype') endif assert(freetype_dep.found(), 'FreeType is required') endif png_dep = dependency('libpng', required: false) png_found = png_dep.found() if cc.get_id() == 'msvc' and not png_dep.found() and cc.has_header('png.h') # MSVC: First look for the DLL + import .lib build of libpng, # which is normally libpngxx.lib, when libpng's pkg-config can't # be found, which is quite normal on MSVC. foreach png: [ 'libpng16', 'libpng15', 'libpng14', 'libpng12', 'libpng13', 'libpng10' ] if not png_found png_lib = cc.find_library(png, required: false) if png_lib.found() zlib_lib = cc.find_library('zlib1') png_found = true endif endif endforeach if png_found png_dep = [png_lib, zlib_lib] endif endif lcms2_dep = dependency('lcms2', required: get_option('with-liblcms2') and cc.get_id() != 'msvc') if cc.get_id() == 'msvc' and not lcms2_dep.found() if cc.has_header('lcms2.h') lcms2_dep = cc.find_library('lcms2', required: get_option('with-liblcms2')) if lcms2_dep.found() # For MSVC builds, we need to check whether the lcms2 we have is a DLL build # and we must apply -DCMS_DLL to the cflags if it is so, otherwise the code # won't link message('MSVC builds: The following line checks whether lcms2 is built as a DLL...') if cc.has_function('cmsGetEncodedCMMversion', prefix: '''#define CMS_DLL #include <lcms2.h>''', dependencies: lcms2_dep) common_flags += '-DCMS_DLL' endif endif endif if get_option('with-liblcms2') assert (lcms2_dep.found(), 'LCMS2 requested but LCMS2 cannot be found') endif endif jpeg_dep = dependency('libjpeg', required: get_option('with-libjpeg') and cc.get_id() != 'msvc') if cc.get_id() == 'msvc' and not jpeg_dep.found() if cc.has_header('jpeglib.h') jpeg_dep = cc.find_library('jpeg', required: get_option('with-libjpeg')) endif if get_option('with-libjpeg') assert (jpeg_dep.found(), 'JPEG support requested but libjpeg/libjpeg-turbo cannot be found') endif endif tiff_dep = dependency('libtiff-4', required: get_option('with-libtiff') and cc.get_id() != 'msvc') if cc.get_id() == 'msvc' and not tiff_dep.found() if cc.has_header('tiff.h') tiff_dep = cc.find_library('libtiff_i', required: get_option('with-libtiff')) endif if get_option('with-libtiff') assert (tiff_dep.found(), 'TIFF support requested but libtiff cannot be found') endif endif cdata.set('HAVE_CAIRO_PDF', cairo_pdf_found) cdata.set('HAVE_CAIRO_PS', cairo_ps_found) cdata.set('HAVE_CAIRO_SVG', cairo_svg_found) cdata.set('HAVE_LIBPNG', png_found) cdata.set('HAVE_LIBLCMS2', lcms2_dep.found()) cdata.set('HAVE_LIBJPEG', jpeg_dep.found()) cdata.set('HAVE_LIBTIFF', tiff_dep.found()) # Maths functions might be implemented in libm libm_dep = cc.find_library('m', required: false) gnome = import('gnome') gir = find_program('g-ir-scanner', required: false) build_gir = gir.found() and not get_option('disable-introspection') configure_file(output: 'config.h', configuration: cdata) subdir('libgxps') subdir('tools') subdir('docs') if get_option('enable-test') gtk3_dep = dependency('gtk+-3.0') subdir('test') endif run_target('release', command: [join_paths('mesonscripts', 'release.sh'), meson.project_name(), meson.project_version()]) 07070100000044000081A40000000000000000000000016447D411000002D1000000000000000000000000000000000000002200000000libgxps-0.3.2+5/meson_options.txtoption('enable-test', type: 'boolean', value: true, description: 'Compile test application') option('enable-gtk-doc', type: 'boolean', value: false, description: 'Enable generating the API reference (depends on GTK-Doc)') option('enable-man', type: 'boolean', value: false, description: 'Whether to generate the man pages for libgxps tools') option('disable-introspection', type: 'boolean', value: false, description: 'Whether to disable the introspection generation') option('with-liblcms2', type: 'boolean', value: true, description: 'With Little CMS 2') option('with-libjpeg', type: 'boolean', value: true, description: 'With libjpeg') option('with-libtiff', type: 'boolean', value: true, description: 'With libtiff') 07070100000045000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001D00000000libgxps-0.3.2+5/mesonscripts07070100000046000081ED0000000000000000000000016447D41100000335000000000000000000000000000000000000002800000000libgxps-0.3.2+5/mesonscripts/release.sh#!/bin/sh NAME="$1" VERSION="$2" test -n "${MESON_SOURCE_ROOT}" || exit 1 test -n "${MESON_BUILD_ROOT}" || exit 1 test -n "${NAME}" || exit 1 test -n "${VERSION}" || exit 1 cd "${MESON_SOURCE_ROOT}" echo "Removing old archive…" rm -f "${NAME}-${VERSION}.tar" rm -f "${NAME}-${VERSION}.tar.xz" echo "Creating git archive…" git archive --prefix="${NAME}-${VERSION}/" --format=tar HEAD -o ${NAME}-${VERSION}.tar echo "Adding documentation to archive…" BUILD_DIR=$(realpath --relative-to=${MESON_SOURCE_ROOT} ${MESON_BUILD_ROOT}) tar --transform "s|${BUILD_DIR}|${NAME}-${VERSION}|" -uf ${NAME}-${VERSION}.tar ${BUILD_DIR}/docs/reference/html tar --transform "s|${BUILD_DIR}|${NAME}-${VERSION}|" -uf ${NAME}-${VERSION}.tar ${BUILD_DIR}/docs/tools/*.1 echo "Compressing archive…" xz -f "${NAME}-${VERSION}.tar" 07070100000047000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001800000000libgxps-0.3.2+5/regtest07070100000048000081A40000000000000000000000016447D4110000044B000000000000000000000000000000000000002200000000libgxps-0.3.2+5/regtest/Config.py# Config.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA class Config: shared_state = {} def __init__(self, config = None): if config is not None: self.__class__.shared_state = config self.__dict__ = self.__class__.shared_state if __name__ == '__main__': c = Config({'foo' : 25}) print(c.foo) cc = Config() print(cc.foo) 07070100000049000081A40000000000000000000000016447D411000021AC000000000000000000000000000000000000002600000000libgxps-0.3.2+5/regtest/HTMLReport.py# HTMLReport.py # # Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from Test import Test from Config import Config import os import errno import subprocess class HTMLPrettyDiff: def write(self, test, outdir, actual, expected, diff): raise NotImplementedError def _create_diff_for_test(self, outdir, test): diffdir = os.path.join(outdir, 'html', test) try: os.makedirs(diffdir) except OSError as e: if e.errno != errno.EEXIST: raise except: raise return diffdir class HTMLPrettyDiffImage(HTMLPrettyDiff): def write(self, test, outdir, result, actual, expected, diff): html = """ <html> <head> <title>%s</title> <style>.label{font-weight:bold}</style> </head> <body> Difference between images: <a href="%s">diff</a><br> <div class=imageText></div> <div class=imageContainer actual="%s" expected="%s">Loading...</div> <script> (function() { var preloadedImageCount = 0; function preloadComplete() { ++preloadedImageCount; if (preloadedImageCount < 2) return; toggleImages(); setInterval(toggleImages, 2000) } function preloadImage(url) { image = new Image(); image.addEventListener('load', preloadComplete); image.src = url; return image; } function toggleImages() { if (text.textContent == 'Expected Image') { text.textContent = 'Actual Image'; container.replaceChild(actualImage, container.firstChild); } else { text.textContent = 'Expected Image'; container.replaceChild(expectedImage, container.firstChild); } } var text = document.querySelector('.imageText'); var container = document.querySelector('.imageContainer'); var actualImage = preloadImage(container.getAttribute('actual')); var expectedImage = preloadImage(container.getAttribute('expected')); })(); </script> </body> </html> """ % (test, diff, actual, expected) diffdir = self._create_diff_for_test(outdir, test) pretty_diff = os.path.abspath(os.path.join(diffdir, result + '-pretty-diff.html')) f = open(pretty_diff, 'w') f.write(html) f.close() return pretty_diff class TestResult: def __init__(self, docsdir, refsdir, outdir, resultdir, results): self._refsdir = refsdir self._outdir = outdir self._test = Test() self.config = Config() self._test_name = resultdir[len(self._outdir):].lstrip('/') self._doc = os.path.join(docsdir, self._test_name) self._results = [] ref_path = os.path.join(self._refsdir, self._test_name) if not self._test.has_md5(ref_path): return ref_names = self._test.get_ref_names(ref_path) for result in results: basename = os.path.basename(result) if basename in ref_names: self._results.append(basename) def get_test_name(self): return self._test_name def is_failed(self): return len(self._results) > 0 def is_crashed(self): return self._test.is_crashed(os.path.join(self._outdir, self._test_name)) def is_failed_to_run(self): return self._test.is_failed(os.path.join(self._outdir, self._test_name)) def get_stderr(self): return self._test.get_stderr(os.path.join(self._outdir, self._test_name)) def get_failed_html(self): html = "" for result in self._results: actual = os.path.abspath(os.path.join(self._outdir, self._test_name, result)) expected = os.path.abspath(os.path.join(self._refsdir, self._test_name, result)) html += "<li><a href='%s'>actual</a> <a href='%s'>expected</a> " % (actual, expected) if self._test.has_diff(actual): diff = os.path.abspath(actual + '.diff.png') html += "<a href='%s'>diff</a> " % (diff) if self.config.pretty_diff: pretty_diff = HTMLPrettyDiffImage() if pretty_diff: html += "<a href='%s'>pretty diff</a> " % (pretty_diff.write (self._test_name, self._outdir, result, actual, expected, diff)) html += "</li>\n" if html: return "<h2><a name='%s'><a href='%s'>%s</a></a></h2>\n<ul>%s</ul><a href='#top'>Top</a>\n" % (self._test_name, self._doc, self._test_name, html) return "" def get_crashed_html(self): html = "" for result in self._results: if not self.is_crashed(): continue html += "<li><a href='%s'>%s</a></li>\n" % (self._doc, self._test_name) if html: return "<ul>%s</ul>\n" % (html) return "" def get_failed_to_run_html(self): html = "" for result in self._results: status = self.is_failed_to_run() if not status: continue html += "<li><a href='%s'>%s</a> [Status: %d]</li>\n" % (self._doc, self._test_name, status) if html: return "<ul>%s</ul>\n" % (html) return "" class HTMLReport: def __init__(self, docsdir, refsdir, outdir): self._docsdir = docsdir self._refsdir = refsdir self._outdir = outdir self._htmldir = os.path.join(outdir, 'html') self.config = Config() try: os.makedirs(self._htmldir) except OSError as e: if e.errno != errno.EEXIST: raise except: raise def create(self): html = "<html><body><a name='top'></a>" results = {} for root, dirs, files in os.walk(self._outdir, False): if not files: continue if not root.lower().endswith('.xps'): continue if root.startswith(self._htmldir): continue results[root] = TestResult(self._docsdir, self._refsdir, self._outdir, root, files) tests = results.keys() tests.sort() failed_anchors = [] failed = "" crashed = "" failed_to_run = "" for test_name in tests: test = results[test_name] if test.is_failed(): failed_anchors.append(test.get_test_name()) failed += test.get_failed_html() crashed += test.get_crashed_html() failed_to_run += test.get_failed_to_run_html() if failed: failed = "<h1><a name='failed'>Tests Failed (differences were found)</name></h1>\n%s" % (failed) if crashed: crashed = "<h1><a name='crashed'>Tests Crashed</a></h1>\n%s" % (crashed) if failed_to_run: failed_to_run = "<h1><a name='failed_to_run'>Tests that failed to run (command returned an error status)</a></h1>\n%s" % (failed_to_run) if failed or crashed or failed_to_run: html += "<ul>\n" if failed: html += "<li><a href='#failed'>Tests Failed (differences were found)</a></li>\n<ul>" for anchor in failed_anchors: html += "<li><a href='#%s'>%s</a></li>" % (anchor, anchor) html += "</ul>\n" if crashed: html += "<li><a href='#crashed'>Tests Crashed(differences were found)</a></li>\n" if failed_to_run: html += "<li><a href='#failed_to_run'>Tests that failed to run (command returned an error status)</a></li>\n" html += "</ul>\n" html += failed + crashed + failed_to_run + "</body></html>" report_index = os.path.join(self._htmldir, 'index.html') f = open(report_index, 'wb') f.write(html) f.close() subprocess.Popen(['xdg-open', report_index]) 0707010000004A000081A40000000000000000000000016447D41100000AC8000000000000000000000000000000000000002300000000libgxps-0.3.2+5/regtest/Printer.py# Printer.py # # Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import sys from Config import Config from threading import RLock class Printer: __single = None def __init__(self): if Printer.__single is not None: raise Printer.__single self._verbose = Config().verbose self._stream = sys.stdout self._rewrite = self._stream.isatty() and not self._verbose self._current_line_len = 0 self._lock = RLock() Printer.__single = self def _erase_current_line(self): if not self._current_line_len: return line_len = self._current_line_len self._stream.write('\b' * line_len + ' ' * line_len + '\b' * line_len) self._current_line_len = 0 def _ensure_new_line(self, msg): if not msg.endswith('\n'): msg += '\n' return msg def _print(self, msg): self._stream.write(msg) self._stream.flush() def printout(self, msg): if not self._rewrite: self.printout_ln(msg) with self._lock: self._erase_current_line() self._print(msg) self._current_line_len = len(msg[msg.rfind('\n') + 1:]) def printout_ln(self, msg=''): with self._lock: self._erase_current_line() self._print(self._ensure_new_line(msg)) def printerr(self, msg): with self._lock: self.stderr.write(self._ensure_new_line(msg)) self.stderr.flush() def print_test_result(self, doc_path, n_test, total_tests, msg): self.printout("[%d/%d] %s: %s" % (n_test, total_tests, doc_path, msg)) def print_test_result_ln(self, doc_path, n_test, total_tests, msg): self.printout_ln("[%d/%d] %s: %s" % (n_test, total_tests, doc_path, msg)) def print_default(self, msg): if self._verbose: self.printout_ln(msg) def get_printer(): try: instance = Printer() except Printer, i: instance = i return instance 0707010000004B000081A40000000000000000000000016447D41100002032000000000000000000000000000000000000002000000000libgxps-0.3.2+5/regtest/Test.py# Test # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import hashlib import os import subprocess import shutil import errno from Config import Config from Printer import get_printer class Test: def __init__(self): self._xpstopng = os.path.join(Config().tools_dir, 'xpstopng') self.printer = get_printer() def __md5sum(self, ref_path): md5 = hashlib.md5() with open(ref_path,'rb') as f: for chunk in iter(lambda: f.read(128 * md5.block_size), b''): md5.update(chunk) return md5.hexdigest() def __should_have_checksum(self, entry): return entry not in ('md5', 'crashed', 'failed', 'stderr'); def create_checksums(self, refs_path, delete_refs = False): path = os.path.join(refs_path, 'md5') md5_file = open(path, 'w') for entry in sorted(os.listdir(refs_path)): if not self.__should_have_checksum(entry): continue ref_path = os.path.join(refs_path, entry) md5_file.write("%s %s\n" % (self.__md5sum(ref_path), ref_path)) if delete_refs: os.remove(ref_path) md5_file.close() def compare_checksums(self, refs_path, out_path, remove_results = True, create_diffs = True, update_refs = False): retval = True md5_path = os.path.join(refs_path, 'md5') md5_file = open(md5_path, 'r') tests = os.listdir(out_path) result_md5 = [] for line in md5_file.readlines(): md5sum, ref_path = line.strip('\n').split(' ', 1) basename = os.path.basename(ref_path) if not self.__should_have_checksum(basename): continue if not basename in tests: retval = False self.printer.print_default("%s found in md5 ref file but missing in output dir %s" % (basename, out_path)) continue result_path = os.path.join(out_path, basename) result_md5sum = self.__md5sum(result_path); matched = md5sum == result_md5sum if update_refs: result_md5.append("%s %s\n" % (result_md5sum, ref_path)) if matched: if remove_results: os.remove(result_path) else: self.printer.print_default("Differences found in %s" % (basename)) if create_diffs: if not os.path.exists(ref_path): self.printer.print_default("Reference file %s not found, skipping diff for %s" % (ref_path, result_path)) else: self._create_diff(ref_path, result_path) if update_refs: if os.path.exists(ref_path): self.printer.print_default("Updating image reference %s" % (ref_path)) shutil.copyfile(result_path, ref_path) retval = False md5_file.close() if update_refs and not retval: self.printer.print_default("Updating md5 reference %s" % (md5_path)) f = open(md5_path + '.tmp', 'wb') f.writelines(result_md5) f.close() os.rename(md5_path + '.tmp', md5_path) for ref in ('crashed', 'failed', 'stderr'): src = os.path.join(out_path, ref) dest = os.path.join(refs_path, ref) try: shutil.copyfile(src, dest) except IOError as e: if e.errno != errno.ENOENT: raise return retval def update_results(self, refs_path, out_path): if not self.has_md5(refs_path): path = os.path.join(refs_path, 'md5') md5_file = open(path, 'w') for entry in sorted(os.listdir(out_path)): if not self.__should_have_checksum(entry): continue result_path = os.path.join(out_path, entry) ref_path = os.path.join(refs_path, entry) md5_file.write("%s %s\n" % (self.__md5sum(result_path), ref_path)) shutil.copyfile(result_path, ref_path) md5_file.close() for ref in ('crashed', 'failed', 'stderr'): result_path = os.path.join(out_path, ref) ref_path = os.path.join(refs_path, ref) if os.path.exists(result_path): shutil.copyfile(result_path, ref_path) elif os.path.exists(ref_path): os.remove(ref_path) def get_ref_names(self, refs_path): retval = [] md5_path = os.path.join(refs_path, 'md5') md5_file = open(md5_path, 'r') for line in md5_file.readlines(): md5sum, ref_path = line.strip('\n').split(' ', 1) basename = os.path.basename(ref_path) if not self.__should_have_checksum(basename): continue retval.append(basename) md5_file.close() return retval def has_md5(self, test_path): return os.path.exists(os.path.join(test_path, 'md5')) def is_crashed(self, test_path): return os.path.exists(os.path.join(test_path, 'crashed')) def is_failed(self, test_path): failed_path = os.path.join(test_path, 'failed') if not os.path.exists(failed_path): return 0 f = open(failed_path, 'r') status = int(f.read()) f.close() return status def has_results(self, test_path): return self.has_md5(test_path) or self.is_crashed(test_path) or self.is_failed(test_path) def get_stderr(self, test_path): return os.path.join(test_path, 'stderr') def has_stderr(self, test_path): return os.path.exists(self.get_stderr(test_path)) def has_diff(self, test_result): return os.path.exists(test_result + '.diff.png') def __create_stderr_file(self, stderr, out_path): if not stderr: return stderr_file = open(os.path.join(out_path, 'stderr'), 'wb') stderr_file.write(stderr) stderr_file.close() def __create_failed_file_if_needed(self, status, out_path): if os.WIFEXITED(status) or os.WEXITSTATUS(status) == 0: return False failed_file = open(os.path.join(out_path, 'failed'), 'w') failed_file.write("%d" % (os.WEXITSTATUS(status))) failed_file.close() return True def _check_exit_status(self, p, out_path): stderr = p.stderr.read() status = p.wait() self.__create_stderr_file(stderr, out_path) if not os.WIFEXITED(status): open(os.path.join(out_path, 'crashed'), 'w').close() return False if self.__create_failed_file_if_needed(status, out_path): return False return True def _create_diff(self, ref_path, result_path): try: from PIL import Image, ImageChops except ImportError: raise NotImplementedError ref = Image.open(ref_path) result = Image.open(result_path) diff = ImageChops.difference(ref, result) diff.save(result_path + '.diff.png', 'png') def create_refs(self, doc_path, refs_path): out_path = os.path.join(refs_path, 'page') p = subprocess.Popen([self._xpstopng, '-r', '72', doc_path, out_path], stderr = subprocess.PIPE) return self._check_exit_status(p, refs_path) 0707010000004C000081A40000000000000000000000016447D41100000DCF000000000000000000000000000000000000002A00000000libgxps-0.3.2+5/regtest/TestReferences.py# TestReferences.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import os import errno from Test import Test from Config import Config from Printer import get_printer from Utils import get_document_paths_from_dir, get_skipped_tests from Queue import Queue from threading import Thread, RLock class TestReferences: def __init__(self, docsdir, refsdir): self._docsdir = docsdir self._refsdir = refsdir self._skipped = get_skipped_tests(docsdir) self._test = Test() self.config = Config() self.printer = get_printer() self._total_tests = 1 self._n_tests = 0 self._queue = Queue() self._lock = RLock() try: os.makedirs(self._refsdir) except OSError as e: if e.errno != errno.EEXIST: raise except: raise def create_refs_for_file(self, filename): if filename in self._skipped: with self._lock: self._n_tests += 1 self.printer.print_default("Skipping test '%s'" % (os.path.join(self._docsdir, filename))) return refs_path = os.path.join(self._refsdir, filename) try: os.makedirs(refs_path) except OSError as e: if e.errno != errno.EEXIST: raise except: raise doc_path = os.path.join(self._docsdir, filename) if not self.config.force and self._test.has_results(refs_path): with self._lock: self._n_tests += 1 self.printer.print_default("Results found, skipping '%s'" % doc_path) return if self._test.create_refs(doc_path, refs_path): self._test.create_checksums(refs_path, self.config.checksums_only) with self._lock: self._n_tests += 1 self.printer.printout_ln("[%d/%d] %s: done" % (self._n_tests, self._total_tests, doc_path)) def _worker_thread(self): while True: doc = self._queue.get() self.create_refs_for_file(doc) self._queue.task_done() def create_refs(self): docs, total_docs = get_document_paths_from_dir(self._docsdir) self._total_tests = total_docs self.printer.printout_ln('Found %d documents' % (total_docs)) self.printer.printout_ln('Process %d using %d worker threads' % (os.getpid(), self.config.threads)) self.printer.printout_ln() self.printer.printout('Spawning %d workers...' % (self.config.threads)) for n_thread in range(self.config.threads): thread = Thread(target=self._worker_thread) thread.daemon = True thread.start() for doc in docs: self._queue.put(doc) self._queue.join() 0707010000004D000081A40000000000000000000000016447D411000020F6000000000000000000000000000000000000002300000000libgxps-0.3.2+5/regtest/TestRun.py# TestRun.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from Test import Test from Config import Config from Utils import get_document_paths_from_dir, get_skipped_tests from Printer import get_printer import sys import os import errno from Queue import Queue from threading import Thread, RLock class TestRun: def __init__(self, docsdir, refsdir, outdir): self._docsdir = docsdir self._refsdir = refsdir self._outdir = outdir self._skipped = get_skipped_tests(docsdir) self._test = Test() self.config = Config() self.printer = get_printer() self._total_tests = 1 # Results self._n_tests = 0 self._n_run = 0 self._n_passed = 0 self._failed = [] self._crashed = [] self._failed_status_error = [] self._did_not_crash = [] self._did_not_fail_status_error = [] self._stderr = [] self._new = [] self._queue = Queue() self._lock = RLock() try: os.makedirs(self._outdir); except OSError as e: if e.errno != errno.EEXIST: raise except: raise def test(self, refs_path, doc_path, test_path): # First check whether there are test results ref_has_md5 = self._test.has_md5(refs_path) ref_is_crashed = self._test.is_crashed(refs_path) ref_is_failed = self._test.is_failed(refs_path) if not ref_has_md5 and not ref_is_crashed and not ref_is_failed: with self._lock: self._new.append(doc_path) self._n_tests += 1 self.printer.print_default("Reference files not found, skipping '%s'" % (doc_path)) return test_has_md5 = self._test.create_refs(doc_path, test_path) test_passed = False if ref_has_md5 and test_has_md5: test_passed = self._test.compare_checksums(refs_path, test_path, not self.config.keep_results, self.config.create_diffs, self.config.update_refs) elif self.config.update_refs: self._test.update_results(refs_path, test_path) with self._lock: self._n_tests += 1 self._n_run += 1 if self._test.has_stderr(test_path): self._stderr.append(doc_path) if ref_has_md5 and test_has_md5: if test_passed: # FIXME: remove dir if it's empty? self.printer.print_test_result(doc_path, self._n_tests, self._total_tests, "PASS") self._n_passed += 1 else: self.printer.print_test_result_ln(doc_path, self._n_tests, self._total_tests, "FAIL") self._failed.append(doc_path) return if test_has_md5: if ref_is_crashed: self.printer.print_test_result_ln(doc_path, self._n_tests, self._total_tests, "DOES NOT CRASH") self._did_not_crash.append(doc_path) elif ref_is_failed: self.printer.print_test_result_ln(doc_path, self._n_tests, self._total_tests, "DOES NOT FAIL") self._did_not_fail_status_error.append(doc_path) return test_is_crashed = self._test.is_crashed(test_path) if ref_is_crashed and test_is_crashed: self.printer.print_test_result(doc_path, self._n_tests, self._total_tests, "PASS (Expected crash)") self._n_passed += 1 return test_is_failed = self._test.is_failed(test_path) if ref_is_failed and test_is_failed: # FIXME: compare status errors self.printer.print_test_result(doc_path, self._n_tests, self._total_tests, "PASS (Expected fail with status error %d)" % (test_is_failed)) self._n_passed += 1 return if test_is_crashed: self.printer.print_test_result_ln(doc_path, self._n_tests, self._total_tests, "CRASH") self._crashed.append(doc_path) return if test_is_failed: self.printer.print_test_result_ln(doc_path, self._n_tests, self._total_tests, "FAIL (status error %d)" % (test_is_failed)) self._failed_status_error(doc_path) return def run_test(self, filename): if filename in self._skipped: with self._lock: self._n_tests += 1 self.printer.print_default("Skipping test '%s'" % (doc_path)) return out_path = os.path.join(self._outdir, filename) try: os.makedirs(out_path) except OSError as e: if e.errno != errno.EEXIST: raise except: raise doc_path = os.path.join(self._docsdir, filename) refs_path = os.path.join(self._refsdir, filename) if not os.path.isdir(refs_path): with self._lock: self._new.append(doc_path) self._n_tests += 1 self.printer.print_default("Reference dir not found for %s, skipping" % (doc_path)) return self.test(refs_path, doc_path, out_path) def _worker_thread(self): while True: doc = self._queue.get() self.run_test(doc) self._queue.task_done() def run_tests(self): docs, total_docs = get_document_paths_from_dir(self._docsdir) self._total_tests = total_docs if total_docs == 1: n_workers = 0 else: n_workers = min(self.config.threads, total_docs) self.printer.printout_ln('Found %d documents' % (total_docs)) self.printer.printout_ln('Process %d using %d worker threads' % (os.getpid(), n_workers)) self.printer.printout_ln() if n_workers > 0: self.printer.printout('Spawning %d workers...' % (n_workers)) for n_thread in range(n_workers): thread = Thread(target=self._worker_thread) thread.daemon = True thread.start() for doc in docs: self._queue.put(doc) self._queue.join() else: for doc in docs: self.run_test(doc) return int(self._n_passed != self._n_run) def summary(self): self.printer.printout_ln() if self._n_run: self.printer.printout_ln("%d tests passed (%.2f%%)" % (self._n_passed, (self._n_passed * 100.) / self._n_run)) self.printer.printout_ln() def report_tests(test_list, test_type): n_tests = len(test_list) if not n_tests: return self.printer.printout_ln("%d tests %s (%.2f%%): %s" % (n_tests, test_type, (n_tests * 100.) / self._n_run, ", ".join(test_list))) self.printer.printout_ln() report_tests(self._failed, "failed") report_tests(self._crashed, "crashed") report_tests(self._failed_status_error, "failed to run") report_tests(self._stderr, "have stderr output") report_tests(self._did_not_crash, "expected to crash, but didn't crash") report_tests(self._did_not_fail_status_error, "expected to fail to run, but didn't fail") else: self.printer.printout_ln("No tests run") if self._new: self.printer.printout_ln("%d new documents: %s\nUse create-refs command to add reference results for them" % (len(self._new), ", ".join(self._new))) self.printer.printout_ln() 0707010000004E000081A40000000000000000000000016447D411000007C3000000000000000000000000000000000000002100000000libgxps-0.3.2+5/regtest/Timer.py# Timer.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from time import time, strftime, gmtime class Timer: def __init__(self, start = True): self._stop = None if start: self.start() else: self._start = None def start(self): self._start = time() def stop(self): self._stop = time() def elapsed(self): if self._start is None: return 0 if self._stop is None: return time() - self._start return self._stop - self._start def elapsed_str(self): h, m, s = [int(i) for i in strftime('%H:%M:%S', gmtime(self.elapsed())).split(':')] retval = "%d seconds" % (s) if h == 0 and m == 0: return retval retval = "%d minutes and %s" % (m, retval) if h == 0: return retval retval = "%d hours, %s" % (h, retval) return retval if __name__ == '__main__': from time import sleep t = Timer() sleep(5) print("Elapsed: %s" % (t.elapsed_str())) sleep(1) print("Elapsed: %s" % (t.elapsed_str())) t.start() sleep(2) t.stop() print("Elapsed: %s" % (t.elapsed_str())) sleep(2) print("Elapsed: %s" % (t.elapsed_str())) 0707010000004F000081A40000000000000000000000016447D411000006DB000000000000000000000000000000000000002100000000libgxps-0.3.2+5/regtest/Utils.py# Utils.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import os def get_document_paths_from_dir(docsdir): paths = [] n_paths = 0 for root, dirs, files in os.walk(docsdir, False): for entry in files: if not entry.lower().endswith('.xps'): continue test_path = os.path.join(root[len(docsdir):], entry) paths.append(test_path.lstrip(os.path.sep)) n_paths += 1 paths.sort() return paths, n_paths def get_skipped_tests(docsdir): from Config import Config config = Config() if config.skipped_file: skipped_file = config.skipped_file elif os.path.exists(os.path.join(docsdir, 'Skipped')): skipped_file = os.path.join(docsdir, 'Skipped') else: return [] skipped = [] f = open(skipped_file, 'r') for line in f.readlines(): line = line.rstrip('\n \t\b\r') if not line or line[0] == '#': continue skipped.append(line) f.close() return skipped 07070100000050000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000002100000000libgxps-0.3.2+5/regtest/commands07070100000051000081A40000000000000000000000016447D41100000AC8000000000000000000000000000000000000002D00000000libgxps-0.3.2+5/regtest/commands/__init__.py# commands # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import argparse __all__ = [ 'register_command', 'print_help', 'run', 'UnknownCommandError', 'Command' ] class UnknownCommandError(Exception): '''Unknown command''' class Command: name = None usage_args = '[ options ... ]' description = None def __init__(self): self._parser = argparse.ArgumentParser( description = self.description, prog = 'gxps-regtest %s' % (self.name), usage = 'gxps-regtest %s %s' % (self.name, self.usage_args)) def _get_args_parser(self): return self._parser def execute(self, args): ns = self._parser.parse_args(args) return self.run(vars(ns)) def run(self, options): raise NotImplementedError _commands = {} def register_command(command_name, command_class): _commands[command_name] = command_class def _get_command(command_name): if command_name not in _commands: try: __import__('commands.%s' % command_name) except ImportError: pass if command_name not in _commands: raise UnknownCommandError('Invalid %s command' % command_name) return _commands[command_name] def run(args): command_class = _get_command(args[0]) command = command_class() return command.execute(args[1:]) def print_help(): import os thisdir = os.path.abspath(os.path.dirname(__file__)) for fname in os.listdir(os.path.join(thisdir)): name, ext = os.path.splitext(fname) if not ext == '.py': continue try: __import__('commands.%s' % name) except ImportError: pass print("Commands are:") commands = [(x.name, x.description) for x in _commands.values()] commands.sort() for name, description in commands: print(" %-15s %s" % (name, description)) print print("For more information run 'gxps-regtest --help-command <command>'") 07070100000052000081A40000000000000000000000016447D41100000A5B000000000000000000000000000000000000003000000000libgxps-0.3.2+5/regtest/commands/create-refs.py# create-refs.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from commands import Command, register_command from TestReferences import TestReferences from Timer import Timer from Config import Config from Printer import get_printer import os import tempfile class CreateRefs(Command): name = 'create-refs' usage_args = '[ options ... ] tests ' description = 'Create references for tests' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--refs-dir', action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), help = 'Directory where the references will be created') parser.add_argument('-f', '--force', action = 'store_true', dest = 'force', default = False, help = 'Create references again for tests that already have references') parser.add_argument('-c', '--checksums-only', action = 'store_true', dest = 'checksums_only', default = False, help = 'Leave only checksum files in references dir, other files will be deleted') parser.add_argument('tests') def run(self, options): config = Config() config.force = options['force'] config.checksums_only = options['checksums_only'] t = Timer() doc = options['tests'] if os.path.isdir(doc): docs_dir = doc else: docs_dir = os.path.dirname(doc) refs = TestReferences(docs_dir, options['refs_dir']) if doc == docs_dir: refs.create_refs() else: refs.create_refs_for_file(os.path.basename(doc)) get_printer().printout_ln("Refs created in %s" % (t.elapsed_str())) return 0 register_command('create-refs', CreateRefs) 07070100000053000081A40000000000000000000000016447D41100000904000000000000000000000000000000000000003200000000libgxps-0.3.2+5/regtest/commands/create-report.py# create-report.py # # Copyright (C) 2012 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from commands import Command, register_command from HTMLReport import HTMLReport from Config import Config import os import tempfile class CreateReport(Command): name = 'create-report' usage_args = '[ options ... ] tests ' description = 'Create report of test results' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--refs-dir', action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), help = 'Directory containing the references') parser.add_argument('-o', '--out-dir', action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'), help = 'Directory containing the results') parser.add_argument('-p', '--pretty-diff', action = 'store_true', dest = 'pretty_diff', default = False, help = 'Include pretty diff output') parser.add_argument('tests') def run(self, options): config = Config() config.pretty_diff = options['pretty_diff'] doc = options['tests'] if os.path.isdir(doc): docs_dir = doc else: docs_dir = os.path.dirname(doc) report = HTMLReport(docs_dir, options['refs_dir'], options['out_dir']) report.create() return 0 register_command('create-report', CreateReport) 07070100000054000081A40000000000000000000000016447D41100000C2E000000000000000000000000000000000000002E00000000libgxps-0.3.2+5/regtest/commands/run-tests.py# run-tests.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from commands import Command, register_command from TestRun import TestRun from Timer import Timer from Config import Config from Printer import get_printer import os import tempfile class RunTests(Command): name = 'run-tests' usage_args = '[ options ... ] tests ' description = 'Run tests for documents' def __init__(self): Command.__init__(self) parser = self._get_args_parser() parser.add_argument('--refs-dir', action = 'store', dest = 'refs_dir', default = os.path.join(tempfile.gettempdir(), 'refs'), help = 'Directory containing the references') parser.add_argument('-o', '--out-dir', action = 'store', dest = 'out_dir', default = os.path.join(tempfile.gettempdir(), 'out'), help = 'Directory where test results will be created') parser.add_argument('--keep-results', action = 'store_true', dest = 'keep_results', default = False, help = 'Do not remove result files for passing tests') parser.add_argument('--create-diffs', action = 'store_true', dest = 'create_diffs', default = False, help = 'Create diff files for failed tests') parser.add_argument('--update-refs', action = 'store_true', dest = 'update_refs', default = False, help = 'Update references for failed tests') parser.add_argument('tests') def run(self, options): config = Config() config.keep_results = options['keep_results'] config.create_diffs = options['create_diffs'] config.update_refs = options['update_refs'] t = Timer() doc = options['tests'] if os.path.isdir(doc): docs_dir = doc else: docs_dir = os.path.dirname(doc) tests = TestRun(docs_dir, options['refs_dir'], options['out_dir']) if doc == docs_dir: status = tests.run_tests() else: status = tests.run_test(os.path.basename(doc)) tests.summary() get_printer().printout_ln("Tests run in %s" % (t.elapsed_str())) return status register_command('run-tests', RunTests) 07070100000055000081ED0000000000000000000000016447D41100000051000000000000000000000000000000000000002500000000libgxps-0.3.2+5/regtest/gxps-regtest#!/usr/bin/env python import sys import main sys.exit(main.main(sys.argv[1:])) 07070100000056000081A40000000000000000000000016447D41100000C6C000000000000000000000000000000000000002000000000libgxps-0.3.2+5/regtest/main.py# main.py # # Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import sys import argparse import commands import os from Config import Config from multiprocessing import cpu_count class ListAction(argparse.Action): def __call__(self, parser, namespace, values, option_string = None): setattr(namespace, self.dest, values.split(',')) class HelpAction(argparse.Action): def __call__(self, parser, namespace, values, option_string = None): if option_string == '--help-command': commands.run([values, '--help']) sys.exit(0) parser.print_help() commands.print_help() sys.exit(0) def main(args): n_cpus = cpu_count() parser = argparse.ArgumentParser( description = 'libgxps regression tests', prog = 'gxps-regtest', usage = '%(prog)s [options ...] command [command-options ...] tests', add_help = False) parser.add_argument('-h', '--help', action = HelpAction, nargs = 0) parser.add_argument('--help-command', metavar = 'COMMAND', action = HelpAction, help = 'Show help for a given command') parser.add_argument('-v', '--verbose', action = 'store_true', dest = 'verbose', default = False, help = 'Run in verbose mode') parser.add_argument('--tools-dir', action = 'store', dest = 'tools_dir', default = os.path.abspath("../tools"), help = 'Directory of gxps tools used for the tests') parser.add_argument('--skip', metavar = 'FILE', action = 'store', dest = 'skipped_file', help = 'File containing tests to skip') parser.add_argument('-t', '--threads', action = 'store', dest = 'threads', type = int, default = n_cpus, help = 'Number of worker threads (Default: %d)' % n_cpus) ns, args = parser.parse_known_args(args) if not args: parser.print_help() sys.exit(0) config = Config(vars(ns)) if config.threads <= 0: config.threads = n_cpus - config.threads try: return commands.run(args) except commands.UnknownCommandError: sys.stderr.write("Unknown command: %s\n" % (args[0])) commands.print_help() return 1 if __name__ == '__main__': import sys sys.exit(main(sys.argv[1:])) 07070100000057000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001500000000libgxps-0.3.2+5/test07070100000058000081A40000000000000000000000016447D411000000E4000000000000000000000000000000000000002100000000libgxps-0.3.2+5/test/meson.buildtest_programs = [ 'test-gxps', ] foreach test_program: test_programs executable(test_program, test_program + '.c', dependencies: [ gxps_dep, gtk3_dep ], include_directories: gxps_inc) endforeach 07070100000059000081A40000000000000000000000016447D4110000266C000000000000000000000000000000000000002100000000libgxps-0.3.2+5/test/test-gxps.c#include <glib.h> #include <gio/gio.h> #include <cairo.h> #include <gtk/gtk.h> #include <stdlib.h> #include <libgxps/gxps.h> typedef struct { GtkWidget *darea; GtkWidget *spin_button; GXPSDocument *doc; cairo_surface_t *surface; } GXPSView; static gboolean drawing_area_draw (GtkWidget *drawing_area, cairo_t *cr, GXPSView *view) { if (!view->surface) return FALSE; cairo_set_source_rgb (cr, 1., 1., 1.); cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (view->surface), cairo_image_surface_get_height (view->surface)); cairo_fill (cr); cairo_set_source_surface (cr, view->surface, 0, 0); cairo_paint (cr); return TRUE; } static void page_changed_callback (GtkSpinButton *button, GXPSView *view) { GXPSPage *xps_page; gint page; gdouble width, height; cairo_t *cr; GError *error = NULL; page = gtk_spin_button_get_value_as_int (button); xps_page = gxps_document_get_page (view->doc, page, &error); if (error) { g_printerr ("Error getting page %d: %s\n", page, error->message); g_error_free (error); return; } gxps_page_get_size (xps_page, &width, &height); cairo_surface_destroy (view->surface); view->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); cr = cairo_create (view->surface); gxps_page_render (xps_page, cr, &error); if (error) { g_printerr ("Error rendering page: %d: %s\n", page, error->message); g_error_free (error); } cairo_destroy (cr); gtk_widget_set_size_request (view->darea, width, height); gtk_widget_queue_draw (view->darea); g_object_unref (xps_page); } static gchar * format_date (time_t utime) { time_t time = (time_t) utime; char s[256]; const char *fmt_hack = "%c"; size_t len; #ifdef HAVE_LOCALTIME_R struct tm t; if (time == 0 || !localtime_r (&time, &t)) return NULL; len = strftime (s, sizeof (s), fmt_hack, &t); #else struct tm *t; if (time == 0 || !(t = localtime (&time)) ) return NULL; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" len = strftime (s, sizeof (s), fmt_hack, t); #pragma GCC diagnostic pop #endif if (len == 0 || s[0] == '\0') return NULL; return g_locale_to_utf8 (s, -1, NULL, NULL, NULL); } static void append_row_to_table (GtkWidget *table, const gchar *key, const gchar *value) { GtkWidget *key_label; GtkWidget *value_label; if (!value) return; key_label = gtk_label_new (NULL); g_object_set (G_OBJECT (key_label), "xalign", 0.0, NULL); gtk_label_set_markup (GTK_LABEL (key_label), key); gtk_container_add (GTK_CONTAINER (table), key_label); gtk_widget_show (key_label); value_label = gtk_label_new (value); g_object_set (G_OBJECT (value_label), "xalign", 0.0, "selectable", TRUE, "ellipsize", PANGO_ELLIPSIZE_END, "hexpand", TRUE, NULL); gtk_grid_attach_next_to (GTK_GRID (table), value_label, key_label, GTK_POS_RIGHT, 1, 1); gtk_widget_show (value_label); } static void properties_button_clicked (GtkWidget *button, GXPSFile *xps) { GtkWidget *dialog; GtkWidget *table; GXPSCoreProperties *core_props; gchar *date; GError *error = NULL; core_props = gxps_file_get_core_properties (xps, &error); if (!core_props) { if (error) { g_printerr ("Error getting core properties: %s\n", error->message); g_error_free (error); } return; } dialog = gtk_dialog_new_with_buttons ("Document Properties", GTK_WINDOW (gtk_widget_get_toplevel (button)), GTK_DIALOG_DESTROY_WITH_PARENT, "Close", GTK_RESPONSE_CLOSE, NULL); g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); table = gtk_grid_new (); gtk_container_set_border_width (GTK_CONTAINER (table), 5); gtk_orientable_set_orientation (GTK_ORIENTABLE (table), GTK_ORIENTATION_VERTICAL); gtk_grid_set_column_spacing (GTK_GRID (table), 6); gtk_grid_set_row_spacing (GTK_GRID (table), 6); append_row_to_table (table, "<b>Title:</b>", gxps_core_properties_get_title (core_props)); append_row_to_table (table, "<b>Creator:</b>", gxps_core_properties_get_creator (core_props)); append_row_to_table (table, "<b>Description:</b>", gxps_core_properties_get_description (core_props)); append_row_to_table (table, "<b>Subject:</b>", gxps_core_properties_get_subject (core_props)); append_row_to_table (table, "<b>Keywords:</b>", gxps_core_properties_get_keywords (core_props)); append_row_to_table (table, "<b>Version:</b>", gxps_core_properties_get_version (core_props)); append_row_to_table (table, "<b>Revision:</b>", gxps_core_properties_get_revision (core_props)); append_row_to_table (table, "<b>Identifier:</b>", gxps_core_properties_get_identifier (core_props)); append_row_to_table (table, "<b>Language:</b>", gxps_core_properties_get_language (core_props)); append_row_to_table (table, "<b>Category:</b>", gxps_core_properties_get_category (core_props)); append_row_to_table (table, "<b>Content Status:</b>", gxps_core_properties_get_content_status (core_props)); append_row_to_table (table, "<b>Content Type:</b>", gxps_core_properties_get_content_type (core_props)); date = format_date (gxps_core_properties_get_created (core_props)); append_row_to_table (table, "<b>Created:</b>", date); g_free (date); append_row_to_table (table, "<b>Last Modified By:</b>", gxps_core_properties_get_last_modified_by (core_props)); date = format_date (gxps_core_properties_get_modified (core_props)); append_row_to_table (table, "<b>Modified:</b>", date); g_free (date); date = format_date (gxps_core_properties_get_last_printed (core_props)); append_row_to_table (table, "<b>Las Printed:</b>", date); g_free (date); gtk_container_add (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), table); gtk_widget_show (table); gtk_widget_show (dialog); g_object_unref (core_props); } gint main (gint argc, gchar **argv) { GFile *file; GXPSFile *xps; GXPSView *view; GtkWidget *win; GtkWidget *hbox, *vbox, *sw; GtkWidget *button; guint page = 0; GError *error = NULL; if (argc < 2) { g_printerr ("Use: test-xps file\n"); return 1; } gtk_init (&argc, &argv); file = g_file_new_for_commandline_arg (argv[1]); xps = gxps_file_new (file, &error); g_object_unref (file); if (error) { g_printerr ("Error creating file: %s\n", error->message); g_error_free (error); g_object_unref (xps); return 1; } view = g_new0 (GXPSView, 1); view->doc = gxps_file_get_document (xps, 0, &error); if (error) { g_printerr ("Error getting document 0: %s\n", error->message); g_error_free (error); g_object_unref (xps); return 1; } win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (win), 600, 600); g_signal_connect (win, "delete-event", G_CALLBACK (gtk_main_quit), NULL); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); view->spin_button = gtk_spin_button_new_with_range (0, gxps_document_get_n_pages (view->doc) - 1, 1); g_signal_connect (view->spin_button, "value-changed", G_CALLBACK (page_changed_callback), view); gtk_box_pack_end (GTK_BOX (hbox), view->spin_button, FALSE, TRUE, 0); gtk_widget_show (view->spin_button); button = gtk_button_new (); g_signal_connect (button, "clicked", G_CALLBACK (properties_button_clicked), xps); gtk_button_set_image (GTK_BUTTON (button), gtk_image_new_from_icon_name ("document-properties", GTK_ICON_SIZE_SMALL_TOOLBAR)); gtk_box_pack_end (GTK_BOX (hbox), button, FALSE, FALSE, 0); gtk_widget_show (button); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_widget_show (hbox); view->darea = gtk_drawing_area_new (); g_signal_connect (view->darea, "draw", G_CALLBACK (drawing_area_draw), view); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_container_add (GTK_CONTAINER (sw), view->darea); gtk_widget_show (view->darea); gtk_box_pack_end (GTK_BOX (vbox), sw, TRUE, TRUE, 0); gtk_widget_show (sw); gtk_container_add (GTK_CONTAINER (win), vbox); gtk_widget_show (vbox); gtk_widget_show (win); if (argc > 2) page = atoi (argv[2]); gtk_spin_button_set_value (GTK_SPIN_BUTTON (view->spin_button), page); if (page == 0) page_changed_callback (GTK_SPIN_BUTTON (view->spin_button), view); gtk_main (); g_object_unref (view->doc); g_object_unref (xps); cairo_surface_destroy (view->surface); g_free (view); return 0; } 0707010000005A000041ED0000000000000000000000026447D41100000000000000000000000000000000000000000000001600000000libgxps-0.3.2+5/tools0707010000005B000081A40000000000000000000000016447D41100000351000000000000000000000000000000000000002700000000libgxps-0.3.2+5/tools/Makefile.sourcesNULL = LIBGXPS_TOOLS_SOURCES = \ gxps-converter.c \ gxps-converter.h \ gxps-image-converter.c \ gxps-image-converter.h \ gxps-image-writer.c \ gxps-image-writer.h \ gxps-print-converter.c \ gxps-print-converter.h \ $(NULL) XPS_TO_PNG_SOURCES = \ gxps-converter-main.c \ gxps-png-converter.c \ gxps-png-converter.h \ gxps-png-writer.c \ gxps-png-writer.h \ $(NULL) XPS_TO_JPEG_SOURCES = \ gxps-converter-main.c \ gxps-jpeg-converter.c \ gxps-jpeg-converter.h \ gxps-jpeg-writer.c \ gxps-jpeg-writer.h \ $(NULL) XPS_TO_PDF_SOURCES = \ gxps-converter-main.c \ gxps-pdf-converter.c \ gxps-pdf-converter.h \ $(NULL) XPS_TO_PS_SOURCES = \ gxps-converter-main.c \ gxps-ps-converter.c \ gxps-ps-converter.h \ $(NULL) XPS_TO_SVG_SOURCES = \ gxps-converter-main.c \ gxps-svg-converter.c \ gxps-svg-converter.h \ $(NULL) 0707010000005C000081A40000000000000000000000016447D4110000055C000000000000000000000000000000000000002C00000000libgxps-0.3.2+5/tools/gxps-converter-main.c/* Main for GXPS converters * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include <glib.h> #include <locale.h> #include G_STRINGIFY(CONVERTER_HEADER) gint main (gint argc, gchar **argv) { GXPSConverter *converter; setlocale (LC_ALL, ""); converter = GXPS_CONVERTER (g_object_new (CONVERTER_TYPE, NULL)); if (!gxps_converter_init_with_args (converter, &argc, &argv)) { g_object_unref (converter); return 1; } gxps_converter_run (converter); g_object_unref (converter); return 0; } 0707010000005D000081A40000000000000000000000016447D41100002F57000000000000000000000000000000000000002700000000libgxps-0.3.2+5/tools/gxps-converter.c/* GXPSConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-converter.h" #include <gio/gio.h> #include <math.h> G_DEFINE_ABSTRACT_TYPE (GXPSConverter, gxps_converter, G_TYPE_OBJECT) static guint document = 0; static guint first_page = 0; static guint last_page = 0; static gboolean only_odd = FALSE; static gboolean only_even = FALSE; static gdouble resolution = 0.0; static gdouble x_resolution = 150.0; static gdouble y_resolution = 150.0; static guint crop_x = 0.0; static guint crop_y = 0.0; static guint crop_width = 0.0; static guint crop_height = 0.0; static const char **file_arguments = NULL; static const GOptionEntry options[] = { { "document", 'd', 0, G_OPTION_ARG_INT, &document, "the XPS document to convert", "DOCUMENT" }, { "first", 'f', 0, G_OPTION_ARG_INT, &first_page, "first page to convert", "PAGE" }, { "last", 'l', 0, G_OPTION_ARG_INT, &last_page, "last page to convert", "PAGE" }, { "odd", 'o', 0, G_OPTION_ARG_NONE, &only_odd, "convert only odd pages", NULL }, { "even", 'e', 0, G_OPTION_ARG_NONE, &only_even, "convert only even pages", NULL }, { "resolution", 'r', 0, G_OPTION_ARG_DOUBLE, &resolution, "resolution in PPI [default: 150]", "RESOLUTION" }, { "rx", '\0', 0, G_OPTION_ARG_DOUBLE, &x_resolution, "X resolution in PPI [default: 150]", "X RESOLUTION" }, { "ry", '\0', 0, G_OPTION_ARG_DOUBLE, &y_resolution, "Y resolution in PPI [default: 150]", "Y RESOLUTION" }, { "crop-x", 'x', 0, G_OPTION_ARG_INT, &crop_x, "X coordinate of the crop area top left corner", "X" }, { "crop-y", 'y', 0, G_OPTION_ARG_INT, &crop_y, "Y coordinate of the crop area top left corner", "Y" }, { "crop-width", 'w', 0, G_OPTION_ARG_INT, &crop_width, "width of crop area in pixels", "WIDTH" }, { "crop-height", 'h', 0, G_OPTION_ARG_INT, &crop_height, "height of crop area in pixels", "HEIGHT" }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arguments, NULL, "FILE [OUTPUT FILE]" }, { NULL } }; static gboolean gxps_converter_real_init_with_args (GXPSConverter *converter, gint *argc, gchar ***argv, GList **option_groups) { GOptionContext *context; GFile *file; GXPSFile *xps; guint n_pages; GList *group; GError *error = NULL; context = g_option_context_new (NULL); g_option_context_set_help_enabled (context, TRUE); g_option_context_add_main_entries (context, options, NULL); for (group = g_list_reverse (*option_groups); group; group = g_list_next (group)) g_option_context_add_group (context, (GOptionGroup *)group->data); if (!g_option_context_parse (context, argc, argv, &error)) { g_printerr ("Error parsing arguments: %s\n", error->message); g_error_free (error); g_option_context_free (context); return FALSE; } if (!file_arguments) { gchar *help_text = g_option_context_get_help (context, TRUE, NULL); g_print ("%s", help_text); g_free (help_text); g_option_context_free (context); return FALSE; } g_option_context_free (context); file = g_file_new_for_commandline_arg (file_arguments[0]); converter->input_filename = g_file_get_path (file); xps = gxps_file_new (file, &error); g_object_unref (file); if (!xps) { g_printerr ("Error creating XPS file: %s\n", error->message); g_error_free (error); return FALSE; } document = CLAMP (document, 1, gxps_file_get_n_documents (xps)); converter->document = gxps_file_get_document (xps, document - 1, &error); g_object_unref (xps); if (!converter->document) { g_printerr ("Error getting document %d: %s\n", document, error->message); g_error_free (error); return FALSE; } n_pages = gxps_document_get_n_pages (converter->document); converter->first_page = MAX (first_page, 1); converter->last_page = last_page < 1 ? n_pages : MIN(last_page, n_pages); converter->only_odd = only_odd; converter->only_even = only_even; if (resolution != 0.0 && (x_resolution == 150.0 || y_resolution == 150.0)) { converter->x_resolution = resolution; converter->y_resolution = resolution; } else { converter->x_resolution = x_resolution; converter->y_resolution = y_resolution; } converter->crop.x = crop_x; converter->crop.y = crop_y; converter->crop.width = crop_width; converter->crop.height = crop_height; return TRUE; } static void gxps_converter_begin_document (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page) { GXPSConverterClass *converter_class; g_return_if_fail (GXPS_IS_CONVERTER (converter)); g_return_if_fail (GXPS_IS_PAGE (first_page)); converter_class = GXPS_CONVERTER_GET_CLASS (converter); if (converter_class->begin_document) converter_class->begin_document (converter, output_filename, first_page); } static cairo_t * gxps_converter_begin_page (GXPSConverter *converter, GXPSPage *page, guint n_page) { g_return_val_if_fail (GXPS_IS_CONVERTER (converter), NULL); g_return_val_if_fail (GXPS_IS_PAGE (page), NULL); return GXPS_CONVERTER_GET_CLASS (converter)->begin_page (converter, page, n_page); } static void gxps_converter_end_page (GXPSConverter *converter) { GXPSConverterClass *converter_class; g_return_if_fail (GXPS_IS_CONVERTER (converter)); converter_class = GXPS_CONVERTER_GET_CLASS (converter); if (converter_class->end_page) converter_class->end_page (converter); } static void gxps_converter_end_document (GXPSConverter *converter) { GXPSConverterClass *converter_class; g_return_if_fail (GXPS_IS_CONVERTER (converter)); converter_class = GXPS_CONVERTER_GET_CLASS (converter); if (converter_class->end_document) converter_class->end_document (converter); } static void gxps_converter_finalize (GObject *object) { GXPSConverter *converter = GXPS_CONVERTER (object); g_clear_object (&converter->document); g_clear_object (&converter->surface); g_clear_pointer (&converter->input_filename, g_free); G_OBJECT_CLASS (gxps_converter_parent_class)->finalize (object); } static void gxps_converter_init (GXPSConverter *converter) { } static void gxps_converter_class_init (GXPSConverterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); klass->init_with_args = gxps_converter_real_init_with_args; object_class->finalize = gxps_converter_finalize; } gboolean gxps_converter_init_with_args (GXPSConverter *converter, gint *argc, gchar ***argv) { GList *option_groups = NULL; gboolean retval; g_return_val_if_fail (GXPS_IS_CONVERTER (converter), FALSE); retval = GXPS_CONVERTER_GET_CLASS (converter)->init_with_args (converter, argc, argv, &option_groups); /* Groups are owned by the option context */ g_list_free (option_groups); return retval; } const gchar * gxps_converter_get_extension (GXPSConverter *converter) { g_return_val_if_fail (GXPS_IS_CONVERTER (converter), NULL); return GXPS_CONVERTER_GET_CLASS (converter)->get_extension (converter); } void gxps_converter_get_crop_size (GXPSConverter *converter, gdouble page_width, gdouble page_height, gdouble *output_width, gdouble *output_height) { guint width, height; g_return_if_fail (GXPS_IS_CONVERTER (converter)); width = converter->crop.width == 0 ? (int)ceil (page_width) : converter->crop.width; height = converter->crop.height == 0 ? (int)ceil (page_height) : converter->crop.height; if (output_width) { *output_width = (converter->crop.x + width > page_width ? (int)ceil (page_width - converter->crop.x) : width); } if (output_height) { *output_height = (converter->crop.y + height > page_height ? (int)ceil (page_height - converter->crop.y) : height); } } void gxps_converter_run (GXPSConverter *converter) { guint i; guint first_page; g_return_if_fail (GXPS_IS_CONVERTER (converter)); first_page = converter->first_page; /* Make sure first_page is always used so that * gxps_converter_begin_document() is called */ if ((converter->only_even && first_page % 2 == 0) || (converter->only_odd && first_page % 2 == 1)) first_page++; for (i = first_page; i <= converter->last_page; i++) { GXPSPage *page; cairo_t *cr; GError *error; if (converter->only_even && i % 2 == 0) continue; if (converter->only_odd && i % 2 == 1) continue; error = NULL; page = gxps_document_get_page (converter->document, i - 1, &error); if (!page) { g_printerr ("Error getting page %d: %s\n", i, error->message); g_error_free (error); continue; } if (i == first_page) { gchar *output_filename = NULL; if (file_arguments[1]) { GFile *file; file = g_file_new_for_commandline_arg (file_arguments[1]); output_filename = g_file_get_path (file); g_object_unref (file); } gxps_converter_begin_document (converter, output_filename, page); g_free (output_filename); } cr = gxps_converter_begin_page (converter, page, i); error = NULL; gxps_page_render (page, cr, &error); if (error) { g_printerr ("Error rendering page %d: %s\n", i, error->message); g_error_free (error); } cairo_destroy (cr); gxps_converter_end_page (converter); g_object_unref (page); } gxps_converter_end_document (converter); } 0707010000005E000081A40000000000000000000000016447D41100000F21000000000000000000000000000000000000002700000000libgxps-0.3.2+5/tools/gxps-converter.h/* GXPSConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_CONVERTER_H__ #define __GXPS_CONVERTER_H__ #include <glib-object.h> #include <libgxps/gxps.h> G_BEGIN_DECLS #define GXPS_TYPE_CONVERTER (gxps_converter_get_type ()) #define GXPS_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_CONVERTER, GXPSConverter)) #define GXPS_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_CONVERTER, GXPSConverterClass)) #define GXPS_IS_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_CONVERTER)) #define GXPS_IS_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_CONVERTER)) #define GXPS_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_CONVERTER, GXPSConverterClass)) typedef struct _GXPSConverter GXPSConverter; typedef struct _GXPSConverterClass GXPSConverterClass; struct _GXPSConverter { GObject parent; GXPSDocument *document; cairo_surface_t *surface; gchar *input_filename; guint first_page; guint last_page; gdouble x_resolution; gdouble y_resolution; cairo_rectangle_int_t crop; guint only_odd : 1; guint only_even : 1; }; struct _GXPSConverterClass { GObjectClass parent_class; gboolean (* init_with_args) (GXPSConverter *converter, gint *argc, gchar ***argv, GList **option_groups); void (* begin_document) (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page); cairo_t *(* begin_page) (GXPSConverter *converter, GXPSPage *page, guint n_page); void (* end_page) (GXPSConverter *converter); void (* end_document) (GXPSConverter *converter); const gchar *(* get_extension) (GXPSConverter *converter); }; GType gxps_converter_get_type (void); gboolean gxps_converter_init_with_args (GXPSConverter *converter, gint *argc, gchar ***argv); const gchar *gxps_converter_get_extension (GXPSConverter *converter); void gxps_converter_get_crop_size (GXPSConverter *converter, gdouble page_width, gdouble page_height, gdouble *output_width, gdouble *output_height); void gxps_converter_run (GXPSConverter *converter); G_END_DECLS #endif /* __GXPS_CONVERTER_H__ */ 0707010000005F000081A40000000000000000000000016447D41100001B63000000000000000000000000000000000000002D00000000libgxps-0.3.2+5/tools/gxps-image-converter.c/* GXPSImageConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-image-converter.h" #include <stdint.h> #include <math.h> #include <string.h> #include <libgxps/gxps.h> G_DEFINE_ABSTRACT_TYPE (GXPSImageConverter, gxps_image_converter, GXPS_TYPE_CONVERTER) static guint get_n_digits (GXPSDocument *document) { guint n_pages = gxps_document_get_n_pages (document); guint retval = 0; while (n_pages >= 10) { n_pages /= 10; retval++; } return retval + 1; } static void gxps_converter_image_converter_begin_document (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page) { GXPSImageConverter *image_converter = GXPS_IMAGE_CONVERTER (converter); image_converter->page_prefix = g_strdup (output_filename ? output_filename : "page"); image_converter->n_digits = get_n_digits (converter->document); } static cairo_t * gxps_converter_image_converter_begin_page (GXPSConverter *converter, GXPSPage *page, guint n_page) { GXPSImageConverter *image_converter = GXPS_IMAGE_CONVERTER (converter); gdouble page_width, page_height; gdouble output_width, output_height; cairo_t *cr; g_return_val_if_fail (converter->surface == NULL, NULL); image_converter->current_page = n_page; gxps_page_get_size (page, &page_width, &page_height); gxps_converter_get_crop_size (converter, page_width * (converter->x_resolution / 96.0), page_height * (converter->y_resolution / 96.0), &output_width, &output_height); converter->surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ceil (output_width), ceil (output_height)); cr = cairo_create (converter->surface); if (image_converter->fill_background) { cairo_save (cr); cairo_set_source_rgb (cr, 1, 1, 1); cairo_paint (cr); cairo_restore (cr); } cairo_translate (cr, -converter->crop.x, -converter->crop.y); cairo_scale (cr, converter->x_resolution / 96.0, converter->y_resolution / 96.0); return cr; } static void gxps_converter_image_converter_end_page (GXPSConverter *converter) { GXPSImageConverter *image_converter = GXPS_IMAGE_CONVERTER (converter); cairo_status_t status; const gchar *extension = gxps_converter_get_extension (converter); gchar *page_filename; FILE *fd; guint width, height; gint stride; guchar *data; gint y; g_return_if_fail (converter->surface != NULL); g_return_if_fail (GXPS_IS_IMAGE_WRITER (image_converter->writer)); width = cairo_image_surface_get_width (converter->surface); height = cairo_image_surface_get_height (converter->surface); stride = cairo_image_surface_get_stride (converter->surface); data = cairo_image_surface_get_data (converter->surface); page_filename = g_strdup_printf ("%s-%0*d.%s", image_converter->page_prefix, image_converter->n_digits, image_converter->current_page, extension); fd = fopen (page_filename, "wb"); if (!fd) { g_printerr ("Error opening output file %s\n", page_filename); g_free (page_filename); cairo_surface_destroy (converter->surface); converter->surface = NULL; return; } if (!gxps_image_writer_init (image_converter->writer, fd, width, height, converter->x_resolution, converter->y_resolution)) { g_printerr ("Error writing %s\n", page_filename); g_free (page_filename); fclose (fd); cairo_surface_destroy (converter->surface); converter->surface = NULL; return; } for (y = 0; y < height; y++) gxps_image_writer_write (image_converter->writer, data + y * stride); gxps_image_writer_finish (image_converter->writer); fclose (fd); g_free (page_filename); cairo_surface_finish (converter->surface); status = cairo_surface_status (converter->surface); if (status) g_printerr ("Cairo error: %s\n", cairo_status_to_string (status)); cairo_surface_destroy (converter->surface); converter->surface = NULL; } static void gxps_image_converter_finalize (GObject *object) { GXPSImageConverter *converter = GXPS_IMAGE_CONVERTER (object); g_clear_pointer (&converter->page_prefix, g_free); g_clear_object (&converter->writer); G_OBJECT_CLASS (gxps_image_converter_parent_class)->finalize (object); } static void gxps_image_converter_init (GXPSImageConverter *converter) { GXPSImageConverter *image_converter = GXPS_IMAGE_CONVERTER (converter); image_converter->fill_background = TRUE; } static void gxps_image_converter_class_init (GXPSImageConverterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GXPSConverterClass *converter_class = GXPS_CONVERTER_CLASS (klass); object_class->finalize = gxps_image_converter_finalize; converter_class->begin_document = gxps_converter_image_converter_begin_document; converter_class->begin_page = gxps_converter_image_converter_begin_page; converter_class->end_page = gxps_converter_image_converter_end_page; } 07070100000060000081A40000000000000000000000016447D4110000089C000000000000000000000000000000000000002D00000000libgxps-0.3.2+5/tools/gxps-image-converter.h/* GXPSImageConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_IMAGE_CONVERTER_H__ #define __GXPS_IMAGE_CONVERTER_H__ #include "gxps-converter.h" #include "gxps-image-writer.h" G_BEGIN_DECLS #define GXPS_TYPE_IMAGE_CONVERTER (gxps_image_converter_get_type ()) #define GXPS_IMAGE_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_IMAGE_CONVERTER, GXPSImageConverter)) #define GXPS_IMAGE_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_IMAGE_CONVERTER, GXPSImageConverterClass)) #define GXPS_IS_IMAGE_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_IMAGE_CONVERTER)) #define GXPS_IS_IMAGE_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_IMAGE_CONVERTER)) #define GXPS_IMAGE_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_IMAGE_CONVERTER, GXPSImageConverterClass)) typedef struct _GXPSImageConverter GXPSImageConverter; typedef struct _GXPSImageConverterClass GXPSImageConverterClass; struct _GXPSImageConverter { GXPSConverter parent; GXPSImageWriter *writer; guint current_page; gchar *page_prefix; guint n_digits; guint fill_background : 1; }; struct _GXPSImageConverterClass { GXPSConverterClass parent_class; }; GType gxps_image_converter_get_type (void); G_END_DECLS #endif /* __GXPS_IMAGE_CONVERTER_H__ */ 07070100000061000081A40000000000000000000000016447D41100000889000000000000000000000000000000000000002A00000000libgxps-0.3.2+5/tools/gxps-image-writer.c/* GXPSImageWriter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-image-writer.h" G_DEFINE_INTERFACE (GXPSImageWriter, gxps_image_writer, G_TYPE_OBJECT) static void gxps_image_writer_default_init (GXPSImageWriterInterface *iface) { } gboolean gxps_image_writer_init (GXPSImageWriter *image_writer, FILE *fd, guint width, guint height, guint x_resolution, guint y_resolution) { g_return_val_if_fail (GXPS_IS_IMAGE_WRITER (image_writer), FALSE); g_return_val_if_fail (fd != NULL, FALSE); return GXPS_IMAGE_WRITER_GET_IFACE (image_writer)->init (image_writer, fd, width, height, x_resolution, y_resolution); } gboolean gxps_image_writer_write (GXPSImageWriter *image_writer, guchar *row) { g_return_val_if_fail (GXPS_IS_IMAGE_WRITER (image_writer), FALSE); return GXPS_IMAGE_WRITER_GET_IFACE (image_writer)->write (image_writer, row); } gboolean gxps_image_writer_finish (GXPSImageWriter *image_writer) { g_return_val_if_fail (GXPS_IS_IMAGE_WRITER (image_writer), FALSE); return GXPS_IMAGE_WRITER_GET_IFACE (image_writer)->finish (image_writer); } 07070100000062000081A40000000000000000000000016447D41100000ABD000000000000000000000000000000000000002A00000000libgxps-0.3.2+5/tools/gxps-image-writer.h/* GXPSImageWriter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_IMAGE_WRITER_H__ #define __GXPS_IMAGE_WRITER_H__ #include <stdio.h> #include <glib-object.h> G_BEGIN_DECLS #define GXPS_TYPE_IMAGE_WRITER (gxps_image_writer_get_type ()) #define GXPS_IMAGE_WRITER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_IMAGE_WRITER, GXPSImageWriter)) #define GXPS_IS_IMAGE_WRITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_IMAGE_WRITER)) #define GXPS_IMAGE_WRITER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), GXPS_TYPE_IMAGE_WRITER, GXPSImageWriterInterface)) typedef struct _GXPSImageWriter GXPSImageWriter; typedef struct _GXPSImageWriterInterface GXPSImageWriterInterface; struct _GXPSImageWriterInterface { GTypeInterface g_iface; gboolean (* init) (GXPSImageWriter *writer, FILE *fd, guint width, guint height, guint x_resolution, guint y_resolution); gboolean (* write) (GXPSImageWriter *writer, guchar *row); gboolean (* finish) (GXPSImageWriter *writer); }; GType gxps_image_writer_get_type (void); gboolean gxps_image_writer_init (GXPSImageWriter *image_writer, FILE *fd, guint width, guint height, guint x_resolution, guint y_resolution); gboolean gxps_image_writer_write (GXPSImageWriter *image_writer, guchar *row); gboolean gxps_image_writer_finish (GXPSImageWriter *image_writer); G_END_DECLS #endif /* __GXPS_IMAGE_WRITER_H__ */ 07070100000063000081A40000000000000000000000016447D411000007C7000000000000000000000000000000000000002C00000000libgxps-0.3.2+5/tools/gxps-jpeg-converter.c/* GXPSJpegConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-jpeg-converter.h" #include "gxps-jpeg-writer.h" #include <libgxps/gxps.h> struct _GXPSJpegConverter { GXPSImageConverter parent; }; struct _GXPSJpegConverterClass { GXPSImageConverterClass parent_class; }; G_DEFINE_TYPE (GXPSJpegConverter, gxps_jpeg_converter, GXPS_TYPE_IMAGE_CONVERTER) static const gchar * gxps_jpeg_converter_get_extension (GXPSConverter *converter) { return "jpg"; } static void gxps_jpeg_converter_end_page (GXPSConverter *converter) { GXPSImageConverter *image_converter = GXPS_IMAGE_CONVERTER (converter); if (!image_converter->writer) image_converter->writer = gxps_jpeg_writer_new (); GXPS_CONVERTER_CLASS (gxps_jpeg_converter_parent_class)->end_page (converter); } static void gxps_jpeg_converter_init (GXPSJpegConverter *converter) { } static void gxps_jpeg_converter_class_init (GXPSJpegConverterClass *klass) { GXPSConverterClass *converter_class = GXPS_CONVERTER_CLASS (klass); converter_class->get_extension = gxps_jpeg_converter_get_extension; converter_class->end_page = gxps_jpeg_converter_end_page; } 07070100000064000081A40000000000000000000000016447D4110000072B000000000000000000000000000000000000002C00000000libgxps-0.3.2+5/tools/gxps-jpeg-converter.h/* GXPSJpegConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_JPEG_CONVERTER_H__ #define __GXPS_JPEG_CONVERTER_H__ #include "gxps-image-converter.h" G_BEGIN_DECLS #define GXPS_TYPE_JPEG_CONVERTER (gxps_jpeg_converter_get_type ()) #define GXPS_JPEG_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_JPEG_CONVERTER, GXPSJpegConverter)) #define GXPS_JPEG_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_JPEG_CONVERTER, GXPSJpegConverterClass)) #define GXPS_IS_JPEG_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_JPEG_CONVERTER)) #define GXPS_IS_JPEG_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_JPEG_CONVERTER)) #define GXPS_JPEG_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_JPEG_CONVERTER, GXPSJpegConverterClass)) typedef struct _GXPSJpegConverter GXPSJpegConverter; typedef struct _GXPSJpegConverterClass GXPSJpegConverterClass; GType gxps_jpeg_converter_get_type (void); G_END_DECLS #endif /* __GXPS_JPEG_CONVERTER_H__ */ 07070100000065000081A40000000000000000000000016447D411000012A7000000000000000000000000000000000000002900000000libgxps-0.3.2+5/tools/gxps-jpeg-writer.c/* GXPSJpegWriter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-jpeg-writer.h" #include <jpeglib.h> #include <setjmp.h> #include <stdint.h> #include <string.h> struct _GXPSJpegWriter { GObject parent; guchar *row_buffer; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr error_mgr; }; struct _GXPSJpegWriterClass { GObjectClass parent_class; }; static void gxps_jpeg_writer_image_writer_iface_init (GXPSImageWriterInterface *iface); G_DEFINE_TYPE_WITH_CODE (GXPSJpegWriter, gxps_jpeg_writer, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GXPS_TYPE_IMAGE_WRITER, gxps_jpeg_writer_image_writer_iface_init)) static void gxps_jpeg_writer_init (GXPSJpegWriter *jpeg_writer) { } static void gxps_jpeg_writer_class_init (GXPSJpegWriterClass *klass) { } GXPSImageWriter * gxps_jpeg_writer_new (void) { return GXPS_IMAGE_WRITER (g_object_new (GXPS_TYPE_JPEG_WRITER, NULL)); } static void jpeg_output_message (j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; /* Create the message */ (*cinfo->err->format_message) (cinfo, buffer); g_printerr ("%s\n", buffer); } static gboolean gxps_jpeg_writer_image_writer_init (GXPSImageWriter *image_writer, FILE *fd, guint width, guint height, guint x_resolution, guint y_resolution) { GXPSJpegWriter *jpeg_writer = GXPS_JPEG_WRITER (image_writer); jpeg_writer->row_buffer = (guchar *) g_malloc (width * 4); jpeg_std_error (&jpeg_writer->error_mgr); jpeg_writer->error_mgr.output_message = jpeg_output_message; jpeg_create_compress (&jpeg_writer->cinfo); jpeg_writer->cinfo.err = &jpeg_writer->error_mgr; jpeg_stdio_dest (&jpeg_writer->cinfo, fd); jpeg_writer->cinfo.image_width = width; jpeg_writer->cinfo.image_height = height; jpeg_writer->cinfo.input_components = 3; /* color components per pixel */ jpeg_writer->cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ jpeg_set_defaults (&jpeg_writer->cinfo); jpeg_writer->cinfo.density_unit = 1; /* dots per inch */ jpeg_writer->cinfo.X_density = x_resolution; jpeg_writer->cinfo.Y_density = y_resolution; jpeg_start_compress (&jpeg_writer->cinfo, TRUE); return TRUE; } static gboolean gxps_jpeg_writer_image_writer_write (GXPSImageWriter *image_writer, guchar *row) { GXPSJpegWriter *jpeg_writer = GXPS_JPEG_WRITER (image_writer); guint image_width = jpeg_writer->cinfo.image_width; uint32_t *pixel = (uint32_t *)row; guchar *rowp; guint i; rowp = jpeg_writer->row_buffer; for (i = 0; i < image_width; i++, pixel++) { *rowp++ = (*pixel & 0xff0000) >> 16; *rowp++ = (*pixel & 0x00ff00) >> 8; *rowp++ = (*pixel & 0x0000ff) >> 0; } jpeg_write_scanlines (&jpeg_writer->cinfo, &jpeg_writer->row_buffer, 1); return TRUE; } static gboolean gxps_jpeg_writer_image_writer_finish (GXPSImageWriter *image_writer) { GXPSJpegWriter *jpeg_writer = GXPS_JPEG_WRITER (image_writer); jpeg_finish_compress (&jpeg_writer->cinfo); g_free (jpeg_writer->row_buffer); jpeg_writer->row_buffer = NULL; return TRUE; } static void gxps_jpeg_writer_image_writer_iface_init (GXPSImageWriterInterface *iface) { iface->init = gxps_jpeg_writer_image_writer_init; iface->write = gxps_jpeg_writer_image_writer_write; iface->finish = gxps_jpeg_writer_image_writer_finish; } 07070100000066000081A40000000000000000000000016447D41100000738000000000000000000000000000000000000002900000000libgxps-0.3.2+5/tools/gxps-jpeg-writer.h/* GXPSJpegWriter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_JPEG_WRITER_H__ #define __GXPS_JPEG_WRITER_H__ #include <glib-object.h> #include "gxps-image-writer.h" G_BEGIN_DECLS #define GXPS_TYPE_JPEG_WRITER (gxps_jpeg_writer_get_type ()) #define GXPS_JPEG_WRITER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_JPEG_WRITER, GXPSJpegWriter)) #define GXPS_JPEG_WRITER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_JPEG_WRITER, GXPSJpegWriterClass)) #define GXPS_IS_JPEG_WRITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_JPEG_WRITER)) #define GXPS_IS_JPEG_WRITER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_JPEG_WRITER)) #define GXPS_JPEG_WRITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_JPEG_WRITER, GXPSJpegWriterClass)) typedef struct _GXPSJpegWriter GXPSJpegWriter; typedef struct _GXPSJpegWriterClass GXPSJpegWriterClass; GType gxps_jpeg_writer_get_type (void); GXPSImageWriter *gxps_jpeg_writer_new (void); G_END_DECLS #endif /* __GXPS_JPEG_WRITER_H__ */ 07070100000067000081A40000000000000000000000016447D41100000BC7000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/tools/gxps-pdf-converter.c/* GXPSPdfConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-pdf-converter.h" #include <libgxps/gxps.h> #include <cairo-pdf.h> #include <string.h> struct _GXPSPdfConverter { GXPSPrintConverter parent; }; struct _GXPSPdfConverterClass { GXPSPrintConverterClass parent_class; }; G_DEFINE_TYPE (GXPSPdfConverter, gxps_pdf_converter, GXPS_TYPE_PRINT_CONVERTER) static const gchar * gxps_pdf_converter_get_extension (GXPSConverter *converter) { return "pdf"; } static void gxps_pdf_converter_begin_document (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); gdouble width, height; GXPS_CONVERTER_CLASS (gxps_pdf_converter_parent_class)->begin_document (converter, output_filename, first_page); _gxps_converter_print_get_output_size (print_converter, first_page, &width, &height); converter->surface = cairo_pdf_surface_create (print_converter->filename, width, height); } static cairo_t * gxps_pdf_converter_begin_page (GXPSConverter *converter, GXPSPage *page, guint n_page) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); gdouble width, height; g_return_val_if_fail (converter->surface != NULL, NULL); _gxps_converter_print_get_output_size (print_converter, page, &width, &height); cairo_pdf_surface_set_size (converter->surface, width, height); return GXPS_CONVERTER_CLASS (gxps_pdf_converter_parent_class)->begin_page (converter, page, n_page); } static void gxps_pdf_converter_init (GXPSPdfConverter *converter) { } static void gxps_pdf_converter_class_init (GXPSPdfConverterClass *klass) { GXPSConverterClass *converter_class = GXPS_CONVERTER_CLASS (klass); converter_class->get_extension = gxps_pdf_converter_get_extension; converter_class->begin_document = gxps_pdf_converter_begin_document; converter_class->begin_page = gxps_pdf_converter_begin_page; } 07070100000068000081A40000000000000000000000016447D41100000713000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/tools/gxps-pdf-converter.h/* GXPSPdfConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PDF_CONVERTER_H__ #define __GXPS_PDF_CONVERTER_H__ #include "gxps-print-converter.h" G_BEGIN_DECLS #define GXPS_TYPE_PDF_CONVERTER (gxps_pdf_converter_get_type ()) #define GXPS_PDF_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_PDF_CONVERTER, GXPSPdfConverter)) #define GXPS_PDF_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_PDF_CONVERTER, GXPSPdfConverterClass)) #define GXPS_IS_PDF_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_PDF_CONVERTER)) #define GXPS_IS_PDF_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_PDF_CONVERTER)) #define GXPS_PDF_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_PDF_CONVERTER, GXPSPdfConverterClass)) typedef struct _GXPSPdfConverter GXPSPdfConverter; typedef struct _GXPSPdfConverterClass GXPSPdfConverterClass; GType gxps_pdf_converter_get_type (void); G_END_DECLS #endif /* __GXPS_PDF_CONVERTER_H__ */ 07070100000069000081A40000000000000000000000016447D411000012B5000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/tools/gxps-png-converter.c/* GXPSPngConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-png-converter.h" #include "gxps-png-writer.h" #include <math.h> #include <libgxps/gxps.h> struct _GXPSPngConverter { GXPSImageConverter parent; guint bg_transparent : 1; }; struct _GXPSPngConverterClass { GXPSImageConverterClass parent_class; }; G_DEFINE_TYPE (GXPSPngConverter, gxps_png_converter, GXPS_TYPE_IMAGE_CONVERTER) static gboolean bg_transparent = FALSE; static const GOptionEntry options[] = { { "transparent-bg", 't', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &bg_transparent, "use a transparent background instead of white", NULL }, { NULL } }; static gboolean gxps_png_converter_init_with_args (GXPSConverter *converter, gint *argc, gchar ***argv, GList **option_groups) { GXPSPngConverter *png_converter = GXPS_PNG_CONVERTER (converter); GOptionContext *context; GOptionGroup *option_group; GError *error = NULL; option_group = g_option_group_new ("png", "PNG Options", "Show PNG Options", NULL, NULL); g_option_group_add_entries (option_group, options); *option_groups = g_list_prepend (*option_groups, option_group); if (GXPS_CONVERTER_CLASS (gxps_png_converter_parent_class)->init_with_args) { if (!GXPS_CONVERTER_CLASS (gxps_png_converter_parent_class)->init_with_args (converter, argc, argv, option_groups)) return FALSE; } context = g_option_context_new (NULL); g_option_context_set_ignore_unknown_options (context, TRUE); g_option_context_set_help_enabled (context, FALSE); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, argc, argv, &error)) { g_printerr ("Error parsing arguments: %s\n", error->message); g_error_free (error); g_option_context_free (context); return FALSE; } g_option_context_free (context); png_converter->bg_transparent = bg_transparent; return TRUE; } static const gchar * gxps_png_converter_get_extension (GXPSConverter *converter) { return "png"; } static void gxps_png_converter_begin_document (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page) { GXPSPngConverter *png_converter = GXPS_PNG_CONVERTER (converter); GXPSImageConverter *image_converter = GXPS_IMAGE_CONVERTER (converter); image_converter->fill_background = !png_converter->bg_transparent; GXPS_CONVERTER_CLASS (gxps_png_converter_parent_class)->begin_document (converter, output_filename, first_page); } static void gxps_png_converter_end_page (GXPSConverter *converter) { GXPSImageConverter *image_converter = GXPS_IMAGE_CONVERTER (converter); GXPSPngConverter *png_converter = GXPS_PNG_CONVERTER (converter); if (!image_converter->writer) { GXPSPngFormat format = png_converter->bg_transparent ? GXPS_PNG_FORMAT_RGBA : GXPS_PNG_FORMAT_RGB; image_converter->writer = gxps_png_writer_new (format); } GXPS_CONVERTER_CLASS (gxps_png_converter_parent_class)->end_page (converter); } static void gxps_png_converter_init (GXPSPngConverter *converter) { } static void gxps_png_converter_class_init (GXPSPngConverterClass *klass) { GXPSConverterClass *converter_class = GXPS_CONVERTER_CLASS (klass); converter_class->init_with_args = gxps_png_converter_init_with_args; converter_class->get_extension = gxps_png_converter_get_extension; converter_class->begin_document = gxps_png_converter_begin_document; converter_class->end_page = gxps_png_converter_end_page; } 0707010000006A000081A40000000000000000000000016447D41100000713000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/tools/gxps-png-converter.h/* GXPSPngConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PNG_CONVERTER_H__ #define __GXPS_PNG_CONVERTER_H__ #include "gxps-image-converter.h" G_BEGIN_DECLS #define GXPS_TYPE_PNG_CONVERTER (gxps_png_converter_get_type ()) #define GXPS_PNG_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_PNG_CONVERTER, GXPSPngConverter)) #define GXPS_PNG_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_PNG_CONVERTER, GXPSPngConverterClass)) #define GXPS_IS_PNG_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_PNG_CONVERTER)) #define GXPS_IS_PNG_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_PNG_CONVERTER)) #define GXPS_PNG_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_PNG_CONVERTER, GXPSPngConverterClass)) typedef struct _GXPSPngConverter GXPSPngConverter; typedef struct _GXPSPngConverterClass GXPSPngConverterClass; GType gxps_png_converter_get_type (void); G_END_DECLS #endif /* __GXPS_PNG_CONVERTER_H__ */ 0707010000006B000081A40000000000000000000000016447D41100001CEC000000000000000000000000000000000000002800000000libgxps-0.3.2+5/tools/gxps-png-writer.c/* GXPSPngWriter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-png-writer.h" #include <png.h> #include <stdint.h> #include <string.h> /* starting with libpng15, png.h no longer #includes zlib.h */ #ifndef Z_BEST_COMPRESSION #define Z_BEST_COMPRESSION 9 #endif struct _GXPSPngWriter { GObject parent; GXPSPngFormat format; png_structp png_ptr; png_infop info_ptr; }; struct _GXPSPngWriterClass { GObjectClass parent_class; }; static void gxps_png_writer_image_writer_iface_init (GXPSImageWriterInterface *iface); G_DEFINE_TYPE_WITH_CODE (GXPSPngWriter, gxps_png_writer, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GXPS_TYPE_IMAGE_WRITER, gxps_png_writer_image_writer_iface_init)) static void gxps_png_writer_init (GXPSPngWriter *png_writer) { } static void gxps_png_writer_class_init (GXPSPngWriterClass *klass) { } GXPSImageWriter * gxps_png_writer_new (GXPSPngFormat format) { GXPSPngWriter *png_writer = GXPS_PNG_WRITER (g_object_new (GXPS_TYPE_PNG_WRITER, NULL)); png_writer->format = format; return GXPS_IMAGE_WRITER (png_writer); } /* Unpremultiplies data and converts native endian ARGB => RGBA bytes */ static void unpremultiply_data (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *b = &data[i]; uint32_t pixel; uint8_t alpha; memcpy (&pixel, b, sizeof (uint32_t)); alpha = (pixel & 0xff000000) >> 24; if (alpha == 0) { b[0] = b[1] = b[2] = b[3] = 0; } else { b[0] = (((pixel & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; b[1] = (((pixel & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; b[2] = (((pixel & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; b[3] = alpha; } } } /* Converts native endian xRGB => RGBx bytes */ static void convert_data_to_bytes (png_structp png, png_row_infop row_info, png_bytep data) { unsigned int i; for (i = 0; i < row_info->rowbytes; i += 4) { uint8_t *b = &data[i]; uint32_t pixel; memcpy (&pixel, b, sizeof (uint32_t)); b[0] = (pixel & 0xff0000) >> 16; b[1] = (pixel & 0x00ff00) >> 8; b[2] = (pixel & 0x0000ff) >> 0; b[3] = 0; } } static gboolean gxps_png_writer_image_writer_init (GXPSImageWriter *image_writer, FILE *fd, guint width, guint height, guint x_resolution, guint y_resolution) { GXPSPngWriter *png_writer = GXPS_PNG_WRITER (image_writer); png_writer->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_writer->png_ptr) { g_printerr ("Error initializing png writer: png_create_write_struct failed\n"); return FALSE; } png_writer->info_ptr = png_create_info_struct (png_writer->png_ptr); if (!png_writer->info_ptr) { g_printerr ("Error initializing png writer: png_create_info_struct failed\n"); return FALSE; } if (setjmp (png_jmpbuf (png_writer->png_ptr))) { g_printerr ("Error initializing png writer: png_jmpbuf failed\n"); return FALSE; } /* write header */ png_init_io (png_writer->png_ptr, fd); if (setjmp (png_jmpbuf (png_writer->png_ptr))) { g_printerr ("Error initializing png writer: error writing PNG header\n"); return FALSE; } png_set_compression_level (png_writer->png_ptr, Z_BEST_COMPRESSION); png_set_IHDR (png_writer->png_ptr, png_writer->info_ptr, width, height, 8, png_writer->format == GXPS_PNG_FORMAT_RGB ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_pHYs (png_writer->png_ptr, png_writer->info_ptr, x_resolution / 0.0254, y_resolution / 0.0254, PNG_RESOLUTION_METER); png_write_info (png_writer->png_ptr, png_writer->info_ptr); if (setjmp (png_jmpbuf (png_writer->png_ptr))) { g_printerr ("Error initializing png writer: error writing png info bytes\n"); return FALSE; } switch (png_writer->format) { case GXPS_PNG_FORMAT_RGB: png_set_write_user_transform_fn (png_writer->png_ptr, convert_data_to_bytes); png_set_filler (png_writer->png_ptr, 0, PNG_FILLER_AFTER); break; case GXPS_PNG_FORMAT_RGBA: png_set_write_user_transform_fn (png_writer->png_ptr, unpremultiply_data); break; } return TRUE; } static gboolean gxps_png_writer_image_writer_write (GXPSImageWriter *image_writer, guchar *row) { GXPSPngWriter *png_writer = GXPS_PNG_WRITER (image_writer); png_write_rows (png_writer->png_ptr, &row, 1); if (setjmp (png_jmpbuf (png_writer->png_ptr))) { g_printerr ("Error writing png: error during png row write\n"); return FALSE; } return TRUE; } static gboolean gxps_png_writer_image_writer_finish (GXPSImageWriter *image_writer) { GXPSPngWriter *png_writer = GXPS_PNG_WRITER (image_writer); png_write_end (png_writer->png_ptr, png_writer->info_ptr); if (setjmp (png_jmpbuf (png_writer->png_ptr))) { g_printerr ("Error finishing png: error during end of write\n"); return FALSE; } png_destroy_write_struct (&png_writer->png_ptr, &png_writer->info_ptr); return TRUE; } static void gxps_png_writer_image_writer_iface_init (GXPSImageWriterInterface *iface) { iface->init = gxps_png_writer_image_writer_init; iface->write = gxps_png_writer_image_writer_write; iface->finish = gxps_png_writer_image_writer_finish; } 0707010000006C000081A40000000000000000000000016447D4110000078A000000000000000000000000000000000000002800000000libgxps-0.3.2+5/tools/gxps-png-writer.h/* GXPSPngWriter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PNG_WRITER_H__ #define __GXPS_PNG_WRITER_H__ #include <glib-object.h> #include "gxps-image-writer.h" G_BEGIN_DECLS #define GXPS_TYPE_PNG_WRITER (gxps_png_writer_get_type ()) #define GXPS_PNG_WRITER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_PNG_WRITER, GXPSPngWriter)) #define GXPS_PNG_WRITER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_PNG_WRITER, GXPSPngWriterClass)) #define GXPS_IS_PNG_WRITER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_PNG_WRITER)) #define GXPS_IS_PNG_WRITER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_PNG_WRITER)) #define GXPS_PNG_WRITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_PNG_WRITER, GXPSPngWriterClass)) typedef enum { GXPS_PNG_FORMAT_RGB, GXPS_PNG_FORMAT_RGBA } GXPSPngFormat; typedef struct _GXPSPngWriter GXPSPngWriter; typedef struct _GXPSPngWriterClass GXPSPngWriterClass; GType gxps_png_writer_get_type (void); GXPSImageWriter *gxps_png_writer_new (GXPSPngFormat format); G_END_DECLS #endif /* __GXPS_PNG_WRITER_H__ */ 0707010000006D000081A40000000000000000000000016447D41100002E9D000000000000000000000000000000000000002D00000000libgxps-0.3.2+5/tools/gxps-print-converter.c/* GXPSPrintConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-print-converter.h" #include <string.h> G_DEFINE_ABSTRACT_TYPE (GXPSPrintConverter, gxps_print_converter, GXPS_TYPE_CONVERTER) static guint paper_width = 0; static guint paper_height = 0; static gboolean expand = FALSE; static gboolean no_shrink = FALSE; static gboolean no_center = FALSE; static const GOptionEntry options[] = { { "paper-width", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &paper_width, "paper width, in points", "WIDTH" }, { "paper-height", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &paper_height, "paper height, in points", "HEIGHT" }, { "expand", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &expand, "expand pages smaller than the paper size", NULL }, { "no-shrink", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &no_shrink, "don't shrink pages larger than the paper size", NULL }, { "no-center", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &no_center, "don't center pages smaller than the paper size", NULL }, { NULL } }; static gboolean gxps_print_converter_init_with_args (GXPSConverter *converter, gint *argc, gchar ***argv, GList **option_groups) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); GOptionContext *context; GOptionGroup *option_group; GError *error = NULL; option_group = g_option_group_new ("printing", "Printing Options", "Show Printing Options", NULL, NULL); g_option_group_add_entries (option_group, options); *option_groups = g_list_prepend (*option_groups, option_group); if (GXPS_CONVERTER_CLASS (gxps_print_converter_parent_class)->init_with_args) { if (!GXPS_CONVERTER_CLASS (gxps_print_converter_parent_class)->init_with_args (converter, argc, argv, option_groups)) return FALSE; } context = g_option_context_new (NULL); g_option_context_set_ignore_unknown_options (context, TRUE); g_option_context_set_help_enabled (context, FALSE); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, argc, argv, &error)) { g_printerr ("Error parsing arguments: %s\n", error->message); g_error_free (error); g_option_context_free (context); return FALSE; } g_option_context_free (context); print_converter->paper_width = paper_width; print_converter->paper_height = paper_height; print_converter->flags = GXPS_PRINT_CONVERTER_SHRINK | GXPS_PRINT_CONVERTER_CENTER; if (expand) print_converter->flags |= GXPS_PRINT_CONVERTER_EXPAND; if (no_shrink) print_converter->flags &= ~GXPS_PRINT_CONVERTER_SHRINK; if (no_center) print_converter->flags &= ~GXPS_PRINT_CONVERTER_CENTER; return TRUE; } static void gxps_converter_print_converter_begin_document (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); gchar *basename; gchar *basename_lower; const gchar *ext; if (output_filename) { print_converter->filename = g_strdup (output_filename); return; } basename = g_path_get_basename (converter->input_filename); basename_lower = g_ascii_strdown (basename, -1); ext = g_strrstr (basename_lower, ".xps"); if (ext) { gchar *name; name = g_strndup (basename, strlen (basename) - strlen (ext)); print_converter->filename = g_strdup_printf ("%s.%s", name, gxps_converter_get_extension (converter)); g_free (name); } else { print_converter->filename = g_strdup_printf ("%s.%s", basename, gxps_converter_get_extension (converter)); } g_free (basename_lower); g_free (basename); } static void gxps_converter_print_get_fit_to_page_transform (GXPSPrintConverter *print_converter, gdouble page_width, gdouble page_height, gdouble paper_width, gdouble paper_height, cairo_matrix_t *matrix) { gdouble x_scale, y_scale; gdouble scale; x_scale = paper_width / page_width; y_scale = paper_height / page_height; scale = (x_scale < y_scale) ? x_scale : y_scale; cairo_matrix_init_identity (matrix); if (scale > 1.0) { /* Page is smaller than paper */ if (print_converter->flags & GXPS_PRINT_CONVERTER_EXPAND) { cairo_matrix_scale (matrix, scale, scale); } else if (print_converter->flags & GXPS_PRINT_CONVERTER_CENTER) { cairo_matrix_translate (matrix, (paper_width - page_width) / 2, (paper_height - page_height) / 2); } else { if (!print_converter->upside_down_coords) { /* Move to PostScript origin */ cairo_matrix_translate (matrix, 0, (paper_height - page_height)); } } } else if (scale < 1.0) { /* Page is larger than paper */ if (print_converter->flags & GXPS_PRINT_CONVERTER_SHRINK) cairo_matrix_scale (matrix, scale, scale); } } static cairo_t * gxps_converter_print_converter_begin_page (GXPSConverter *converter, GXPSPage *page, guint n_page) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); gdouble page_width, page_height; gdouble cropped_width, cropped_height; gdouble output_width, output_height; cairo_matrix_t matrix; cairo_t *cr; g_return_val_if_fail (converter->surface != NULL, NULL); cairo_surface_set_fallback_resolution (converter->surface, converter->x_resolution, converter->y_resolution); cr = cairo_create (converter->surface); cairo_translate (cr, -converter->crop.x, -converter->crop.y); gxps_page_get_size (page, &page_width, &page_height); gxps_converter_get_crop_size (converter, page_width, page_height, &cropped_width, &cropped_height); _gxps_converter_print_get_output_size (print_converter, page, &output_width, &output_height); gxps_converter_print_get_fit_to_page_transform (print_converter, cropped_width, cropped_height, output_width, output_height, &matrix); cairo_transform (cr, &matrix); cairo_rectangle (cr, converter->crop.x, converter->crop.y, cropped_width, cropped_height); cairo_clip (cr); return cr; } static void gxps_converter_print_converter_end_page (GXPSConverter *converter) { g_return_if_fail (converter->surface != NULL); cairo_surface_show_page (converter->surface); } static void gxps_converter_print_converter_end_document (GXPSConverter *converter) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); if (converter->surface) { cairo_status_t status; cairo_surface_finish (converter->surface); status = cairo_surface_status (converter->surface); if (status) g_printerr ("Cairo error: %s\n", cairo_status_to_string (status)); cairo_surface_destroy (converter->surface); converter->surface = NULL; } g_free (print_converter->filename); print_converter->filename = NULL; } static void gxps_print_converter_finalize (GObject *object) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (object); if (print_converter->filename) { g_free (print_converter->filename); print_converter->filename = NULL; } G_OBJECT_CLASS (gxps_print_converter_parent_class)->finalize (object); } static void gxps_print_converter_init (GXPSPrintConverter *converter) { } static void gxps_print_converter_class_init (GXPSPrintConverterClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GXPSConverterClass *converter_class = GXPS_CONVERTER_CLASS (klass); object_class->finalize = gxps_print_converter_finalize; converter_class->init_with_args = gxps_print_converter_init_with_args; converter_class->begin_document = gxps_converter_print_converter_begin_document; converter_class->begin_page = gxps_converter_print_converter_begin_page; converter_class->end_page = gxps_converter_print_converter_end_page; converter_class->end_document = gxps_converter_print_converter_end_document; } void _gxps_converter_print_get_output_size (GXPSPrintConverter *converter, GXPSPage *page, gdouble *output_width, gdouble *output_height) { gdouble page_width, page_height; gxps_page_get_size (page, &page_width, &page_height); /* The page width is in points, Windows expects a dpi of 96 while * cairo will handle the dpi in 72. We need to make the conversion * ourselves so we have the right output size */ if (output_width) { *output_width = converter->paper_width == 0 ? page_width * 72.0 / 96.0 : converter->paper_width; } if (output_height) { *output_height = converter->paper_height == 0 ? page_height * 72.0 / 96.0 : converter->paper_height; } } 0707010000006E000081A40000000000000000000000016447D41100000A8D000000000000000000000000000000000000002D00000000libgxps-0.3.2+5/tools/gxps-print-converter.h/* GXPSPrintConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PRINT_CONVERTER_H__ #define __GXPS_PRINT_CONVERTER_H__ #include "gxps-converter.h" G_BEGIN_DECLS #define GXPS_TYPE_PRINT_CONVERTER (gxps_print_converter_get_type ()) #define GXPS_PRINT_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_PRINT_CONVERTER, GXPSPrintConverter)) #define GXPS_PRINT_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_PRINT_CONVERTER, GXPSPrintConverterClass)) #define GXPS_IS_PRINT_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_PRINT_CONVERTER)) #define GXPS_IS_PRINT_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_PRINT_CONVERTER)) #define GXPS_PRINT_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_PRINT_CONVERTER, GXPSPrintConverterClass)) typedef enum { GXPS_PRINT_CONVERTER_EXPAND = 1 << 0, GXPS_PRINT_CONVERTER_SHRINK = 1 << 1, GXPS_PRINT_CONVERTER_CENTER = 1 << 2 } GXPSPrintConverterFlags; typedef struct _GXPSPrintConverter GXPSPrintConverter; typedef struct _GXPSPrintConverterClass GXPSPrintConverterClass; struct _GXPSPrintConverter { GXPSConverter parent; gchar *filename; guint paper_width; guint paper_height; GXPSPrintConverterFlags flags; guint upside_down_coords : 1; }; struct _GXPSPrintConverterClass { GXPSConverterClass parent_class; }; GType gxps_print_converter_get_type (void); void _gxps_converter_print_get_output_size (GXPSPrintConverter *converter, GXPSPage *page, gdouble *output_width, gdouble *output_height); G_END_DECLS #endif /* __GXPS_PRINT_CONVERTER_H__ */ 0707010000006F000081A40000000000000000000000016447D41100001FD8000000000000000000000000000000000000002A00000000libgxps-0.3.2+5/tools/gxps-ps-converter.c/* GXPSPdfConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-ps-converter.h" #include <libgxps/gxps.h> #include <cairo-ps.h> #include <string.h> struct _GXPSPsConverter { GXPSPrintConverter parent; cairo_ps_level_t level; guint eps : 1; guint duplex : 1; }; struct _GXPSPsConverterClass { GXPSPrintConverterClass parent_class; }; G_DEFINE_TYPE (GXPSPsConverter, gxps_ps_converter, GXPS_TYPE_PRINT_CONVERTER) typedef struct _GXPSPaperSize { const gchar *name; guint width; guint height; } GXPSPaperSize; static const GXPSPaperSize paper_sizes[] = { { "match", 0, 0 }, { "A0", 2384, 3371 }, { "A1", 1685, 2384 }, { "A2", 1190, 1684 }, { "A3", 842, 1190 }, { "A4", 595, 842 }, { "A5", 420, 595 }, { "B4", 729, 1032 }, { "B5", 516, 729 }, { "Letter", 612, 792 }, { "Tabloid", 792, 1224 }, { "Ledger", 1224, 792 }, { "Legal", 612, 1008 }, { "Statement", 396, 612 }, { "Executive", 540, 720 }, { "Folio", 612, 936 }, { "Quarto", 610, 780 }, { "10x14", 720, 1008 }, }; static gboolean level2 = FALSE; static gboolean level3 = FALSE; static gboolean eps = FALSE; static gboolean duplex = FALSE; static gchar *paper = NULL; static const GOptionEntry options[] = { { "level2", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &level2, "generate Level 2 PostScript", NULL }, { "level3", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &level3, "generate Level 3 PostScript", NULL }, { "eps", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &eps, "generate Encapsulated PostScript", NULL }, { "paper", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &paper, "paper size (match, letter, legal, A4, A3, ...)", "PAPER" }, { "duplex", '\0', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE, &duplex, "enable duplex printing", NULL }, { NULL } }; static gboolean gxps_ps_converter_init_with_args (GXPSConverter *converter, gint *argc, gchar ***argv, GList **option_groups) { GXPSPsConverter *ps_converter = GXPS_PS_CONVERTER (converter); GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); GOptionContext *context; GOptionGroup *option_group; guint i; GError *error = NULL; option_group = g_option_group_new ("postscrit", "PostScript Options", "Show PostScript Options", NULL, NULL); g_option_group_add_entries (option_group, options); *option_groups = g_list_prepend (*option_groups, option_group); if (GXPS_CONVERTER_CLASS (gxps_ps_converter_parent_class)->init_with_args) { if (!GXPS_CONVERTER_CLASS (gxps_ps_converter_parent_class)->init_with_args (converter, argc, argv, option_groups)) return FALSE; } context = g_option_context_new (NULL); g_option_context_set_ignore_unknown_options (context, TRUE); g_option_context_set_help_enabled (context, FALSE); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, argc, argv, &error)) { g_printerr ("Error parsing arguments: %s\n", error->message); g_error_free (error); g_option_context_free (context); return FALSE; } g_option_context_free (context); ps_converter->level = (level2 && !level3) ? CAIRO_PS_LEVEL_2 : CAIRO_PS_LEVEL_3; ps_converter->eps = eps; ps_converter->duplex = duplex; for (i = 0; paper && i < G_N_ELEMENTS (paper_sizes); i++) { if (g_ascii_strcasecmp (paper, paper_sizes[i].name) == 0) { print_converter->paper_width = paper_sizes[i].width; print_converter->paper_height = paper_sizes[i].height; break; } } g_print ("DBG: paper size: %s %d, %d\n", paper, print_converter->paper_width, print_converter->paper_height); return TRUE; } static const gchar * gxps_ps_converter_get_extension (GXPSConverter *converter) { return "ps"; } static void gxps_ps_converter_begin_document (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page) { GXPSPsConverter *ps_converter = GXPS_PS_CONVERTER (converter); GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); gdouble width, height; GXPS_CONVERTER_CLASS (gxps_ps_converter_parent_class)->begin_document (converter, output_filename, first_page); _gxps_converter_print_get_output_size (print_converter, first_page, &width, &height); converter->surface = cairo_ps_surface_create (print_converter->filename, width, height); if (ps_converter->level == CAIRO_PS_LEVEL_2) cairo_ps_surface_restrict_to_level (converter->surface, ps_converter->level); if (ps_converter->eps) cairo_ps_surface_set_eps (converter->surface, 1); if (ps_converter->duplex) { cairo_ps_surface_dsc_comment (converter->surface, "%%Requirements: duplex"); cairo_ps_surface_dsc_begin_setup (converter->surface); cairo_ps_surface_dsc_comment (converter->surface, "%%IncludeFeature: *Duplex DuplexNoTumble"); } cairo_ps_surface_dsc_begin_page_setup (converter->surface); } static cairo_t * gxps_ps_converter_begin_page (GXPSConverter *converter, GXPSPage *page, guint n_page) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); gdouble width, height; g_return_val_if_fail (converter->surface != NULL, NULL); _gxps_converter_print_get_output_size (print_converter, page, &width, &height); if (width > height) { cairo_ps_surface_dsc_comment (converter->surface, "%%PageOrientation: Landscape"); cairo_ps_surface_set_size (converter->surface, height, width); } else { cairo_ps_surface_dsc_comment (converter->surface, "%%PageOrientation: Portrait"); cairo_ps_surface_set_size (converter->surface, width, height); } return GXPS_CONVERTER_CLASS (gxps_ps_converter_parent_class)->begin_page (converter, page, n_page); } static void gxps_ps_converter_init (GXPSPsConverter *converter) { } static void gxps_ps_converter_class_init (GXPSPsConverterClass *klass) { GXPSConverterClass *converter_class = GXPS_CONVERTER_CLASS (klass); converter_class->init_with_args = gxps_ps_converter_init_with_args; converter_class->get_extension = gxps_ps_converter_get_extension; converter_class->begin_document = gxps_ps_converter_begin_document; converter_class->begin_page = gxps_ps_converter_begin_page; } 07070100000070000081A40000000000000000000000016447D411000006FB000000000000000000000000000000000000002A00000000libgxps-0.3.2+5/tools/gxps-ps-converter.h/* GXPSPsConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_PS_CONVERTER_H__ #define __GXPS_PS_CONVERTER_H__ #include "gxps-print-converter.h" G_BEGIN_DECLS #define GXPS_TYPE_PS_CONVERTER (gxps_ps_converter_get_type ()) #define GXPS_PS_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_PS_CONVERTER, GXPSPsConverter)) #define GXPS_PS_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_PS_CONVERTER, GXPSPsConverterClass)) #define GXPS_IS_PS_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_PS_CONVERTER)) #define GXPS_IS_PS_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_PS_CONVERTER)) #define GXPS_PS_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_PS_CONVERTER, GXPSPsConverterClass)) typedef struct _GXPSPsConverter GXPSPsConverter; typedef struct _GXPSPsConverterClass GXPSPsConverterClass; GType gxps_ps_converter_get_type (void); G_END_DECLS #endif /* __GXPS_PS_CONVERTER_H__ */ 07070100000071000081A40000000000000000000000016447D411000009DD000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/tools/gxps-svg-converter.c/* GXPSSvgConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include <config.h> #include "gxps-svg-converter.h" #include <libgxps/gxps.h> #include <cairo-svg.h> #include <string.h> struct _GXPSSvgConverter { GXPSPrintConverter parent; }; struct _GXPSSvgConverterClass { GXPSPrintConverterClass parent_class; }; G_DEFINE_TYPE (GXPSSvgConverter, gxps_svg_converter, GXPS_TYPE_PRINT_CONVERTER) static const gchar * gxps_svg_converter_get_extension (GXPSConverter *converter) { return "svg"; } static void gxps_svg_converter_begin_document (GXPSConverter *converter, const gchar *output_filename, GXPSPage *first_page) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); gdouble width, height; GXPS_CONVERTER_CLASS (gxps_svg_converter_parent_class)->begin_document (converter, output_filename, first_page); _gxps_converter_print_get_output_size (print_converter, first_page, &width, &height); converter->surface = cairo_svg_surface_create (print_converter->filename, width, height); cairo_svg_surface_restrict_to_version (converter->surface, CAIRO_SVG_VERSION_1_2); } static void gxps_svg_converter_init (GXPSSvgConverter *converter) { GXPSPrintConverter *print_converter = GXPS_PRINT_CONVERTER (converter); print_converter->upside_down_coords = TRUE; } static void gxps_svg_converter_class_init (GXPSSvgConverterClass *klass) { GXPSConverterClass *converter_class = GXPS_CONVERTER_CLASS (klass); converter_class->get_extension = gxps_svg_converter_get_extension; converter_class->begin_document = gxps_svg_converter_begin_document; } 07070100000072000081A40000000000000000000000016447D41100000713000000000000000000000000000000000000002B00000000libgxps-0.3.2+5/tools/gxps-svg-converter.h/* GXPSSvgConverter * * Copyright (C) 2011 Carlos Garcia Campos <carlosgc@gnome.org> * * This 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. * * This 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 this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __GXPS_SVG_CONVERTER_H__ #define __GXPS_SVG_CONVERTER_H__ #include "gxps-print-converter.h" G_BEGIN_DECLS #define GXPS_TYPE_SVG_CONVERTER (gxps_svg_converter_get_type ()) #define GXPS_SVG_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_CAST (obj, GXPS_TYPE_SVG_CONVERTER, GXPSSvgConverter)) #define GXPS_SVG_CONVERTER_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST (cls, GXPS_TYPE_SVG_CONVERTER, GXPSSvgConverterClass)) #define GXPS_IS_SVG_CONVERTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE (obj, GXPS_TYPE_SVG_CONVERTER)) #define GXPS_IS_SVG_CONVERTER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE (obj, GXPS_TYPE_SVG_CONVERTER)) #define GXPS_SVG_CONVERTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GXPS_TYPE_SVG_CONVERTER, GXPSSvgConverterClass)) typedef struct _GXPSSvgConverter GXPSSvgConverter; typedef struct _GXPSSvgConverterClass GXPSSvgConverterClass; GType gxps_svg_converter_get_type (void); G_END_DECLS #endif /* __GXPS_SVG_CONVERTER_H__ */ 07070100000073000081A40000000000000000000000016447D41100000B94000000000000000000000000000000000000002200000000libgxps-0.3.2+5/tools/meson.buildtools_sources = [ 'gxps-converter.c', 'gxps-converter.h', 'gxps-image-converter.c', 'gxps-image-converter.h', 'gxps-image-writer.c', 'gxps-image-writer.h', 'gxps-print-converter.c', 'gxps-print-converter.h', ] gxps_tools_deps = [ glib_dep, gobject_dep, gio_dep, cairo_dep, cairo_pdf_dep, cairo_ps_dep, cairo_svg_dep, archive_dep, freetype_dep, png_dep, lcms2_dep, jpeg_dep, tiff_dep, libm_dep, gxps_dep ] gxps_tools = static_library('gxpstools', include_directories: core_inc, sources: tools_sources, install: false, dependencies: gxps_tools_deps) gxps_tools_dep = declare_dependency(link_with: gxps_tools, dependencies: gxps_tools_deps) if png_found xpstopng_sources = [ 'gxps-converter-main.c', 'gxps-png-converter.c', 'gxps-png-converter.h', 'gxps-png-writer.c', 'gxps-png-writer.h', ] executable('xpstopng', xpstopng_sources, dependencies: gxps_tools_dep, install: true, c_args: [ '-DCONVERTER_TYPE=GXPS_TYPE_PNG_CONVERTER', '-DCONVERTER_HEADER=gxps-png-converter.h', ]) endif if jpeg_dep.found() xpstojpeg_sources = [ 'gxps-converter-main.c', 'gxps-jpeg-converter.c', 'gxps-jpeg-converter.h', 'gxps-jpeg-writer.c', 'gxps-jpeg-writer.h', ] executable('xpstojpeg', xpstojpeg_sources, dependencies: gxps_tools_dep, install: true, c_args: [ '-DCONVERTER_TYPE=GXPS_TYPE_JPEG_CONVERTER', '-DCONVERTER_HEADER=gxps-jpeg-converter.h', ]) endif if cairo_pdf_dep.found() xpstopdf_sources = [ 'gxps-converter-main.c', 'gxps-pdf-converter.c', 'gxps-pdf-converter.h', ] executable('xpstopdf', xpstopdf_sources, dependencies: gxps_tools_dep, install: true, c_args: [ '-DCONVERTER_TYPE=GXPS_TYPE_PDF_CONVERTER', '-DCONVERTER_HEADER=gxps-pdf-converter.h', ]) endif if cairo_ps_dep.found() xpstops_sources = [ 'gxps-converter-main.c', 'gxps-ps-converter.c', 'gxps-ps-converter.h', ] executable('xpstops', xpstops_sources, dependencies: gxps_tools_dep, install: true, c_args: [ '-DCONVERTER_TYPE=GXPS_TYPE_PS_CONVERTER', '-DCONVERTER_HEADER=gxps-ps-converter.h', ]) endif if cairo_svg_dep.found() xpstosvg_sources = [ 'gxps-converter-main.c', 'gxps-svg-converter.c', 'gxps-svg-converter.h', ] executable('xpstosvg', xpstosvg_sources, dependencies: gxps_tools_dep, install: true, c_args: [ '-DCONVERTER_TYPE=GXPS_TYPE_SVG_CONVERTER', '-DCONVERTER_HEADER=gxps-svg-converter.h', ]) endif 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1303 blocks
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor