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
openSUSE Build Service is sponsored by