pax_global_header00006660000000000000000000000064152602676200014520gustar00rootroot0000000000000052 comment=63b841e3465bf50802197cf9f989ebbe896cd573 dov-paps-f6dc573/000077500000000000000000000000001526026762000136745ustar00rootroot00000000000000dov-paps-f6dc573/.gitignore000066400000000000000000000011141526026762000156610ustar00rootroot00000000000000/*.bak /*.lo /*.o /*.orig /*.rej /*.tab.c *.ps /*~ /.*.sw[nop] /.deps /.dirstamp /.gitignore /.libs /GPATH /GRTAGS /GSYMS /GTAGS /ID /Makefile /Makefile.in /TAGS /_libs /aclocal.m4 /autom4te.cache /config /config.cache /config.h /config.h.in /config.log /config.lt /config.status /config.status.lineno /configure /configure.lineno /intltool-extract.in /intltool-merge.in /intltool-update.in /libtool /m4/libtool.m4 /m4/lt*.m4 /po/*.gmo /po/*.mo /po/.intltool-merge-cache /po/Makefile /po/Makefile.in /po/Makefile.in.in /po/POTFILES /po/paps.pot /po/stamp-it /so_locations /stamp-h1 /tags dov-paps-f6dc573/AUTHORS000066400000000000000000000000461526026762000147440ustar00rootroot00000000000000Dov Grobgeld dov-paps-f6dc573/COPYING.LIB000066400000000000000000000614471526026762000153500ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 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 library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] 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 Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the 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 a program 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. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. 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, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library 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 compile 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) 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. c) 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. d) 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 source code 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 to 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 Library 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. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library 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. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! dov-paps-f6dc573/ChangeLog000066400000000000000000000155561526026762000154620ustar00rootroot000000000000002022-12-17 gettextize * m4/gettext.m4: Upgrade to gettext-0.21. * m4/host-cpu-c-abi.m4: New file, from gettext-0.21. * m4/iconv.m4: Upgrade to gettext-0.21. * m4/intlmacosx.m4: Upgrade to gettext-0.21. * m4/lib-ld.m4: Upgrade to gettext-0.21. * m4/lib-link.m4: Upgrade to gettext-0.21. * m4/lib-prefix.m4: Upgrade to gettext-0.21. * m4/nls.m4: Upgrade to gettext-0.21. * m4/po.m4: Upgrade to gettext-0.21. * m4/progtest.m4: Upgrade to gettext-0.21. 2015-08-16 Dov Grobgeld * paps.c : Ported to cairo and removed libpaps as it is not needed anymore. 2007-04-13 Dov Grobgeld * paps.c: Erased --justify option since it is not implemented in pango yet. 2007-01-19 Dov Grobgeld * configure.in: Bumped version to 0.6.8 * src/libpaps.c: Removed warnings. * src/paps.c: Added --help info about units. * src/paps.1: Added info about units. 2006-12-07 Dov Grobgeld * paps.1: Fixed bug in documentation of the --markup option. 2006-11-28 Dov Grobgeld * libpaps.h: Minor doxygen warning fixes. * doc/Doxyfile, doc/Makefile.am: Removed for new doxygen support. * aminclude.am, acinclude.m4 : Added for doxygen support * configure.in: Made doxygen documentation build at autoconf time (package preparer) and not at user configuration time. 2006-11-03 Dov Grobgeld * configure.in: Bumped version to 0.6.7 * src/paps.1: Described the new options. * src/paps.c: (split_text_into_paragraphs) Reverted to old split_text_into_paragraphs as I couldn't understand Akira's code and I don't think it is relevant anymore with the new cpi/lpi interpretation. (split_text_into_paragraphs) Added functionality for using markup. - Added option --markup for printing pango markup'd text. - Changed the interpretation of LPI so that it only controls the line spacing and not the size of the characters. - Added option --stretch-chars to stretch characters to fill the --lpi value. - Removed global variables lpi and cpi and moved them into page_layout_t . - Changed creation of options to allow passing the page_layout_t as a parameter. - Removed all references to wchar as it is not used anymore. (It shouldn't have been used in the first place as there are plenty of utf8 and ucs4 manipulating function in glib.) * src/libpaps.c: Moved the stretching of characters to c-level only. Erased the scale_x and scale_y parameters from most functions. 2006-09-07 Akira TAGOH * src/paps.c (_paps_arg_lpi_cb): new function. (_paps_arg_cpi_cb): new function. (main): added --lpi and --cpi option. (#1472021) (split_text_into_paragraphs): need to adjust the layout to avoid the cutting off after scaling with LPI/CPI parameters. (split_paragraphs_into_lines): figure out the scaling. (output_pages): likewise. (draw_line_to_page, draw_page_header_line_to_page): modified to give the scaling according to LPI/CPI to libpaps. * src/libpaps.c (paps_set_scale): new function. (paps_new, paps_layout_to_postscript_strdup) (paps_layout_line_to_postscript_strdup, add_postscript_prologue) (add_line_to_postscript, draw_contour, draw_bezier_outline): modified to output the PostScript file with any scaling. * src/paps.c: added the missing header file. (main): added --font option instead of --font-scale and --family, which works like specifying PangoFontDescription. (#1512382) (get_language): new function. (main): give a proper language to PangoContext. (#1512384) (split_text_into_paragraphs): support the wordwrap from Pango. (#1512385) * src/paps.1: removed --font-scale and --family description and added --font instead. * src/paps.c (split_text_into_paragraphs) (split_paragraphs_into_lines, output_pages): formfeed support that is originally written by Jan Willem Stumpel (main, read_file): added --encoding option support. (#1494769) 2006-05-25 Akira TAGOH * src/paps.1: fixed a typo. (#1491827) * src/paps.c (main): changed an option name --font_scale to --font-scale according to other options prefers a hyphen other than underscore. * src/paps.c (split_text_into_paragraphs): fixed a possible segfault issue. 2006-05-03 Dov Grobgeld * Release new version 0.6.6 * src/paps.c: Added #include to fix compilation errors. 2006-04-26 Dov Grobgeld * src/libpaps.c: Got rid of dead old crud. * src/libpaps.c: Defined PANGO_GLYPH_EMPTY that is not exported from pango. * src/libpaps.c: Ignore characters that FT_Load_Glyph returns an error code about. 2006-04-17 Dov Grobgeld * Release new version 0.6.5 * src/paps.1: Fixed errors in option documentations. * configure.in: Bumped version to 0.6.5 * src/paps.c: Made header separation independant on font scale. * src/paps.c: Made header font description independent on font scale. * doc/mainpage.h: Fixed typo. * src/paps.c: Output GOption parser message when parsing fails. * src/paps.1: Fixed man page so that it corresponds to goption parser. * src/paps.c: Fixed header position so that it takes margins into account. * src/Makefile.am: Added pango.1 to EXTRA_DIST to make it part of tar file. 2006-04-09 Dov Grobgeld * src/paps.c: Added tagoh's header and footer patch. * src/paps.c: Added tagoh's goption patch. 2006-04-08 Dov Grobgeld * Release new version 0.6.4 * src/paps.1: Added man page contributed by Lior Kaplan. 2006-03-28 Dov Grobgeld * src/paps.c: Applied tagoh's patch for setting the page size in PostScript. * src/paps.c: Fixed linebreaks in --help printout. 2005-12-22 Dov Grobgeld * Released new bugfix version 0.6.3 * src/libpaps.c: Fixed overflow of static hash character size. 2005-12-20 Dov Grobgeld * Released new version 0.6.2 * src/libpaps.c: Skip bitmap glyphs in order not to crash in decomposition. * src/paps.c: Created external for margin settings. * src/libpaps.c: Fixed default font to monospace per Jan Willem Stumpel suggestion. * src/paps.c: Fixed printing of row that lacks a trailing newline. 2005-11-24 Dov Grobgeld * src/libpaps.c: Fixed potential sprintf buffer overrun bug. * src/paps.c: Added options for letter and legal paper sizes * src/paps.c: Added postscript commands for setting page size * src/paps.c: Added DSC comments for orientation 2005-11-19 Dov Grobgeld * Released new version 0.6.1 * src/libpaps.c: Fixed scaling bug in output postscript. The characters were to small by a factor of 72/64 due to different coordinate systems in PostScript and freetype2. * src/libpaps.c: Various cleanup of the library. dov-paps-f6dc573/INSTALL.md000066400000000000000000000004031526026762000153210ustar00rootroot00000000000000## Installation instructions paps is preferably compiled with meson. ## Dependencies - Meson 0.55.0 or later - cairo - libpaper - C++17 - https://github.com/fmtlib/fmt - used declared as a subproject ## Compilation ``` meson build cd build && ninja ``` dov-paps-f6dc573/Makefile.am000066400000000000000000000025511526026762000157330ustar00rootroot00000000000000NULL = ACLOCAL_AMFLAGS=-I m4 SUBDIRS = src po EXTRA_DIST = autogen.sh intltool-extract.in intltool-merge.in intltool-update.in scripts meson.build misc README.md INSTALL.md MAINTAINERCLEANFILES = \ $(srcdir)/aclocal.m4 \ $(builddir)/config \ $(NULL) -include $(top_srcdir)/git.mk dist_bin_SCRIPTS = scripts/src-to-paps # Install the pango_markup.outlang package papssharedir = $(prefix)/share/$(PACKAGE) papsshare_DATA = misc/pango_markup.outlang check-gettext: @if test x$(USE_NLS) != "xyes" ; then echo "Missing gettext. Rerun configure and check for" \ "'checking whether to use NLS... yes'!" ; exit 1 ; fi update-po: check-gettext @find $(srcdir)/src/ -name "*.cc" -print | sort > $(srcdir)/po/POTFILES.in.2 ; \ if diff $(srcdir)/po/POTFILES.in $(srcdir)/po/POTFILES.in.2 >/dev/null 2>&1 ; then \ rm -f $(srcdir)/po/POTFILES.in.2 ; \ else \ mv $(srcdir)/po/POTFILES.in.2 $(srcdir)/po/POTFILES.in ; \ fi cd po && $(MAKE) $(AM_MAKEFLAGS) update-po force-update-gmo: check-gettext touch po/*.po cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo force-update-gmo-%: check-gettext @language=`echo $@ | sed s/force-update-gmo-//` ; \ if test ! -f po/$$language.po ; then echo "file po/$$language.po does not exist" ; exit 1 ; fi ; \ touch po/$$language.po ; \ cd po && $(MAKE) $(AM_MAKEFLAGS) update-gmo .PHONY: check-gettext update-po update-gmo force-update-gmo dov-paps-f6dc573/NEWS000066400000000000000000000036711526026762000144020ustar00rootroot00000000000000Overview of changes between 0.8.0 and 0.8.1 ============================================= * Added --booklet and --signature for two-column booklet imposition (works with --rtl, header and footer are drawn per column). * Added --geometry COLSxROWS; the font is scaled down to fit the row height. * Added --inkscape-multipage for SVG output with pages side by side, and --page-gap. * Re-enabled --stretch-chars; the stretch is applied per line. * Fixed markup spanning line breaks and --cpi clipping of markup. * Date formatting in headers/footers is now localized. * Fixed memory leaks reported by AddressSanitizer. * Use libpaper for paper sizes, added libtool, and an option to disable NLS. * Build fixes: glib 2.82, fmt 11.2 (fmt::localtime), BSD with NLS, unused-result warnings, and localized dates with fmt 10. * src-to-paps is now Python 3 and uses a portable shebang. * Documented landscape handling and PDF/SVG support. Overview of changes between 0.7.9 and 0.8.0 ============================================= * Fixed minor bugs from 0.7.9 * Fixed bug when using markup with char mapping and visible wrap marker. * Added src-to-paps preprocessor scripts for conversion of source code to paps through GNU Highlight. Overview of changes between 0.7.0 and 0.7.9 ============================================= * Switch to C++17 * The header and footers are now modifiable by a syntax borrowed from python and the fmt library. E.g. the following command line shows what is possible: paps --header-left="{now:%Y-%m-%d %H:%M}" --header-center="{path}" --header-right="Page {page_idx:02d}/{num_pages:02d}" --header -o hello.pdf paps.cc * Add separation line option Overview of changes between 0.7.0 and 0.7.1 ============================================= * Added initial meson compilation support. * Minor accumulated bug fixes since 0.7.0 Older news ============================================= * 2015-08-16 Sun Ported paps to cairo dov-paps-f6dc573/README000066400000000000000000000000171526026762000145520ustar00rootroot00000000000000See: README.md dov-paps-f6dc573/README.md000066400000000000000000000034741526026762000151630ustar00rootroot00000000000000# About paps is a command line program for converting Unicode text encoded in UTF-8 to postscript and pdf by using pango. # Installation instructions See file:INSTALL.md # Example Here is the output from processing the file examples/small-hello.utf8: ![Example image](misc/small-hello.png) # History paps was written around 2005 to enable printing of plain text unicode UTF-8 files. It is named by the use of the excellent Pango library (from which it took its first two characters) and outputed postscript (ps, the last two characters). When the initial version was written, there was no simple way of translating the output of pango to postscript outlines, and therefore initially output bitmap fonts in the postscript output. But I wanted to have resolution independent postscript, and therefore in a subsequent version Ι wrote my own font encoding library in PostScript, by making use of the fact that PostScript is a full programming language. This worked well when sending the result to a PostScript printer, but the use of this library had some serious disadvantages: - When converting to pdf you got huge files (two orders of magnitude larger!) since in contrast to PostScript, PDF is not a programming language, and therefore each glyph would be encoded as a PDF path. - You couldn't select and copy and paste from the resulting postscript file, since there was no underlying text, only graphic paths. In the early 2010's the library cairo and its accompanying pangocairo library finally created an easy way of converting pango output to postscript in an idiomatic way. But it took me several years until I finally rewrote paps to make use of them. But I finally did so and released the resulting version on github in 2015 as version 0.7.0 . # Usage Run `paps --help` for getting usage help. Run `man paps` for the man page dov-paps-f6dc573/acinclude.m4000066400000000000000000000264121526026762000160720ustar00rootroot00000000000000# This file is part of Autoconf. -*- Autoconf -*- # Copyright (C) 2004 Oren Ben-Kiki # This file is distributed under the same terms as the Autoconf macro files. # Generate automatic documentation using Doxygen. Works in concert with the # aminclude.m4 file and a compatible doxygen configuration file. Defines the # following public macros: # # DX_???_FEATURE(ON|OFF) - control the default setting fo a Doxygen feature. # Supported features are 'DOXYGEN' itself, 'DOT' for generating graphics, # 'HTML' for plain HTML, 'CHM' for compressed HTML help (for MS users), 'CHI' # for generating a seperate .chi file by the .chm file, and 'MAN', 'RTF', # 'XML', 'PDF' and 'PS' for the appropriate output formats. The environment # variable DOXYGEN_PAPER_SIZE may be specified to override the default 'a4wide' # paper size. # # By default, HTML, PDF and PS documentation is generated as this seems to be # the most popular and portable combination. MAN pages created by Doxygen are # usually problematic, though by picking an appropriate subset and doing some # massaging they might be better than nothing. CHM and RTF are specific for MS # (note that you can't generate both HTML and CHM at the same time). The XML is # rather useless unless you apply specialized post-processing to it. # # The macro mainly controls the default state of the feature. The use can # override the default by specifying --enable or --disable. The macros ensure # that contradictory flags are not given (e.g., --enable-doxygen-html and # --enable-doxygen-chm, --enable-doxygen-anything with --disable-doxygen, etc.) # Finally, each feature will be automatically disabled (with a warning) if the # required programs are missing. # # Once all the feature defaults have been specified, call DX_INIT_DOXYGEN with # the following parameters: a one-word name for the project for use as a # filename base etc., an optional configuration file name (the default is # 'Doxyfile', the same as Doxygen's default), and an optional output directory # name (the default is 'doxygen-doc'). ## ----------## ## Defaults. ## ## ----------## DX_ENV="" AC_DEFUN([DX_FEATURE_doc], ON) AC_DEFUN([DX_FEATURE_dot], ON) AC_DEFUN([DX_FEATURE_man], OFF) AC_DEFUN([DX_FEATURE_html], ON) AC_DEFUN([DX_FEATURE_chm], OFF) AC_DEFUN([DX_FEATURE_chi], OFF) AC_DEFUN([DX_FEATURE_rtf], OFF) AC_DEFUN([DX_FEATURE_xml], OFF) AC_DEFUN([DX_FEATURE_pdf], ON) AC_DEFUN([DX_FEATURE_ps], ON) ## --------------- ## ## Private macros. ## ## --------------- ## # DX_ENV_APPEND(VARIABLE, VALUE) # ------------------------------ # Append VARIABLE="VALUE" to DX_ENV for invoking doxygen. AC_DEFUN([DX_ENV_APPEND], [AC_SUBST([DX_ENV], ["$DX_ENV $1='$2'"])]) # DX_DIRNAME_EXPR # --------------- # Expand into a shell expression prints the directory part of a path. AC_DEFUN([DX_DIRNAME_EXPR], [[expr ".$1" : '\(\.\)[^/]*$' \| "x$1" : 'x\(.*\)/[^/]*$']]) # DX_IF_FEATURE(FEATURE, IF-ON, IF-OFF) # ------------------------------------- # Expands according to the M4 (static) status of the feature. AC_DEFUN([DX_IF_FEATURE], [ifelse(DX_FEATURE_$1, ON, [$2], [$3])]) # DX_REQUIRE_PROG(VARIABLE, PROGRAM) # ---------------------------------- # Require the specified program to be found for the DX_CURRENT_FEATURE to work. AC_DEFUN([DX_REQUIRE_PROG], [ AC_PATH_TOOL([$1], [$2]) if test "$DX_FLAG_DX_CURRENT_FEATURE$$1" = 1; then AC_MSG_WARN([$2 not found - will not DX_CURRENT_DESCRIPTION]) AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0) fi ]) # DX_TEST_FEATURE(FEATURE) # ------------------------ # Expand to a shell expression testing whether the feature is active. AC_DEFUN([DX_TEST_FEATURE], [test "$DX_FLAG_$1" = 1]) # DX_CHECK_DEPEND(REQUIRED_FEATURE, REQUIRED_STATE) # ------------------------------------------------- # Verify that a required features has the right state before trying to turn on # the DX_CURRENT_FEATURE. AC_DEFUN([DX_CHECK_DEPEND], [ test "$DX_FLAG_$1" = "$2" \ || AC_MSG_ERROR([doxygen-DX_CURRENT_FEATURE ifelse([$2], 1, requires, contradicts) doxygen-DX_CURRENT_FEATURE]) ]) # DX_CLEAR_DEPEND(FEATURE, REQUIRED_FEATURE, REQUIRED_STATE) # ---------------------------------------------------------- # Turn off the DX_CURRENT_FEATURE if the required feature is off. AC_DEFUN([DX_CLEAR_DEPEND], [ test "$DX_FLAG_$1" = "$2" || AC_SUBST([DX_FLAG_DX_CURRENT_FEATURE], 0) ]) # DX_FEATURE_ARG(FEATURE, DESCRIPTION, # CHECK_DEPEND, CLEAR_DEPEND, # REQUIRE, DO-IF-ON, DO-IF-OFF) # -------------------------------------------- # Parse the command-line option controlling a feature. CHECK_DEPEND is called # if the user explicitly turns the feature on (and invokes DX_CHECK_DEPEND), # otherwise CLEAR_DEPEND is called to turn off the default state if a required # feature is disabled (using DX_CLEAR_DEPEND). REQUIRE performs additional # requirement tests (DX_REQUIRE_PROG). Finally, an automake flag is set and # DO-IF-ON or DO-IF-OFF are called according to the final state of the feature. AC_DEFUN([DX_ARG_ABLE], [ AC_DEFUN([DX_CURRENT_FEATURE], [$1]) AC_DEFUN([DX_CURRENT_DESCRIPTION], [$2]) AC_ARG_ENABLE(doxygen-$1, [AS_HELP_STRING(DX_IF_FEATURE([$1], [--disable-doxygen-$1], [--enable-doxygen-$1]), DX_IF_FEATURE([$1], [don't $2], [$2]))], [ case "$enableval" in #( y|Y|yes|Yes|YES) AC_SUBST([DX_FLAG_$1], 1) $3 ;; #( n|N|no|No|NO) AC_SUBST([DX_FLAG_$1], 0) ;; #( *) AC_MSG_ERROR([invalid value '$enableval' given to doxygen-$1]) ;; esac ], [ AC_SUBST([DX_FLAG_$1], [DX_IF_FEATURE([$1], 1, 0)]) $4 ]) if DX_TEST_FEATURE([$1]); then $5 : fi if DX_TEST_FEATURE([$1]); then AM_CONDITIONAL(DX_COND_$1, :) $6 : else AM_CONDITIONAL(DX_COND_$1, false) $7 : fi ]) ## -------------- ## ## Public macros. ## ## -------------- ## # DX_XXX_FEATURE(DEFAULT_STATE) # ----------------------------- AC_DEFUN([DX_DOXYGEN_FEATURE], [AC_DEFUN([DX_FEATURE_doc], [$1])]) AC_DEFUN([DX_MAN_FEATURE], [AC_DEFUN([DX_FEATURE_man], [$1])]) AC_DEFUN([DX_HTML_FEATURE], [AC_DEFUN([DX_FEATURE_html], [$1])]) AC_DEFUN([DX_CHM_FEATURE], [AC_DEFUN([DX_FEATURE_chm], [$1])]) AC_DEFUN([DX_CHI_FEATURE], [AC_DEFUN([DX_FEATURE_chi], [$1])]) AC_DEFUN([DX_RTF_FEATURE], [AC_DEFUN([DX_FEATURE_rtf], [$1])]) AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) AC_DEFUN([DX_XML_FEATURE], [AC_DEFUN([DX_FEATURE_xml], [$1])]) AC_DEFUN([DX_PDF_FEATURE], [AC_DEFUN([DX_FEATURE_pdf], [$1])]) AC_DEFUN([DX_PS_FEATURE], [AC_DEFUN([DX_FEATURE_ps], [$1])]) # DX_INIT_DOXYGEN(PROJECT, [CONFIG-FILE], [OUTPUT-DOC-DIR]) # --------------------------------------------------------- # PROJECT also serves as the base name for the documentation files. # The default CONFIG-FILE is "Doxyfile" and OUTPUT-DOC-DIR is "doxygen-doc". AC_DEFUN([DX_INIT_DOXYGEN], [ # Files: AC_SUBST([DX_PROJECT], [$1]) AC_SUBST([DX_CONFIG], [ifelse([$2], [], Doxyfile, [$2])]) AC_SUBST([DX_DOCDIR], [ifelse([$3], [], doxygen-doc, [$3])]) # Environment variables used inside doxygen.cfg: DX_ENV_APPEND(SRCDIR, $srcdir) DX_ENV_APPEND(PROJECT, $DX_PROJECT) DX_ENV_APPEND(DOCDIR, $DX_DOCDIR) DX_ENV_APPEND(VERSION, $PACKAGE_VERSION) # Doxygen itself: DX_ARG_ABLE(doc, [generate any doxygen documentation], [], [], [DX_REQUIRE_PROG([DX_DOXYGEN], doxygen) DX_REQUIRE_PROG([DX_PERL], perl)], [DX_ENV_APPEND(PERL_PATH, $DX_PERL)]) # Dot for graphics: DX_ARG_ABLE(dot, [generate graphics for doxygen documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_DOT], dot)], [DX_ENV_APPEND(HAVE_DOT, YES) DX_ENV_APPEND(DOT_PATH, [`DX_DIRNAME_EXPR($DX_DOT)`])], [DX_ENV_APPEND(HAVE_DOT, NO)]) # Man pages generation: DX_ARG_ABLE(man, [generate doxygen manual pages], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_MAN, YES)], [DX_ENV_APPEND(GENERATE_MAN, NO)]) # RTF file generation: DX_ARG_ABLE(rtf, [generate doxygen RTF documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_RTF, YES)], [DX_ENV_APPEND(GENERATE_RTF, NO)]) # XML file generation: DX_ARG_ABLE(xml, [generate doxygen XML documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [], [DX_ENV_APPEND(GENERATE_XML, YES)], [DX_ENV_APPEND(GENERATE_XML, NO)]) # (Compressed) HTML help generation: DX_ARG_ABLE(chm, [generate doxygen compressed HTML help documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_HHC], hhc)], [DX_ENV_APPEND(HHC_PATH, $DX_HHC) DX_ENV_APPEND(GENERATE_HTML, YES) DX_ENV_APPEND(GENERATE_HTMLHELP, YES)], [DX_ENV_APPEND(GENERATE_HTMLHELP, NO)]) # Seperate CHI file generation. DX_ARG_ABLE(chi, [generate doxygen seperate compressed HTML help index file], [DX_CHECK_DEPEND(chm, 1)], [DX_CLEAR_DEPEND(chm, 1)], [], [DX_ENV_APPEND(GENERATE_CHI, YES)], [DX_ENV_APPEND(GENERATE_CHI, NO)]) # Plain HTML pages generation: DX_ARG_ABLE(html, [generate doxygen plain HTML documentation], [DX_CHECK_DEPEND(doc, 1) DX_CHECK_DEPEND(chm, 0)], [DX_CLEAR_DEPEND(doc, 1) DX_CLEAR_DEPEND(chm, 0)], [], [DX_ENV_APPEND(GENERATE_HTML, YES)], [DX_TEST_FEATURE(chm) || DX_ENV_APPEND(GENERATE_HTML, NO)]) # PostScript file generation: DX_ARG_ABLE(ps, [generate doxygen PostScript documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_LATEX], latex) DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) DX_REQUIRE_PROG([DX_DVIPS], dvips) DX_REQUIRE_PROG([DX_EGREP], egrep)]) # PDF file generation: DX_ARG_ABLE(pdf, [generate doxygen PDF documentation], [DX_CHECK_DEPEND(doc, 1)], [DX_CLEAR_DEPEND(doc, 1)], [DX_REQUIRE_PROG([DX_PDFLATEX], pdflatex) DX_REQUIRE_PROG([DX_MAKEINDEX], makeindex) DX_REQUIRE_PROG([DX_EGREP], egrep)]) # LaTeX generation for PS and/or PDF: if DX_TEST_FEATURE(ps) || DX_TEST_FEATURE(pdf); then AM_CONDITIONAL(DX_COND_latex, :) DX_ENV_APPEND(GENERATE_LATEX, YES) else AM_CONDITIONAL(DX_COND_latex, false) DX_ENV_APPEND(GENERATE_LATEX, NO) fi # Paper size for PS and/or PDF: AC_ARG_VAR(DOXYGEN_PAPER_SIZE, [a4wide (default), a4, letter, legal or executive]) case "$DOXYGEN_PAPER_SIZE" in #( "") AC_SUBST(DOXYGEN_PAPER_SIZE, "") ;; #( a4wide|a4|letter|legal|executive) DX_ENV_APPEND(PAPER_SIZE, $DOXYGEN_PAPER_SIZE) ;; #( *) AC_MSG_ERROR([unknown DOXYGEN_PAPER_SIZE='$DOXYGEN_PAPER_SIZE']) ;; esac #For debugging: #echo DX_FLAG_doc=$DX_FLAG_doc #echo DX_FLAG_dot=$DX_FLAG_dot #echo DX_FLAG_man=$DX_FLAG_man #echo DX_FLAG_html=$DX_FLAG_html #echo DX_FLAG_chm=$DX_FLAG_chm #echo DX_FLAG_chi=$DX_FLAG_chi #echo DX_FLAG_rtf=$DX_FLAG_rtf #echo DX_FLAG_xml=$DX_FLAG_xml #echo DX_FLAG_pdf=$DX_FLAG_pdf #echo DX_FLAG_ps=$DX_FLAG_ps #echo DX_ENV=$DX_ENV ]) dov-paps-f6dc573/autogen.sh000077500000000000000000000002551526026762000156770ustar00rootroot00000000000000#!/bin/sh mkdir -p config m4 echo "no" | glib-gettextize --force --copy intltoolize --copy --force --automake autoreconf --force --install -I config -I m4 ./configure $* dov-paps-f6dc573/configure.ac000066400000000000000000000020431526026762000161610ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configuration script AC_INIT(paps, 0.8.1, dov.grobgeld@gmail.com) AC_CONFIG_AUX_DIR(config) AC_CONFIG_MACRO_DIRS([m4]) AC_CONFIG_SRCDIR(src/paps.cc) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE AC_PROG_CXX AC_PROG_CC AX_COMPILER_FLAGS WARN_CFLAGS="$WARN_CFLAGS -Wno-format-y2k" dnl Requires to declare wcwidth() AC_GNU_SOURCE PKG_PROG_PKG_CONFIG PKG_CHECK_MODULES([PANGO], [pangocairo pangoft2]) PKG_CHECK_MODULES(FMT, fmt >= 6.0) AC_SUBST(FMT_CFLAGS) AC_SUBST(FMT_LIBS) AC_PROG_INTLTOOL([0.23]) LT_INIT ad_FUNC_SYSTEMPAPERNAME AS_IF([test "$ac_cv_lib_paper_systempapername" = "no" -o "$ac_cv_header_paper_h" = "no"], [AC_MSG_ERROR([paps needs libpaper to work])]) GETTEXT_PACKAGE=paps AC_SUBST(GETTEXT_PACKAGE) AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"], [Package name for gettext.]) ALL_LINGUAS="" AM_GNU_GETTEXT_VERSION([0.19.7]) AM_GNU_GETTEXT([external]) datadir='${prefix}/${DATADIRNAME}' AC_SUBST(datadir) AC_CONFIG_FILES([Makefile src/Makefile po/Makefile.in]) AC_OUTPUT() dov-paps-f6dc573/examples/000077500000000000000000000000001526026762000155125ustar00rootroot00000000000000dov-paps-f6dc573/examples/small-hello.utf8000066400000000000000000000005461526026762000205400ustar00rootroot00000000000000paps by Dov Grobgeld (דב גרובגלד) Printing through Παν語 (Pango) Arabic السلام عليكم Bengali (বাঙà§�লা) ষাগতোম Greek (Ελληνικά) Γειά σας Hebrew ש×�ָלוֹ×� Japanese (日本語) ã�“ã‚“ã�«ã�¡ã�¯, コï¾�ニï¾�ハ Chinese (中文,普通è¯�,汉语) 你好 Vietnamese (Tiếng Việt) Xin Chào dov-paps-f6dc573/git.mk000066400000000000000000000155701526026762000150200ustar00rootroot00000000000000# git.mk # # Copyright 2009, Red Hat, Inc. # Copyright 2010,2011 Behdad Esfahbod # Written by Behdad Esfahbod # # Copying and distribution of this file, with or without modification, # is permitted in any medium without royalty provided the copyright # notice and this notice are preserved. # # The latest version of this file can be downloaded from: # https://raw.github.com/behdad/git.mk/master/git.mk # Bugs, etc, should be reported upstream at: # https://github.com/behdad/git.mk # # To use in your project, import this file in your git repo's toplevel, # then do "make -f git.mk". This modifies all Makefile.am files in # your project to -include git.mk. Remember to add that line to new # Makefile.am files you create in your project, or just rerun the # "make -f git.mk". # # This enables automatic .gitignore generation. If you need to ignore # more files, add them to the GITIGNOREFILES variable in your Makefile.am. # But think twice before doing that. If a file has to be in .gitignore, # chances are very high that it's a generated file and should be in one # of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. # # The only case that you need to manually add a file to GITIGNOREFILES is # when remove files in one of mostlyclean-local, clean-local, distclean-local, # or maintainer-clean-local make targets. # # Note that for files like editor backup, etc, there are better places to # ignore them. See "man gitignore". # # If "make maintainer-clean" removes the files but they are not recognized # by this script (that is, if "git status" shows untracked files still), send # me the output of "git status" as well as your Makefile.am and Makefile for # the directories involved and I'll diagnose. # # For a list of toplevel files that should be in MAINTAINERCLEANFILES, see # Makefile.am.sample in the git.mk git repo. # # Don't EXTRA_DIST this file. It is supposed to only live in git clones, # not tarballs. It serves no useful purpose in tarballs and clutters the # build dir. # # This file knows how to handle autoconf, automake, libtool, gtk-doc, # gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu. # # This makefile provides the following targets: # # - all: "make all" will build all gitignore files. # - gitignore: makes all gitignore files in the current dir and subdirs. # - .gitignore: make gitignore file for the current dir. # - gitignore-recurse: makes all gitignore files in the subdirs. # # KNOWN ISSUES: # # - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the # submodule doesn't find us. If you have configure.{in,ac} files in # subdirs, add a proxy git.mk file in those dirs that simply does: # "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. # And add those files to git. See vte/gnome-pty-helper/git.mk for # example. # git-all: git-mk-install git-mk-install: @echo Installing git makefile @any_failed=; \ find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ if grep 'include .*/git.mk' $$x >/dev/null; then \ echo $$x already includes git.mk; \ else \ failed=; \ echo "Updating $$x"; \ { cat $$x; \ echo ''; \ echo '-include $$(top_srcdir)/git.mk'; \ } > $$x.tmp || failed=1; \ if test x$$failed = x; then \ mv $$x.tmp $$x || failed=1; \ fi; \ if test x$$failed = x; then : else \ echo Failed updating $$x; >&2 \ any_failed=1; \ fi; \ fi; done; test -z "$$any_failed" .PHONY: git-all git-mk-install ### .gitignore generation $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk $(AM_V_GEN) \ { \ if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ for x in \ $(DOC_MODULE)-decl-list.txt \ $(DOC_MODULE)-decl.txt \ tmpl/$(DOC_MODULE)-unused.sgml \ "tmpl/*.bak" \ xml html \ ; do echo /$$x; done; \ fi; \ if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ for lc in $(DOC_LINGUAS); do \ for x in \ $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ $(DOC_PAGES) \ $(DOC_INCLUDES) \ ; do echo /$$lc/$$x; done; \ done; \ for x in \ $(_DOC_OMF_ALL) \ $(_DOC_DSK_ALL) \ $(_DOC_HTML_ALL) \ $(_DOC_MOFILES) \ $(DOC_H_FILE) \ "*/.xml2po.mo" \ "*/*.omf.out" \ ; do echo /$$x; done; \ fi; \ if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ for lc in $(HELP_LINGUAS); do \ for x in \ $(HELP_FILES) \ "$$lc.stamp" \ "$$lc.mo" \ ; do echo /$$lc/$$x; done; \ done; \ fi; \ if test "x$(gsettings_SCHEMAS)" = x; then :; else \ for x in \ $(gsettings_SCHEMAS:.xml=.valid) \ $(gsettings__enum_file) \ ; do echo /$$x; done; \ fi; \ if test -f $(srcdir)/po/Makefile.in.in; then \ for x in \ po/Makefile.in.in \ po/Makefile.in \ po/Makefile \ po/POTFILES \ po/stamp-it \ po/.intltool-merge-cache \ "po/*.gmo" \ "po/*.mo" \ po/$(GETTEXT_PACKAGE).pot \ intltool-extract.in \ intltool-merge.in \ intltool-update.in \ ; do echo /$$x; done; \ fi; \ if test -f $(srcdir)/configure; then \ for x in \ autom4te.cache \ configure \ config.h \ stamp-h1 \ libtool \ config.lt \ ; do echo /$$x; done; \ fi; \ if test "x$(DEJATOOL)" = x; then :; else \ for x in \ $(DEJATOOL) \ ; do echo /$$x.sum; echo /$$x.log; done; \ echo /site.exp; \ fi; \ for x in \ .gitignore \ $(GITIGNOREFILES) \ $(CLEANFILES) \ $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ so_locations \ .libs _libs \ $(MOSTLYCLEANFILES) \ "*.$(OBJEXT)" \ "*.lo" \ $(DISTCLEANFILES) \ $(am__CONFIG_DISTCLEAN_FILES) \ $(CONFIG_CLEAN_FILES) \ TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ "*.tab.c" \ $(MAINTAINERCLEANFILES) \ $(BUILT_SOURCES) \ $(DEPDIR) \ Makefile \ Makefile.in \ "*.orig" \ "*.rej" \ "*.bak" \ "*~" \ ".*.sw[nop]" \ ".dirstamp" \ ; do echo /$$x; done; \ } | \ sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ sed 's@/[.]/@/@g' | \ LC_ALL=C sort | uniq > $@.tmp && \ mv $@.tmp $@; all: $(srcdir)/.gitignore gitignore-recurse-maybe gitignore: $(srcdir)/.gitignore gitignore-recurse gitignore-recurse-maybe: @for subdir in $(DIST_SUBDIRS); do \ case " $(SUBDIRS) " in \ *" $$subdir "*) :;; \ *) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse-maybe || echo "Skipping $$subdir");; \ esac; \ done gitignore-recurse: @for subdir in $(DIST_SUBDIRS); do \ test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir"); \ done maintainer-clean: gitignore-clean gitignore-clean: -rm -f $(srcdir)/.gitignore .PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe dov-paps-f6dc573/m4/000077500000000000000000000000001526026762000142145ustar00rootroot00000000000000dov-paps-f6dc573/m4/libpaper.m4000066400000000000000000000011041526026762000162500ustar00rootroot00000000000000dnl ## --------------------------------------------------------- ## dnl ## Check if libpaper is available ## dnl ## demaille@inf.enst.fr ## dnl ## --------------------------------------------------------- ## dnl dnl # serial 1 AC_DEFUN([ad_FUNC_SYSTEMPAPERNAME], [ AC_CHECK_LIB(paper, systempapername, dnl Action if found [ AC_DEFINE([HAVE_SYSTEMPAPERNAME], 1, [Define if you have the systempapername function]) LIBS="$LIBS -lpaper" AC_CHECK_HEADERS(paper.h) ]) ]) dov-paps-f6dc573/m4/pkg.m4000066400000000000000000000171671526026762000152530ustar00rootroot00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 1 (pkg-config-0.24) # # Copyright © 2004 Scott James Remnant . # # 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. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) # only at the first occurence in configure.ac, so if the first place # it's called might be skipped (such as if it is within an "if", you # have to call PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])# PKG_CHECK_MODULES # PKG_INSTALLDIR(DIRECTORY) # ------------------------- # Substitutes the variable pkgconfigdir as the location where a module # should install pkg-config .pc files. By default the directory is # $libdir/pkgconfig, but the default can be changed by passing # DIRECTORY. The user can override through the --with-pkgconfigdir # parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ]) dnl PKG_INSTALLDIR # PKG_NOARCH_INSTALLDIR(DIRECTORY) # ------------------------- # Substitutes the variable noarch_pkgconfigdir as the location where a # module should install arch-independent pkg-config .pc files. By # default the directory is $datadir/pkgconfig, but the default can be # changed by passing DIRECTORY. The user can override through the # --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ]) dnl PKG_NOARCH_INSTALLDIR # PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, # [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ------------------------------------------- # Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])# PKG_CHECK_VAR dov-paps-f6dc573/meson.build000066400000000000000000000043521526026762000160420ustar00rootroot00000000000000project('paps', 'c', 'cpp', version: '0.8.1', default_options : ['cpp_std=c++17'], meson_version : '>= 0.55') fmt_dep = dependency('fmt') pkg = import('pkgconfig') pango_dep = dependency('pangoft2') cairo_dep = dependency('pangocairo') glib_dep = dependency('glib-2.0') gobject_dep = dependency('gobject-2.0') # C compiler. This is the cross compiler if we're cross-compiling cc = meson.get_compiler('c') libpaper = cc.find_library('paper') cdata = configuration_data() # Checks for library functions foreach f : ['memmove', 'memset', 'strdup'] cdata.set('HAVE_' + f.to_upper(), cc.has_function(f)) endforeach # Checks for header files # Some HAVE_FOO defines need to be defined to either 1 or 0, others need to # be defined or undefined. The code base is a bit inconsistent there. foreach h : ['stdlib.h', 'string.h', 'memory.h'] cdata.set10('HAVE_' + h.underscorify().to_upper(), cc.has_header(h)) endforeach foreach h : ['strings.h', 'sys/times.h'] cdata.set('HAVE_' + h.underscorify().to_upper(), cc.has_header(h)) endforeach # Not entirely correct, but sufficient for us. Should move away from this # ancient define and just include individual headers based on individual defs if cc.has_header('strings.h') # define to 1 or leave undefined otherwise, don't simplify cdata.set('STDC_HEADERS', 1) endif # This is available pretty much everywhere cdata.set('HAVE_STRINGIZE', 1) # i18n support i18n = import('i18n') # Not really sure what this is used for, but paps doesn't compile without it. cdata.set_quoted('DATADIR','.') buildtype = get_option('buildtype') if buildtype == 'debug' or buildtype == 'debugoptimized' cdata.set('DEBUG', 1) endif cdata.set_quoted('GETTEXT_PACKAGE', meson.project_name()) cdata.set_quoted('PACKAGE_STRING', 'paps-' + meson.project_version()) incs = include_directories('.', 'src') subdir('src') nls = get_option('nls') if nls cdata.set('ENABLE_NLS', 1) subdir('po') endif # write config.h config_h = configure_file(output: 'config.h', configuration: cdata) # Install the src-to-paps script with its pango_markup.outlang # dependancy. install_data(['misc/pango_markup.outlang'], install_dir : 'share/paps') install_data(['scripts/src-to-paps'], install_dir : 'bin') dov-paps-f6dc573/meson_options.txt000066400000000000000000000002051526026762000173260ustar00rootroot00000000000000option('nls', type : 'boolean', value : 'true', description : 'Enable native language support (translations)') dov-paps-f6dc573/misc/000077500000000000000000000000001526026762000146275ustar00rootroot00000000000000dov-paps-f6dc573/misc/pango_markup.outlang000066400000000000000000000010551526026762000207060ustar00rootroot00000000000000extension "pango" bold "$text" italics "$text" underline "$text" color "$text" colormap "green" "#33CC00" "red" "#FF0000" "darkred" "#990000" "blue" "#0000FF" "brown" "#9A1900" "pink" "#CC33CC" "yellow" "#FFCC00" "cyan" "#66FFFF" "purple" "#993399" "orange" "#FF6600" "brightorange" "#FF9900" "brightgreen" "#33FF33" "darkgreen" "#009900" "black" "#000000" "teal" "#008080" "gray" "#808080" "darkblue" "#000080" "white" "#FFFFFF" default "#000000" end translations "&" "&" "<" "<" ">" ">" end dov-paps-f6dc573/misc/small-hello.png000066400000000000000000000506461526026762000175610ustar00rootroot00000000000000‰PNG  IHDR˜ªÕSÞbKGDÿ‡�Ì¿ pHYs  šœtIMEÕ 3œ±ä› IDATxÚì½KrëJv÷ûÓ©]çøVTTA#Øà68 7"[n’ @äŽ@äz=Bž¡¾#„ÓqÏ¡¬ïÞû…ïç:'oÉd_z쇴~ŠØ["ñH<òŸ+Wf®u¡5‚ 'ñéy»‰ÊÂ{áâe‚¡Eá¡�JÉVR.ª]];€ C>¤…qQûí¢naèŠ`hço Aøˆ‚qQûî¢na‚ð±ùßi0ráþöl§§ ï“ÿ±ÖÅO\TþÕ\”‚¡Ïò“ ‚ð~ùo.€Ÿø‰ ~²?Úü+† •.IiOü�Ÿø‰OüÄø?ðŸJ÷¦X‚ ü¿V.þÀ'~ã|r, ±0Apø¿ùɈÅ'>ñG~¶Rq�æ“–!RA,3rñG>ñ çw~6£%Æé)‚°ŒOü‘?ò3çØŒ—”nP Ažøƒ‘‹àïhk[ü�ßø‰O"‚ lù_ü�?ò3¿ðÚHÅøÄßùÃÆù)¢!B‰â?ó3ÿ¿ÆŸñwþhFL·§ þ>ñ3¿ )ç]üÌ/ü�ßø-·A¨ Æù;¿?ñG~áÿð?ü�ß�dˆ`‚àð¿ùÙx/>ñ³‘‹¿ózcaH‡D„ ÿ‡ß�?ðG+¥Xüba‚PåÿCóŸŒ\”Þ‹�³¦D€ßø�ߌeñ¿›™žÚüüÒ)a+®XèZü=±0ApøÝZ¥çBW&^ˆ`‚à ­eQµ/D0Ah ]±*ª  ANFC AD0AxStã'÷§† ba‚ ‚!‚† "‚ ˆ`‚ ‚!‚ ‚!‚† "‚ ˆ`‚ ‚!üH´‘ËmÞ“`(Šwv“ ԛܑü%™2’·^x/‚1£Ë%ó3÷ZÕ~&¯ØŽ*&´MëKºL¸Š›ïê‰ûÜÑ%“š(‚ñB:Ïܳl7;„Œ¹ã‰>�³m•ÝÊ×FÑgEðé°>«„œß#Äo¼Ú›½ÖE—!½=ßfx�Çû–ÜÑ}wÝÐ÷Ê;Í|æq‡"¥Ï—™à] ä®väï¹=œckì/jUóÅœÏÎ'žÝ.ÀfdVŒ||`BÎ�éÊ ÀŠÑ¦sS~ÖÔÕ‰¸¶Ÿ–GoqSÙ®Wîߦ”Ÿ �sÔ­ž{>ïù¶ &¶¿+¢Ê·+TízúÜÙí<ñHÉH�=B2óI̘‚Œ¤²7d$ÀtÇêÉi XEÍïq»&a¾×.~è.I΀KúÌiѧKÛþÚä(º´QÀœˆ1]Ú ‰hÃ3§Å=Z{^û6CÚtQÀ]"܃ùkÀ¯'u°¯ì’K& ¸db�ñ`ÿêÖŽš™—¹Î˜À¼ü-º,i3dÀÆÅ¹$£K«b‡ÌiÑ¥O‹™c½D(F´h™ª�ÓfDJ—Vcwª0÷dF·ñš'ÀÕϲ¨HZ5!Z:ŸìÚU‰±6ïÅîpßšì`W+tÂ;³0<®L‹²ÀcDNŸ+|æ$t¸`Ê’_ð€ŸŠ9VÀˆ€[àj�`$ÄܲdJŠÇ= bkj÷ŸT-6öIÀ’ŽiÍÆDæX=>YAèïµ¼Úk¯súŠ)3%Ƥ¬ €"ó{i‡Lù+r†ÆÒš�² Ã -r:ÆÿÐg Ì2äºV}m|ÖxŒ‰+`n*_Y G¦ø5ûI15ÛMQLQ„À­)³"¥ 7ŸFn­#¹ü>#ä–%9iƒÅ·0Ÿ“„¤Ž¬¤æŒ+/§øîü+¿éÿÑÿ­Ï�~ÔZký¤}�޵ÖS�îk­µŽÍ'Z/4:0û¬4½ÖZûÚÓk­µÖ��#Çö(åïè'­õ“ö4zaŽé5”¨Ü¶JyÆ•ÖÚ7ÇÑZëÄ)½¯ÑS­µÖ놣†V>It¨ÑhOí}X˜ïÖ•­ûsu¡Ý¦¼Wå§�)™ÖC{·¦½Ð+½Ò �F'vÿØ^ãÂ¹Š°ñ¹4±0w¾Jà!ÞÙó©v=aÃ1WζÉ�÷ej¯AïÙ#tŽ&|K®ô?êÒÿ¬ÿE�ôDÏô¿êÓÿ®sýú?õ錒ø5Ãÿ†wÀÜé{ŽaZúû ˆ˜AÍ)W=òƽVÊ}ãЃåÉã¿Úv·pÊÑsŒñL;;ÛñU4â=Sâ¾±¤\Ã{imšMG&­mãÛ#5;¨ls=fL˜0#l]IÀ^ûç=öEsýP9϶ â9÷ÜÛ±$ÆgÒÜáðö”ȽÞNY¿H{ý|͆ùÛ„ùÞq ߺ#ãSvÜ—øÆ8ÑéÉ3 rS*µ·Ä×xd@ÚàróMÅÚ­&û’ê@ÛTse{ís»�o÷_9?½‡}g÷Žú*6ŒùÙ7õœ.Âý‘ÆåËÎÖªñ™ï\0þj^ˆ‚ˆ)+n�¾f+†ÆÅ—Ÿ(F>}`ÎòŒ¹Š)4Ž6xÎoåQçø G ¬åp¾18b™�ñI˜�Ów¤áQ8.’Ç£ "!vΫ¬€¹ç θîôH©üg”[x‡‚±1wGäL¾›¶t„Ç-�Ư¿ŸÌqâ��„)ÃË5£0{•¯ý¼±%¾’Æ£öðÏðÞ{@f+t¶W4óï�x`Q™çá–²i$ä�|…Ë–;•³�bmœÈeipLË­ò:³ý>–tÞ u “#¼;ÁPNÏ»WyüjO+”ÓǦF:GÎ�Uªkicä{&=ïÊŘZ?Kâ”#´•Ù7s;®«ÙP08iíHÏ©Ð÷Ð Bs6c=sbVŒ;Õ<b#ÑŽ¯¦Ħ,¿î£b�`l¯¡‹ÇÊÞ�K.ˆè�³jæ‚6Ø!èC÷ù‚K¦ bֲNJI¹Ø×y¨Ù(Ò-yׂ1¢0ƒ¥SëÜ2!"¦Åɘ]|Û¦�ìKÒoxµS cDè´‚eUëì±a2#^sF´â;ÞŽ;|SE¿Ò¦ß�=­[HŠGBd S�\%O€!9�3­t4–(`FÂÔT‹Å…Y*×5"qƒôiÑ"0û+{Ž[< "fŒQ­Uîñ%N—  p¼C›�âÆ`ü9§{1<³}‡›�-‹éª[@_*Ûö¥6þ¼`j¸G Y™aLNAÂ�kÚx¦ÍòIâpg]†m< ¦;-{EbŽÜ©ÈEùòvÝm±y7Ó¢ú|©lé±fÄ”!>!·yè8B¶Ë5�ÌHm÷ `h*Ç=±9çƒ�¯¸fBÄŽXMH˜âØ{}2¶c0)c<�‘˜y“}Ïtz ‚ðCÒ ‚p2"‚ ˆ`‚ ‚!‚† "‚ ˆ`ߊvctŒ‰‰µ.¯Ï'¹?#³Ô¾ Ãšym‰{Æ&’‡ ˆ`NbØ,Ÿ1üáV© B0ŠZìG¿–ÏkKNÛd¼:í¸màñUÖ(2–¤,Þx:yy–ýK³GcUµªþUŽ;Á|| g~òQ—dtj!sö?Í׺_cY9ò±ÃçŠeø¼Ô$ÏiZ@¤8'¸«2±¥^G0îk*߆{’ƒþ‚îéSö¬0&¦Óð¼Š9·‹þrRR¦$@ŽGÄ&ÎÕ)’1"c…Ç£ÚÓì×B ½!s"Y›úó̵$b6!tr"Ôž09éÑ¥U÷vaZZË…ñ&¶µ¿'ÂÕ[‘Ù ¡û·Vªp—´öÉybèŠð&†z›€;ódN隌HY›Š‘ÙçWÆë�·ú|¯RIx Qãµ$} k´%:GWbv�Eò�Wïåwš¡ÓÛñ:x/8Vh~–öþæŽH—‹‚© ˜Xb}§ x¹&w2Ì ?¯2¬ze;  Lß¾š®OƒÊwÛ 2ÛY9¿×·Þ%ÛS޼1�l}û²„yå¸ÙÉ‘ùÞÌÙ 3³†ë„´â-Ú΄ ¦6PN5�˜:šKe@°Wœý�»WÔ>9繨�ûâÑgx4–ŠðŽãÁ¼)Z̈ˆh¡˜Ñ6é–sF´±ä’ˆc]$@ÌÊäz/ G´�Ñr¶.�Ӧˈ ÚL˜TÂÄä&gü…Ðþ�6m".Ík^–p΀6-#mZL¸´ùÖ'\rÁXš7™­²-t¹ ËÄ ¤èÒ"â²1ãúq j1¡ÍèšsÃ’.}_fD6ñW\ÑcŘŒŒŒ¥‘¬ÌÄòê,IFvÀ–SŽ·ä’ 3ûšŸbùM‹ÁÎs)ŸßˆKó\Ýf.uï£ F™<Äã�EB€<˜ìßÛv='æŽ)�� b F³âÁi¡9b<ó�¢‹â‘5ò§f`|*m«Ã�+ú(F@aJ859½m|Y±F±ÆÖN¹¶½üÒ†è°fM@Z1Ú§ÀŠ)І•Ñ%'aÅ#K2k±%&óka«±gm€²Kò+!¿rÏ=ó7î¹Ç'&8Ò1»·çh²”0ùrû,XÐ'1¼|.CîLâ£Ü”-ÖµçR€^³f�¢íØ!|—´ðÞ¹{±“…}“ßþQÎu‡•;5t²�¯ôJ¯ô�F¶ôçPÏ”j´¯Wöi†úÉÜ�N­¼õ§¸½±Í`�s7žœ+ökÏéQ¥¬ìí0#%§CRi‰ý½Ž·MúœNj‰½Ú‘ì~þY‘¾ý[¤ü4'Ç«äŠW' _Cm¤âTgã.›ö|»`Ž·¢L²4µNÍ^íªVføsP1ð'NWá;qBJNŸ…öì‘pËnr£Í½+ަܹ³G`ïL*ö¦âÅø!yÑLÏçŽ=w„àŠ2‹‡¯4[£Z*ÿàK\æd;fõká;Æz^9rÇt‚Jf°m:êÂT½¡câç(£1Tƒ�¿W;×Û#ct‚´ûàˆŠ¿÷ •s¶LêÞÇuz~-Bã�(˜Q˜–÷5Eã°…Ð# g@Áè•ótýµÒVW«z‡rª–r ˆ˜0aDËØ+ûœ §L¨StéÒ?a.K�ÅÀ$’œ6ž#¨yJ�iì"oÏ-rÖ/ŒC¾ˆ¬ò{,Œ><Ö¯éûÁ©b^íÈ×@Ê„a¥dŠ+®øÌ93iÏeBJÿÄÔ×+<“´ú±²G^ùÍÁøØ]’×é œ3®Ð¥4åÏé“Ë}ræ¦-(œ‰P…9VViqï^}æhŸÌvt23‡þf«]Ù)Ij)"·3b¯Ÿq΀l'Å”j´A‚Ÿgää’ÝXƒ>ss„Â|²=W(uï#Y…óï~9P•ª‡³¢êÈT¦lížK™ •1%bÄ„IãH¾GéŠ5” h(áÛÁÁ‰]þH˜1§�ÇfõÆŒœ‘é L*êÊù·";v@Êv.eß(�éÓeàtRÊNIÿUW_\WìªÍšŸ¦û“2"2æf›ÜyŠå>Kr繤 ÷vTYpŸ‰`ü¸œ?¬º6ƒhÏ.ºƒ‚žîëG­u¢}�FûzªSó{¨ußl5ÕZ?iO£ÑS�˜½}=ÕYÃÖ±s†ÍO°g¸7Ð+[–í¹}�éŽÙ³oõÊ« µ¯C½¶ÇÙœ5Õ¡öõP¯µÖksì1tåx�s]†æúÐ�Ú£øf8v­CS‚¾~2“î�ãbg24÷}e†GÃÊs¨þµo`շú�-Ou¿•){ß r�O±|.Õ;Ú{ëÛ{ûX»+O2~ùC«~óDFŠÿÄþlN×i§î™6,_/(În½¹3X=NaË6'feí¥{¦'å¯çÄ+S¶»ýû¼Ö5ˆÌ'Šœ¡ý{»õêèù¢–þoŸÍỚÓea-”%‰“úQÕÆ}@Ñbú (ámx7™Ï-ú•5ž·_-Í`FÄÂΘhñø�îDD‡Sퟀ�élü<Á c8sâÊì™çSÐfX9c›Å�& KqrŒá{Œ(âVŽ"³-~™úëe%½f„Ö ˜TÄãë²²ÎË'ë-ØrÚÂôê•"S(Rzæ¹ÌZŒe®váƒ9=¿9-ÚD´¸¤xØ MÌ#ãÒ,¬kã¿0ZÖ·ç†øU–™WŸKë`<Ñ_ÂçüÐüPɘK;å¸÷ÕÇñ –d(|zòÒמKjî̵̯† "’½]„÷êÃAC AD0AÁAÁAC AD0AÁACAC AD0AÁAC A AD0AÁAC AD0AÁAÁAC AD0AÁACAC AD0AÁAC A AD0AÁAC AD0A¾­`g|*»ŒŒèR1£M«á›6-–òTá½ ÆŒŒüÌ}~%ß³OñlŒLÞ:ᣠFA ÌÎÜ늻=߬Yqó¦¬h¿ ÊV÷œ,•ÊÚaK&òÖ U0fx@òjV�OøJGÊ÷VÜüÙÇœ1p®=A�,™ù­/ïœðQC‘ãóÆ «Èl•ÊÉZvµÓ¡QñQdÎ1N•Š—Œö”8Áõ&@b-uF‰rû›’wNø¨‚±Äã†>0­tSf´i“Ó"¢ ä´qÏ€V¥ÂÎiÑ6Ÿ*æt¹´â£èÒf΀Vƒ íë2D´Iðî±`^‚@|v—&�÷LÁ€)}àÆTö ¿R�†¦ŠE¬sKQéÁÇ ‰ñ(ˆP(³ß†ˆ‚5w,PôOèôäDDäôYñÈõÞNOµK58Ã36×38ÓNðä=D02rzÖïÛϯ¸`Ș'VŽñÞ¡ê6\0fl¶˜ã3v<rîð€à` ½’+îvü ³FÁP´’0<80œW,…„˜€âd‹GD0l‡¢oªß Pìê=ûÛš5 ˜Õzþ�ùw+$¾#G[S~�nìLä\Ò²nÈ>ŠhÇbÈ:eÛ¶öåGHHNºG,.iWääš1+¼†íóOIf…%”7MøØ‚Q��Ù*:;Ї÷ÐÞkšìº³£†¼¢‹sækBCóùÖòÀé¶ü)°`ÅŠ5¿6ž¡ìzdµ�PÏxlòZwÈ=ã(èî"Aøp‚1Ç玱ùé^?Ü�ŽÍ±ÛÇ÷á030®ÍØlë³bŠGJ䔥Úªžg‚üƹs”ñÂLkWÖ£¿ñx¨§$%á P(û©x1„.S†„öçÆT›fþO`üûl…Ý‘¿b³ä ƹ%”3;â†59]ûIéªÜØ_ìñ;Æ2P{elÍ- @5 ÒŽè05{{N§ão¦ô�Ó�’qá}ðé¹ö…ªØ >RRn�¶T™ß—N7¦™ ÏHζò†$Ä|! ,vöú+;¯!Ýéðä5WæÀ|¯€ž=WÎ%!�³®ùI¾0£ ÀC‘2aì\Y¬¸'æ’ÀˆÑ…³oÛt³6¥Èkö” | ÁP ñk/~HŠb­­Šk+äLÀôèçxfÐ3'æd$æh¹Ý{LŠ""À##npz*”7È{¾Ï¶ìïœ!ª;L»dtñ‰ižSªìÙ³�ï:òÖ ©K’ÓFQÐvªÊÈ «Ni³¤m*t×ôøCÊéNwÀ�Ï@ŠO›6—LYÓ3ÇÍ€)‘ñG”-ÎÔiÝ·•¼xÅÛàíøWòÊñC gD‹!w€o„®Cl¶ð IY3$¼™ëïõäÂ÷ÏÅoúw~ã—7>MÁfÈ4« 1fGjÏìÌŒ�oXâŒ.Oµ²ÌŸÏxâ“Þ-!?óü‰?ó<<.ñðø æOüòµãõÉióô†þ€‚�/œJ.ïM0~Ø}¼i¨OBùÂË}ß�fG·Qá † "‚ ˆ`‚ ‚!‚† ‚† "‚ ˆ`¼Ñ‹ò«V™L‚ôúäÏ÷²+ûÚ×"üˆ³“m¿’Q& 6é áÕcN?ÿÆ—2¡³S=2\V2œÖ»¬éS&Dä•´‰KÚ\pAÅ„ .+F¿W“Ÿ±“~ù¥VUJ^IäPÒw:jwd6ÝÂ]íÚ ¦Ä{å"e¬–^ɽ–›¯ØN0®K,£Ëö…/óyd̸ä¢Òr.iqÁ%]fÖå–Ó¥E»V9·aF«!)ã|'eÁ’‹JÛ8¥¼0^ � 1ªÒ&Ïèà‘0$£MLÌ�iÅ1è×ÄÇ;ÁóqŸ�‚¨ˆøÊ±0<ÚDD´MÕ¾©Ù�óIîܱ‚]b†´+i«ýŠ|ß¼Þ©`¤D@`ÛîŒ.=4SÛF.I(‘2$ ³B°¤ÃÍ””!#&¦’<²¦Ãt§•Ë0lô–¤•ê¤èÒÁç‘�JÇÁ¯üV˜*pÇš5žS…J£¿Ç-S úŒ1ÕCŠW ìWº>Wtè쩸°â‘ޱ�: Ok±½k+4OÄæÙ5_CxeÁˆŒ¹ž²°¹Kôé7” •Ç&+ØŠ1w”ãe…,S–¹Ñú¬€ÂædíA¥3Q¾Æ«=ƒ{…c=("R¦_7‚ñ`¶ÇV¦6—F¦2”m£{ĦÞôqÀý‹Êœ6>kBîlºç]?ÇŠ5!>w çÌk�¯­8–×±ÄcLJ¾§ëáKÝ^[0¬X1Å'sº‘7Ék�� öZ+Ó2ccÓ…È+Uùx›ìŠGî¤[>T½ söŒ�Ó·ƒ•[‹iÆØÚ/o�õ,£ï88oö¤Gh¤+3—{ر«ìžÛûuM°#Ê¥è'äLÞläKø�‚árÃeúÃ÷@�1cÆ,X s·}�=û"{Œ¹bBûi„¦ø }Õí”±…º\�ÐgÌ­W ñ˜]R†_Ñ@ðP´‰NêÜät DÅÛ9êFV„tìÀtS2… Š˜Ö fv"{…£oÚõ’KBçg?1£€ÔI‡<§EÁêE;oXóHH\qëíšõe¾TX³ fÅ�©j¥‰?%=¸ÿÛàñÄ# ƒ#gÐ6CÃu)®ïwkGpV,¬�T4t± [Ž>S‘ áµ£Žkä.˜µ=b .iá³6/î’>aã|ŠsñY°";0{ÑgÈÔ”¯Ã؈[n{ð=V(<’3:"¯1AܧÇ#SÒ#“Á bV güL}˜;$4â\í|d Wv‹�Ï�;bq~ ¯/™íb\©5¦sâ('a�æÑ™’”?ÓKД1dEq`dc ;³lÇ[BV<±>9U�‚+Yη“VŽÝÖÄjÏ$¶Ÿ0à æ-jGçb2ƒ IDAT~xµáØò^>òh®ùFB! / åü[0"†ølÒÿDŒ˜3"Ú Uy]K&Lj˦rRF”3<Š“JÔ§y|"Ø;ÎPZk|"ææ,99ɳÇ23‰íõüñ3÷œ’0#;¿%`…¢MjîGN›ô¨-çíÌ >4¿éÿÑÿ­ÏáQÇÚ7 ó¿¯ûýÚ~ê™Oc³m¢×º£ÑèŽ~ÔZ+ <½ÖZ¯ìSóéÊlèX¯Í゙ÖJõ¤=ÝßSb_ãü…^Õö Í1=§Ìû ul�+G~Ô8ß½¾íï«ÊùêT¯lhï¤{­ç¹…úÑù.Þsìäà9…÷Æ•þGýOúŸõ¿è‘žè™þWýoúßu®ÿCÿ§þ/}ñ›þ�ßøå,ë"¯µAACKûÀ»T<³[zÖ^ð˜�qcçB¤&SYÁ’ÏtÌQ®ñœóù޽Qw§Îˆyll/ ŠÊ<Ô`g+EÆ„�€ë£&xîXÕ#OH¬/æõp;iŠü€¹~eswjÕ(1nÙ¬æ)ËxoËxeJòÅÚ ðññ)H�¿¡ÊÜiÌÌ„°[G |ÇšàUæ}tèSÐeB·1Ã[Õ �lÔI+eë°É2£*¥¼²Ç(s�ˆ\/²0“"À?YZ®w’æø{²³û䌞5Ó³U 2 GÁ²R]SSM’�³o·z°¤Ã”%òƒ # ¨Y WN©x¥‰b>Šœœ¢¡³Ñ$1§Ü³ý3f½�;#ϲ0®mk}ZEÆd7+Yš*]8Ÿmí€;<’gN® €{ ÊqœŸ°âÕO)ðèqUÉ亟ebiµÓ!ÙØWÀgM‡ °G½w¶ô^% ð’� ›&Ù•_Uëæ¨ÆNTý^oŸÏîÞý½Û "gÒ¦'Îë ÀÌ-+PlZ­‘ÓQ˜;[O�aƒÇÂ?¡ç¯L»š˜Wþš‚9ÊœkiÚ]ßä6ï-¹�¢Û�`is¼ßÌ{(e*7å,ly2 µs¯®Î¼çy¥3±D9U}n‚ fKJ5ø}ʔʚ2a¢ά•¥Ù»ìLfg ";ý÷2 QVi•š[áP‘’1#2“¾ÊùŒb§3=ú@´SÅ|‚ÊT¬ÂùwsÆŸŒÂØA†&9‘"ëk‰OšSPÊÏM¥5.ìÕN­éî3ed¦¦�ÁfHŸá2bT©ºY¥òïrßPÕ} ·9æûŽßã°ôÈÍó˜SÐaÐ0£‡ï؆ss ÚÏ :ÄÌ�ýsAÄDæ`Ï›‡QŽÎoÇñc=Ô¾F‡zm²»—3VÎ܇ÍOR™Å°ÍÚ¾ÉË>Õ�zeó¨?íÌ¡X˜³/lÎõÐü”³?†vÄvNCùiâd2Òž3'd;«$®å__×þ~ÔžötÇÌ]ð4&‹ü£)ñ¶œ�^èX{zmfq¬œ;ÇÞ<óZ?Úy,;¯c;ÂÓ±îkO§vf ºcfL¬´§}½Ð‰öt¢×ænîÎÃð5:Ö+W¾Ô¾öt¢:Ð}{är�í<ŒŽLS�ìíÏOdTpoäBgŒþÞ±;Æv˹ÓÂmÌæ°ÙÅ·39BǿѫµÄm3p[ì™ÅXî1§pܬ÷ÜàqOF`G`r–öè[Ò qmÈ„›ÊhCÎÒŽ -É›o—äÎÑËòm®mV±Rªëav­ ×gPuÍÎÉ)�U¸9K°%,LW¥¼®9E­ìÛã€W[Í«ÌÀô¡)Çöæ<�’Ÿ=ñ\øÑxW™Ïr"oè·ßŒ×FѦÿ¦gxKZ²°íƒ Æq+`j×{üˆ(";·òG*õÖW"rñ±ùáõðèîYúýÑQT²oÃ%å4°â¬0>‚ÆwÁõÙC’§ã¾©-~H¡›šÙ«’ Z�ìí‚ 8HövA^  AD0AÁAC AD0AÁx×ò¬AãT´÷®pỌô›¶ò9‰Cb“”ñŽ…�(¯{Ø”¡‰Û}O x¬É™“rÏ�ÓÕP&»ùŠœcº”yÜóJ·E„WŒÌùÝu8Æ„Ž˜¤V0ÊDÌ7ä$L›Ê_vP®�.žI¦SæÝ*»&1¹‘Œ s ÅÛ±(f„vŒ&4s.||:\@mûà‘1ä–‚œ„e ! D'o"«J'$k�‘°RM?[/DÒ˜ô°°[¶�÷é�2glŽœpK™Û£NZùÌ?PnEN`ŽWæD)óŸæ é‹…!o#û9J?Ük¯x ûõHI3G’‘8VK³�ÃA¹¡ˆÙŽ©$fèu�óHAxSÁÈkÙ;a�zFÑMGħ #aˆOÆœú'±¹d “yÕ#ãÊLéš‘?û¸‚ ‚ñlRcô—íö¸fæçlÒ»”9З6ÝàĦ$ì32Y1<ï*{ûÛ076ÎXÞAã `|’Tv±�† AƒÒw’É*UA8ŠD À—ù�‚ Æ©ôd‰ ˆ…!‚† "‚ ˆ`‚ ‚!‚† ‚Æk±|§ä9K“r#žî½™ó…x Œu–2!ÇcÕPf�¦­_(£¥/î„)R¸nˆœÐ�[Kì ¸G1ehC-LH€u¥eZîSCS ¯äÃðÈL*¢ƒù‘¾x÷µÖ8|FNñˆM•ÞMQÐ.éskº&wŽo஡µßõ ÜŠ1!á‘ÒÇ„G¢†)ÇF¸çŠkrFx;~™/´é��ã2ÞÓÁÈÁœw‚g½B90aŠ"`ý,[0@‘[ È熀˜>žs•åUt)£Ë{Äö›9×Ürɤb3*Úô3عMxÁøÑ(Žºîše±Úe™QfS룴õŠ®õ¨½ÞŠ�€î UiNLŸÇ£ËŒâ¨o%7áSþ9)w ÇLœÖ‚ÎNÔ÷&›+4ÇlÛ Ïe¾ØÓ�9Æ ‘q›Ære>Ÿ:^šŸ�°ÑçtãtŽªWwô‰ž!d"¯FÊØx�å'KWΧ…ÉÏ~m+’bI�‚%žó)(2øâ<ðrß+§7¯È¹¯m_wÄ…uB\Ó®å¥ßZ1e™6ÖCŽj8géHÐ ]æGFº'vû&ô�TÍ­%°åjïù¯¹R:G»ÖöñLNÝ�7èùž¯&ïÛˆ% þfŸ}rR•ÿ dü͹öè3‘(kßF0rºxôɈIèQ0!E1%6N¸MyÆ�!1CVÜ›~ôдI�üœEL`^ž%}†ø (̸CNÈöô®·åËc�g –È!+%gHxô­?ÂåÖ»™­¤sî͹ä(', =ò*§:`32§²«ØnE<öd?Ÿpîð ^MoO9G"‡ì²�ñ+et{Ä6áæ#Cn¤[r/V�È�Ü&gE\àñ„"§‹ÂcÊf$@B�œ6>�@J—>w(Ú¦·î‘Ç^FdœUÛól4 Zh@Ñ"æÆø †NˆÍëR¹xú Õ52WÒd¥d<šR(¢ó FôØ—¼ÑëhQðô*¯l›7dpÀÓ1áÜ@Ç›; ÷€W±7\²h¸cQÃpòóhÊÎèxâ- À£CÂ-}…¹ó¦v¿K†bcìt•¿B`×ç\þ�qG¸3©{µö£pZŽ•ñµçdÜX_~aÒ(û¦“2Bñ…Ì�ê@KWPÍA²m·NoóŠìqG›z®÷™É;Ÿ�°&¨ù/TC»Y�¿Bû<§à  &53}›KNUº{§Œ^æèSB>ó@og@¸ôZÌÞtè2ÜyãLZf]†Ü ž“ý®ãu*‚ñ-º$k>…1ÃwÍÊ2“o¤ìpd{·Ü´Ë¾ÍRÎ& H‰ˆ¹1†z�WÙæ×#/þË$±K\©(¥ÇþKå³k#M=#…]“§e#'ÅÉŽ×ûF�ÛGŒÇ„;rÚ¤N•Èa)Me¿=w2ÒêÄ@Û¸.˻ܣMw§óSŸ~M&•;»K`ʘÕDZ‘9]Éiån _I0< º{û”¾Ÿ91!W&{Ú¾^1„;>‚[ëE¸3¾îà+ÅúVD éǤ´+–ÓÒºBçöJ|ÇFQÜŸ”.);K0fÖÏSÎ�è8ݨUM:7ûo'[Ž«Ê“î;C—n¾f¨9Ó3F8‚нå9wçKín _I0æô²`_CÏ>¶ˆŒaƒ»q· °+;+&LɉŒ‹3?iŠõK)èүܬÐ'åÖÈÂ�)}ÊL'9·€ïÈL'W+u†˜Åø{,—IePÓ£`bå-9a¥GÐðDUcR{�ʬç-xÞê•�…õÅÎ/UÆi¹}3†L­ ¸Ñ x pœÏ÷5;*­È]Ðøž ûyájÕÀT¨>MÓ–ª]˜‘Ñ?¡Ï^>È̤0ÂãÖø–f«¹ó öÏ?Øa9&†#néÑ5ižsr"º ðXÐ'uÎreæ†gÝÃúgÉÉ&þU™‚ä9Õ§êè-çV„¦œŠîÑs\£k²¢HD ke¥ "Ã3¥hÑbR©ìŠƒä0§KÄ%— ˜S[±s"G¾LˆhsÁF ™: 0«~­‚¬&Ùá³%Mãäö6²Äw<Êñ?Ô[£M¾vub ZægßLµž›õ£ ŠÍìÇMUÛ ÊæDZÌðÙ-É’Ï,ñX°bÅŠ€€Ohcß±pŒvŸÐ¼„§Ø9Í”=†Dµ…åª2‘{[!¦;¾•ÌV¹aM¶”c~ßQТMdª[û¤;4¢Øq6zN5œ3æŽ3\¹¢OL‹¥ÓuIHœ ½�9SžxdÅ#šh3'« 6wˆÉé°FóÈÍÊŠ@a\è[qMj÷ûNV™¼u—¤ #ãšÏTQÏ´2)Š ›5<±¸%E{dx M¿Ækì%G»F§Tûçö�sÔ^¯Ï6tðüŠÃQÑÝÉ:ï“’òÀ”NÍ껪ˆyÀš”Ó­ ñŽz52Fä «U2ÛøÌ ðɘrͬÑþ9æ+éõ3õñjSºo j®çБ·¸r o§ìb_¼¹`„d´ ¹5.`Jl&O-’Ù  ‹"´žú9 CÖDäxŒŒ51$7ó€;BÖŒÈÈ íºOŸ‚>ÊÏs¶9Ü#÷7ô¨›V«f_íæ/w¦žW%ã¦!º—¿#9q­ÞRÐÅk˜aàÕŽ×9IìrKR ´ã%Õ»˜ñ`ªl�‚6 �޳ÐnË 9ÉIËѼq¹mXõÂÞ'?¨x�8ÒmNá ãalÜ›ß m:µ×g^k™6å>3âP<ŒM„�ÂÌE�*ÁerBǘÞN;{>ŠÙYó–xg ×–VbY}¯Ü•ôèÂ8÷xéWYÑ£¾£·ïÇA²·ÛÞjTY鲯e:6Pp è… ÑV¢…L*gT\wÏÃ;sÚÑs†>O9Ãé�²ì«¥½–l¹ba¼¸Íé ¿÷uPŒàCÆ€*ÄkðC[?½U…(GÏwØ·'`ý�”¦°=ü�†È…X�bîþò’‚ø0¶#²¤GÞ’—D AD0AÁACŒ÷À9kW‹ïúJÔW¸Wê™%S?ì]D0*´M˜ØSh�QIº;ùÎ#Z•ªS8˽›ªg=óû¾¸)#Zµc»{ÍO�‘�š:aFVù™Ó>ãú]æ´|ÛÝûvï€ð½ð�³·gL(;}K�;Z¤ÜÙÅX3 î(Ø®nð‘Ù•“Šn­å-ð+«LBÚN…‚œ2³¸Êæ�kRÆDLYà5LПâ;Á|¯‰Hè᛼*�‘Û¥÷ñY÷5e`J› Ó›Ó1÷UÕBêg¯’D0Þ\0xUÁضÙ}¦vmç ·@åD’¸£…2“Ãݘ™¥xÔsžß�šdÇ9³3 ;Ä?`AĈCr²J°¾¢f•„6 ñÂéè<¯Ê.Mê£u`ÝŽrž@5 Nf= ïN0æddø„|fÄ#9sÒ=Yуo�šn²`æåD�‘îmð™Ñ§L_Øç�22ægǙҧã¬Í’7fJ�±1Ò·:;Øzod �OÁ•É wãtA¨E½©Å¢Ú„ò«¦#hÍ9¯ìâõ¦4C…ìŸ= í';a„w 9#2óp—öåx kì™þÊ×ws)ºøv‘WëÀJÕ‘5�›ûÏnd.ENf–±W[ô€¹©š™mŸ3ó»6]¾GjZù22wÀm+õ &á3¥öqCÀG02:µø›$‡K#DeSPv¶9ã�çœ_šŽÆ’”`'_lýjí¶I%èÀur¶7ᇌœEᒚ 顨ÄbÜ¥Ç篾m€»„¼ vRWE G™×ÖM\0°×WnÓ>iû3Qcüî ùN¸½…=n—!W@l²¥7Yû)9“­í7ÆÇ3©ŸÊÀ®ä wŽRF¸º"ç ™¹öª€Ë9¯ˆY™ÿËôÑ'þÅŒ>W戅y>¥­´6a‡rÒg¤Ì¾{Á袜¶»|•¦{R l[ɯ˒´–pÇí(]TþŠ¿AÀÒˆû½F´{�M1°2§em®f…‘°MPÿÏ$P˜óÙÄ2 ™“Ø»X°‰…•PPÔ,�k¦´I¸fi÷mí%n(�‘é"ù,ð�¥•âÅ©:`;rΗi ݆Œð©‰Æ¹¢Œ¶  ¨3¶q]»;–�ð}ñ¬aÕ»¾óú2öb§s’×~W Ý—|'‚Æn”ÍbO·§ÊÐzáO¡é5­&[ÊNW w| Š©#Ê WÎhÙ;ÒcÌŸ1cÆlÇpr&Ì(×5ѽâŠ+îXíxTJGê7ûHÇúXª’žÉãΚx8 ï7ä{l3cÁMº-Љ¼w› ò*ÖNÏ|¯¤n¾ #sm“‘]¶[SÓªßQÎ<˜š8—sRú&_û6wÆŒØÉßôšçÄtPté›J‘1À#†CºÎ(ΤÁÂp%¥r…»^ƒÞ޼-LDôÍðç½õÌMÅ*ó»o­†ª.ˆ©äËk£Y‡¦bV^º&ÕCRéšml«ÌZ5¥8V;w¹í*x�Þ œ wÌhµûPéÞm­µÜvÃÊF¤0Yî½ÆpÃÂ)jO‹¼}ɦ„¬x`H ÷äÖ&Øä³šrǯ ‰�m²$f�ON›!>×(úF:|'ûÈŠE›)‡¢@§”‰ð^FßVò ú/æŽÍso«~æLÒ*³¡Ý9Õ>;#Šõç³Ë>¦qpŸÎœ³†`û.Aš:Ý·²ü¡=R¶“ø:g¢bCºÁøT­3—â[™ ¬åSüÍtÌ~Áx?>Œc³ÊèØ!)÷ôèÙV- Gb“û%ääøôâR®ÉÁ$¢éÁÐ!d“á39Pá ^# l�¶™Q1·¯x3W•*}Uû¿üýæÀyBܱ•øÀu=Tª»bnó¹îR½C®™2ܳ½dfŽ7ä¾–QlYÛ(h9Ý¡IíI¥ÆWS ¸±¢–‘˜;!¼;Á8MN‚½1=7Y'<Êt¸ŠØ©(å°[J—[|óÂå䦽Û8 ý‚Aƒï£ù¯}=fŸ¡Ig\5ã›¶ô�¤�Ê4¢wPnr[ŠìÈ�-Œd*Ê„…“¦Þu,*SÀnh1€#óRrî+[ÌX$Û«¯;zï ‹¬ÇÀq‘§lœ®ÕÔÈÂ;ŒÓo�îñ¾‡�ã> }R2“”'«x<Χ°Æoý/uÀÅ—0ÂÇ?1ÚxÒà[Pä$<Ø«4ÄTJ¸�ëJ®“Ñ^˜ïÜÏ>ÉNâ£ú=R w¼5ë³ Ãé¤IDAT­µ¸"b0±OîW<îéÓ“üBÂ.¥Ý�¿zæ‡û�ªÖÃcHALaZ¨ççÚö ±‰+e$oAï„y•Û#7Ír½=Òõ9¥K2#§G™{îEcu,ì53�]9’ö“ž˜éá®G¥m…î†æ´é£d øƳ†UoÌËõšxÆTu+GÁ=׬‰)§Y•Û(§"ÎÏ8ÃêÀ˹8ð]@€zöï­\½ Åà Ïf÷jû;^œ)}’=Yî]’Æ$CþΖ‡ÊsŠ�’–�;W"‡ ÏÈ—&üÀ‚á3’ZuÍœyçs…;«pΔ€‚„r0qhª®vecN÷ÀDªÎŽ5âx9½ß €)S¢†«Ë™ì´Ø Qígt¦4d �9w`swbâØJWTÇVf¤¬¸£Ó(9ʼn³,óƒ8'o̹26Ãáî]O÷ˆ—ðÎ|c RúfH4ãžØ$Ù-]wckê+Ç.�”¿:^eÛñ> S2B2 ÓÒ•ƒ²ÛvnHLJNHAFràµí�rÿâN“b`ªÑmgUEɈÌN‚Þмðî�må›.Fh§Õy±KÊ©X^ƒÿ(¯”&Ýëo˜›Eçw$•¹˜9ÉNVôC‚q¨“rµ×Û³ª#摉¤¤ø‚á±`ÎÔ×蘵œ°•±"2“°À3c S3¡*'⎹ÙbÌ-›AÖÐF(c9x¤¤ø¦•R�àÛŒðû¼±’}>K†ô�ó.`M—!1}nlUó|.��À>6ÇLŒ¯bÃ_¡R™çNµÓuPÖ#´­œ<{O=V Hi1dl'c ­\•ÏÉ­Ú^MþÔÁ#ÿ$!±ÀãŽ@ætþP¼0óYa,‡ðÕ ”™ŽGµ jŸx'¼–Q­í¯\ø‘ñ–9 �# ›Oc3Ãc»Ì»ºäû¢wbS=rg¨0 $c;t ^ƒOÀklÅ33umßßmnÍ_b¦Ü0"ånç Hð¬¡òˆU§gÕ6è¢ö¦SΘԾ‘ïl­Ì Úº ”‘~gÙx?"ošùÌõÙxaC |ü“æ# ,“Ú_î”Ào4Ð{ô˜;KÕfµŠí3ݱ|–ô�m|2 Gά6;ĵášËÝ©Ü#�¾ó÷ܹƒ7À5ÅžØwÜìñêxµE„aåú泯»÷¢0]¤xG.&•iâ®G#�œëïÛÂøÞ v–~o^çfG§B�!‚3²=Ç?×W’׺áÑ•±M]¨éÉ~ˆç1gè <Å^ô–¡y'[—Â÷fa¼sÁ(-€ž¼¯&À·R¥¥Kòž‘‰A¯É�Ü‚Ž$2AC AD0AÁACx9йLŠD0„Óä"¢4º– ˆ`("r†œNøáø$·à[ȲÈJ C8…{ú¬yI°Áç“}õü¶Â‡·0 'Ò–ï’w?‰(X}˜¼sÆ;)ˆ6„\‘9©˜®)£†xg-ãÚÅÍ„²É:woý"=|–ôMøä’ò¬M™Qç|~¶Å3¯ˆP¿ò†4•ãr–x\£XrKÁ=)áÎtÿ·’Åä{ ÏdÕ‚¡Åአ!·`ó~ŒG¸dÈ‚rw`s·û&Ä\q�r"r.™’3xQâá/¤¤€ï,ûŒÏÅÔ$RV$¶¢nÎÚ´äÿ †Ï\¤÷Åæ˜ LI<®Prc1ØNà)>! 1å¢ý9ªAd)Ù åW8‘ßôÿèÿÖçk4z±óÙ£ù=ÑÓƒû?éT¿V½ÖZk�NÌght¬µÖúQû:ÖZ?éØ^ñ£öt_k½Ö¾î¿èÜOÚ×h¿ö©g�ú¤c½Ú9«§‡vÛÄ>±'í¿à™$šJIžœRhý¨cs�Na­=�îè'ûÉP£C­µÖ¡½³U¦ÚÓ�³‡ð|®ô?êÒÿ¬ÿE�ôDÏô¿êÓÿ®sýú?õégú0Ê–£7<¥omŠÞ‘U¢ƒoÒƒ Ħe ZhߣñÛ°:sbî(ƒþ©ù¹õWD(ÂJ\�Û£ãI7<âÉÌ–ïÕéYvJ\Z^‹CyˆYåÿ‘™PØêï©JußÀØ©Ð/M8|½#Üó=Q«Æöéx69ô RÁzx/ˆá]JBÀpprüñ¦wCÁN÷åö„7rÁ�Æ÷*›ÄÄçAû¶jäŒhY÷›bÀ%\š­'&IAĈ‚9\:β -".i[$gB‹ m»å„K.*É –´¹àÂI °ä’ˆ‘=vN— .h9¡tŸ‡bêdÖhIìsÅÿßÞÙ^·mdaøá§”ü R�á U°SHªÀTKVº‚¥*U�  DW ¦ÁÙ“œ[ÒÝ3hŠ’H{ãeÞGçÈ4`ƒÁËû1àu‘àzpàǨðãåΧÉð¯yMƒ#Š ½*áž…M¢âEgF_ç[Ðó­º­Ý¿§ÑXå+Ûx.ç$”rƤ&eÔ73F\ðzéÊUL šI‹QÌk2ýsíú•œðšœF’ñžžÏ3Ýçw ÃÌìØQ‹[KBÔbj¹axïùÖRØ­ÝXjxïy<ÑÂú†¥–ÚÀÑñ>Brk‰%~ŸÌ°Ôr›øJn};÷ûWžlj×þø‰ÝšÙ��,ÜúX‚‹9LÍlõqÏ}•GÍÒûË #D=» ~¿ëobXf¹?Ë~Ilúšy?~9Ž�âcÞ®Õ4øþfÓ¨ÕÄ�òòLÂÚŒ±aD±G?êÇÐRÃ2ËlX»rõ±Ëm!7,±ÜÆÖ7üÌ0Ë »5³óh$Žýu¾µ,zWlÃØB0®£câ/™E—ð2LÛ80V w§>$–…É–‡ýóT–EÇ?ަØ�ë�‡ÀŸ»Õ&!4;ñ-¦á–È£[s3†¾•§Ã�%‘€.ŸÅ¢/ב´dA@óGZZîzõ2Ü|·–„eA–ã2áÍȃÚÊ~¸1sÍÛ¨ßõ±¬=~?ºÆ—aŸjï$Híq¹nÿÑ ÁØ2èé‚|90¦Æ×|Ö$21K_ÿkì ãUf{æMâªàÑÀ‡ç”ĵ7’èß4ôÃíuFÉÈ·Uµô 8áxË‘�ºLý6sØòiŽëK®ÞgNõ`�çâ÷âøe™òÑè�s?ÜÙ­Ž#”µóž­=§Í]4×ÇÃ'b ™O¾ÂêJ¯OíÍÒH�ýÜZŒ×$rSú~|Äölµ4ü_”\ð†Ù£a©’¬ÂÊÖ®Á¼·¼S�gì5_ Ž%¸¯ÈŸÑ'çW2\͵w_-_¿Iˆí 8zfRæ| óç’<ºUNÁ˜wœ­ðþ–Z]}¬|Ëñ8eÎ�!sF/¨û%/ÍÕ¼£*®_™$šsS­pý#'eÎ�üщê&g¾Á­xÈŒsòZð9Ól¹­KN™PpÀ„#æÀÇ¿ý)ŽŸŸÙƒ!Ç xCÊtíwŸ;á>c²¶Æ|úd«ó5²ôœŒÑ„k2 ÆäkëÒ­›S/¼Î8$圴V‘í*œoªûüïÏ’,R‰óZd~µ9¼‚çe&z/~<+…ÚÑßW”üÛ¯ømâéx±õ'Ïoì'•ÏÖôàˆ1Ð〔›5·rîëβ6±}ñd«sØØúºµu]�–ã Sœýà`=ßéÁŠõ¼öJ¢ñ]Æ‘ŸÆo×: 'A.züôìèÀK§n†«ÍZÝS\‘hȹ$¥ô‡"J°¶ª²ÑßÀ|vIÖEÆk¦òŒ1×Üród±¤!ÀR)v›Þ“ •3†�¶º™`Ì8æØËUÊ—êÜWœr¸$7³5å¬ç”¾†[Y»2Óš`諾 ÁpŸkýÕD«Ë7¦ô1âÀJ«OÙQ¨é^Ù"u·`äŸJ)žI8"¡¤Ç)#Nx/zÜ¢„*P{Ì #Né1ÜJ0Þ²*H8{ÂäÎBNè­($¸à„’÷ŒqV³ÓV wúˆ}1ó7P ô|«~TRàó°šæjCÁ(9¬Å�\´d”•KçµÞö8'aÖ¨@É{–Ê7.µ`Já%jÆïH)ýy͘nì ‰/Ø<­ºXP¦¤ð+*2ŸÈøì­úJ%ž-}²Í#3=÷ï8#�¶¹â Y´m>™]5Î‚ßø…”’ ^‘G6Füzy/·ß«¨RzÂŒßø©ÖúŒ¼ú*uqÇæjW!vÆ Á‹B“¦—‹X4š,"íx·ͰI‹6ÚtyšÜcñ™{î0ÿ#„Ø]Áh„æ^�Œ=ÚÁÆh7°°q›{:Üs�a4hÓå3Ÿ}ÀS9!v[2šÞtèСË>ûüÀ>]:´hia<Цëí'û|æ>dH‚!éb·b §¤éS«]üt¡Ï&­X0*ÉèøØE‹6{|æŽûð”`±Û‚AmÖöjÉÕv° Nš4hÑá.ÈÅrüB‚!Ä® Flc´üÒ­N”XmÖÓªnÑ^2Ú^,VçG$Bìž`8kѨ¤#dIœ�Q=¶Ú¦A“Zt• †»)uÉhúìHµÖ³¹°0*ÉhÁp™‘j¹–, !þ Æb W#ÈDõfD’ଉø·­� †»ka°ômÕohð_°œÖq•�IEND®B`‚dov-paps-f6dc573/po/000077500000000000000000000000001526026762000143125ustar00rootroot00000000000000dov-paps-f6dc573/po/LINGUAS000066400000000000000000000000001526026762000153250ustar00rootroot00000000000000dov-paps-f6dc573/po/Makevars000066400000000000000000000067531526026762000160210ustar00rootroot00000000000000# Makefile variables for PO directory in any package using GNU gettext. # # Copyright (C) 2003-2019 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation gives # unlimited permission to use, copy, distribute, and modify it. # Usually the message domain is the same as the package name. DOMAIN = paps # These two variables depend on the location of this directory. subdir = po top_builddir = .. # These options get passed to xgettext. XGETTEXT_OPTIONS = --keyword=_ --keyword=N_ # This is the copyright holder that gets inserted into the header of the # $(DOMAIN).pot file. Set this to the copyright holder of the surrounding # package. (Note that the msgstr strings, extracted from the package's # sources, belong to the copyright holder of the package.) Translators are # expected to transfer the copyright for their translations to this person # or entity, or to disclaim their copyright. The empty string stands for # the public domain; in this case the translators are expected to disclaim # their copyright. COPYRIGHT_HOLDER = Dov Grobgeld # This tells whether or not to prepend "GNU " prefix to the package # name that gets inserted into the header of the $(DOMAIN).pot file. # Possible values are "yes", "no", or empty. If it is empty, try to # detect it automatically by scanning the files in $(top_srcdir) for # "GNU packagename" string. PACKAGE_GNU = # This is the email address or URL to which the translators shall report # bugs in the untranslated strings: # - Strings which are not entire sentences, see the maintainer guidelines # in the GNU gettext documentation, section 'Preparing Strings'. # - Strings which use unclear terms or require additional context to be # understood. # - Strings which make invalid assumptions about notation of date, time or # money. # - Pluralisation problems. # - Incorrect English spelling. # - Incorrect formatting. # It can be your email address, or a mailing list address where translators # can write to without being subscribed, or the URL of a web page through # which the translators can contact you. MSGID_BUGS_ADDRESS = dov.grobgeld@gmail.com # This is the list of locale categories, beyond LC_MESSAGES, for which the # message catalogs shall be used. It is usually empty. EXTRA_LOCALE_CATEGORIES = # This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt' # context. Possible values are "yes" and "no". Set this to yes if the # package uses functions taking also a message context, like pgettext(), or # if in $(XGETTEXT_OPTIONS) you define keywords with a context argument. USE_MSGCTXT = no # These options get passed to msgmerge. # Useful options are in particular: # --previous to keep previous msgids of translated messages, # --quiet to reduce the verbosity. MSGMERGE_OPTIONS = # These options get passed to msginit. # If you want to disable line wrapping when writing PO files, add # --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and # MSGINIT_OPTIONS. MSGINIT_OPTIONS = # This tells whether or not to regenerate a PO file when $(DOMAIN).pot # has changed. Possible values are "yes" and "no". Set this to no if # the POT file is checked in the repository and the version control # program ignores timestamps. PO_DEPENDS_ON_POT = yes # This tells whether or not to forcibly update $(DOMAIN).pot and # regenerate PO files on "make dist". Possible values are "yes" and # "no". Set this to no if the POT file and PO files are maintained # externally. DIST_DEPENDS_ON_UPDATE_PO = yes dov-paps-f6dc573/po/POTFILES.in000066400000000000000000000000141526026762000160620ustar00rootroot00000000000000src/paps.cc dov-paps-f6dc573/po/meson.build000066400000000000000000000000431526026762000164510ustar00rootroot00000000000000i18n.gettext(meson.project_name()) dov-paps-f6dc573/scripts/000077500000000000000000000000001526026762000153635ustar00rootroot00000000000000dov-paps-f6dc573/scripts/src-to-paps000077500000000000000000000121461526026762000174650ustar00rootroot00000000000000#!/usr/bin/env python3 ###################################################################### # Use GNU source-highlight to turn source code into pango markup # and pipe to paps. # # This is currently of limited use for long lines, as the paps/pango # line breaking algorithm will insert hyphens in the text. # # 2023-01-14 Sat # Dov Grobgeld ###################################################################### import argparse import subprocess from pathlib import Path import sys import tempfile def xec(cmd, decode=True, chomp=True, verbose=False): '''Run a command a returns its stdout output. decode -- run (utf8) decode of the resulting string chomp -- get rid of the last newline (like in perl) ''' ph = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE) if verbose: sys.stderr.write(cmd + '\n') res = b'' maxsize = 1024 while True: buf = ph.stdout.read() if len(buf)==0: break res += buf ph.wait() if decode: res=res.decode(errors='ignore') if chomp: res = res[:-1] return res def arg_if_not_none(param_name, val): '''Return either an empty string or the parameter with its value''' if val is None: return '' return param_name + ' ' + val + ' ' # Defaults # TBD - Make this a configuration variable pango_outlang_path = '/usr/local/share/paps/pango_markup.outlang' parser = argparse.ArgumentParser(description='Process a file') parser.add_argument('-o', '--output', dest='output', action='store', type=str, default='-', help='Output filename') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', help='Verbose editing') parser.add_argument('--pango-outlang-path', dest='pango_outlang_path', action='store', type =str, default = pango_outlang_path, help='Path to pango_markup.outlang') parser.add_argument('-s', '--source-lang', dest='source_lang', action='store', type =str, default = None, help='Override the source lang') parser.add_argument('--style-file', dest='style_file', action='store', type =str, default = None, help='Set a style file for syntax highlight') parser.add_argument('--style-css-file', dest='style_css_file', action='store', type =str, default = None, help='Set a style file for syntax highlight') # TBD add additional paps variables, or pass them verbatim parser.add_argument('--columns', dest='columns', action='store', type =int, default = 1, help='Number of columns in paps output') parser.add_argument('--landscape', dest='landscape', action='store_true', help='Output landscape') parser.add_argument('--font', dest='font', action='store', default='', help='Paps font') parser.add_argument('filename', nargs='*', help='Input. Default is stdin') args = parser.parse_args() Output = args.output # Create the pmu file markups = [] for fn in args.filename: source_lang = ({'.py' : 'python', '.cpp' : 'C', '.cxx' : 'C', '.cc' : 'C', '.c' : 'C' }.get(Path(fn).suffix) if args.source_lang is None else args.source_lang) markups.append( xec(f'source-highlight ' + (f'-s {source_lang} ' if source_lang is not None else '') + arg_if_not_none('--style-file', args.style_file) + arg_if_not_none('--style-css-file', args.style_css_file) + f'--outlang-def {args.pango_outlang_path} ' f'--input {fn}', verbose=args.verbose)) with tempfile.TemporaryDirectory() as tmp: pmu_file = Path(tmp) / 'src-to-paps.pmu' with open(pmu_file,'w') as ofh: ofh.write('----'.join(markups)) title =','.join(args.filename) output_args = f'-o {Output} ' if Output!='-' else '' res = xec(f'paps --header ' f'{"--landscape " if args.landscape else ""}' f'--columns {args.columns} ' f'--separation-lines ' f'--markup ' f'--header-center "{title}" ' + (f' --font "{args.font}" ' if len else "") + f'--header-left "{{now:%c}}" ' f'--header-right="Page {{page_idx}}/{{num_pages}}" ' '--format pdf ' '--wrap=char ' '--show-wrap ' f'{output_args}' f'{pmu_file} ', verbose=args.verbose, decode=False, chomp=False) # This is how to write binary data to stdout sys.stdout.buffer.write(res) dov-paps-f6dc573/src/000077500000000000000000000000001526026762000144635ustar00rootroot00000000000000dov-paps-f6dc573/src/Makefile.am000066400000000000000000000010541526026762000165170ustar00rootroot00000000000000man_MANS = paps.1 bin_PROGRAMS = paps paps_CXXFLAGS = $(WARN_CFLAGS) $(PANGO_CFLAGS) $(FMT_CFLAGS) paps_SOURCES = paps.cc format_from_dict.cc markup_balance.cc markup_balance.h paps_LDADD = $(WARN_LDFLAGS) $(PANGO_LIBS) $(FMT_LIBS) $(lib_LIBRARIES) $(all_libraries) noinst_HEADERS = format_from_dict.h paps_DEPENDENCIES = $(lib_LIBRARIES) AM_CPPFLAGS = -DGETTEXT_PACKAGE='"$(GETTEXT_PACKAGE)"' -DDATADIR='"$(datadir)"' EXTRA_DIST = paps.1 meson.build test_markup_balance.cc test_markup_cpi.sh test_inkscape_multipage.sh -include $(top_srcdir)/git.mk dov-paps-f6dc573/src/SConstruct000066400000000000000000000006541526026762000165220ustar00rootroot00000000000000env = Environment() env.ParseConfig('pkg-config --cflags --libs pangocairo pangoft2') env.Append(CFLAGS=['-Wall','-g'], CPPDEFINES=['ENABLE_NLS', 'GETTEXT_PACKAGE=\\"paps\\"', 'DATADIR=\\".\\"'], LINKFLAGS=[#'-fsanitize=address', # Use for fsanitize ] ) env.Program('paps', ['paps.cc'], ) dov-paps-f6dc573/src/format_from_dict.cc000066400000000000000000000076321526026762000203200ustar00rootroot00000000000000/* * paps.cc: A postscript printing program using pango. * * Copyright (C) 2002, 2005, 2022 Dov Grobgeld * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #include "format_from_dict.h" #include #include using namespace std; using namespace fmt; static string scalar_to_string(scalar_t scalar, const string& spec="") { if (holds_alternative(scalar)) { auto val = get(scalar); if (!spec.length()) return val; return format(runtime(format("{{:{}}}", spec)), val); } if (holds_alternative(scalar)) { auto val = get(scalar); if (!spec.length()) return to_string(val); return format(runtime(format("{{:{}}}", spec)), val); } if (holds_alternative(scalar)) { auto val = get(scalar); if (!spec.length()) return to_string(val); return format(runtime(format("{{:{}}}", spec)), val); } if (holds_alternative(scalar)) { time_t val = get(scalar).time; if (!spec.length()) return to_string(val); const auto *tm = std::localtime(&val); if (tm == nullptr) return {}; // Use the environment locale so that %A, %c etc. are localized, as // strftime would. fmt >= 11 requires the "L" modifier for this, while // fmt 10 localizes when given a locale and rejects the modifier. #if FMT_VERSION >= 110000 string lspec = spec[0] == 'L' ? spec : "L" + spec; #else string lspec = spec[0] == 'L' ? spec.substr(1) : spec; #endif std::locale loc; try { loc = std::locale(""); } catch (const std::runtime_error&) { // Invalid locale in the environment. Fall back to the classic one. } return format(loc, runtime(format("{{:{}}}", lspec)), *tm); } throw runtime_error("Unrecognized type!"); // I shouldn't be here! } // Take a python like format string and a dictionary and format // it according to the format string. string format_from_dict(const string& str, dict_t dict) { string res; // Currently no support for double {{ and double }} int pos=0; size_t len = str.size(); while (true) { size_t start = str.find("{", pos); if (start == string::npos || start == len-1) break; if (str[start+1]=='{') { res += str.substr(pos, start+1); pos = start+1; continue; } size_t end = str.find("}", start); if (end == string::npos) throw runtime_error(format("No end brace for start {{ at {}", start)); if (end < len-1 && str[end+1] == '}') throw runtime_error(format("Can't have double }}}} in formatting clause!", start)); res += str.substr(pos, start-pos); string spec = str.substr(start+1, end-start-1); size_t colon_pos = spec.find(":"); if (colon_pos == string::npos) { if (dict.count(spec) == 0) throw runtime_error(format("Can't find {} in dictionary!", spec)); res += scalar_to_string(dict[spec]); } else { // Split on ':' string spec_format = spec.substr(colon_pos+1); spec = spec.substr(0, colon_pos); res += scalar_to_string(dict[spec], spec_format); } pos = end+1; } res += str.substr(pos); return res; } dov-paps-f6dc573/src/format_from_dict.h000066400000000000000000000027021526026762000201530ustar00rootroot00000000000000/* * paps.cc: A postscript printing program using pango. * * Copyright (C) 2002, 2005, 2022 Dov Grobgeld * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #ifndef FORMAT_FROM_DICT_H #define FORMAT_FROM_DICT_H #include #include #include #include // Embed the time to disambiguate on systems where time_t==int struct paps_time_t { std::time_t time; }; using scalar_t = std::variant; using dict_t = std::map; // Take a python like format string and a dictionary and format // it according to the format string. std::string format_from_dict(const std::string& str, dict_t dict); #endif /* FORMAT_FROM_DICT */ dov-paps-f6dc573/src/markup_balance.cc000066400000000000000000000100201526026762000177270ustar00rootroot00000000000000#include #include #include #include #include #include "markup_balance.h" /* Pango markup is parsed one line at a time, so an element that spans * several lines would be unbalanced on each of them. Close every open * element at the end of each line and reopen it at the start of the next. */ std::string balance_markup_per_line(const char *text) { std::string out; std::vector> open; // name, full open tag const char *p = text; while (*p) { if (*p == '\n' || *p == '\r' || *p == '\f') { // close in reverse order before the line break for (auto it = open.rbegin(); it != open.rend(); ++it) out += "first + ">"; // keep \r\n together out += *p++; if (p[-1] == '\r' && *p == '\n') out += *p++; for (auto &o : open) out += o.second; continue; } if (*p != '<') { out += *p++; continue; } // Find the end of the tag, honoring quoted attribute values const char *q = p + 1; char quote = 0; while (*q && (quote || *q != '>')) { if (quote) { if (*q == quote) quote = 0; } else if (*q == '"' || *q == '\'') quote = *q; q++; } if (!*q) { out += p; // unterminated tag, let pango complain break; } std::string tag(p, q + 1 - p); p = q + 1; out += tag; if (tag.size() < 3 || tag[1] == '!' || tag[1] == '?') continue; if (tag[1] == '/') { if (!open.empty()) open.pop_back(); } else if (tag[tag.size() - 2] != '/') { size_t e = 1; while (e < tag.size() && !isspace((unsigned char)tag[e]) && tag[e] != '>') e++; open.emplace_back(tag.substr(1, e - 1), tag); } } return out; } // Display width of a unicode code point; non-printable counts as 0. static int cp_width(unsigned cp) { int w = wcwidth((wchar_t)cp); return w < 0 ? 0 : w; } std::string wrap_markup_to_columns(const char *text, size_t cols) { std::string out; size_t width = 0; const unsigned char *p = (const unsigned char *)text; while (*p) { if (*p == '\n' || *p == '\r' || *p == '\f') { out += *p++; width = 0; continue; } if (*p == '<') { // Tags take no columns. Honor quoted attribute values. const unsigned char *q = p + 1; char quote = 0; while (*q && (quote || *q != '>')) { if (quote) { if (*q == quote) quote = 0; } else if (*q == '"' || *q == '\'') quote = *q; q++; } if (*q) q++; out.append((const char *)p, q - p); p = q; continue; } // One visible character: an entity, or a UTF-8 sequence const unsigned char *q = p; unsigned cp = *p; if (*p == '&') { const unsigned char *e = p + 1; while (*e && *e != ';' && *e != '<' && *e != '&' && *e > ' ') e++; if (*e == ';') { q = e + 1; cp = 'x'; // entities are a single column } else q = p + 1; } else { int n = cp < 0x80 ? 1 : cp >= 0xf0 ? 4 : cp >= 0xe0 ? 3 : 2; cp = n == 1 ? cp : cp & (0xff >> (n + 1)); q = p + 1; for (int i = 1; i < n && (*q & 0xc0) == 0x80; i++) cp = (cp << 6) | (*q++ & 0x3f); } int w = cp_width(cp); if (width > 0 && width + w > cols) { out += '\n'; width = 0; } width += w; out.append((const char *)p, q - p); p = q; } return out; } dov-paps-f6dc573/src/markup_balance.h000066400000000000000000000010511526026762000175750ustar00rootroot00000000000000#pragma once #include #include // Make Pango markup that spans several lines valid on each line by closing // open elements at every line break and reopening them on the next line. std::string balance_markup_per_line(const char *text); // Insert line breaks so that no line has more than `cols` visible columns. // Tags take no columns and an entity (&) takes one. Apply this before // balance_markup_per_line() so that elements are re-opened on the new lines. std::string wrap_markup_to_columns(const char *text, size_t cols); dov-paps-f6dc573/src/meson.build000066400000000000000000000046731526026762000166370ustar00rootroot00000000000000paps_config = configuration_data() paps_config.set('configure_input', 'paps-config.h file generated by Meson') paps_config.set('PACKAGE', meson.project_name()) paps_config.set('PACKAGE_NAME', 'paps') paps_config.set('PACKAGE_BUGREPORT', 'https://github.com/paps/paps/issues/new') paps_config.set('PAPS_VERSION', meson.project_version()) version_arr = meson.project_version().split('.') paps_config.set('PAPS_MAJOR_VERSION', version_arr[0].to_int()) paps_config.set('PAPS_MINOR_VERSION', version_arr[1].to_int()) paps_config.set('PAPS_MICRO_VERSION', version_arr[2].to_int()) paps_config.set('SIZEOF_INT', cc.sizeof('int')) if cc.get_id() == 'msvc' paps_config.set('PAPS_MSVC_BUILD_PLACEHOLDER', '#define PAPS_BUILT_WITH_MSVC') else paps_config.set('PAPS_MSVC_BUILD_PLACEHOLDER', '#undef PAPS_BUILT_WITH_MSVC') endif #paps_config_h = configure_file(input: files('paps-config.h.in'), # output: 'paps-config.h', # configuration: paps_config, # install_dir: join_paths(get_option('includedir'), 'paps')) paps = executable('paps', ['paps.cc', 'format_from_dict.cc', 'markup_balance.cc'], c_args: ['-DHAVE_CONFIG_H'], include_directories: incs, dependencies : [pango_dep, cairo_dep, glib_dep, gobject_dep, fmt_dep, libpaper], install: true) install_man('paps.1') test_format_from_dict = executable('test_format_from_dict', ['test_format_from_dict.cc', 'format_from_dict.cc'], include_directories: incs, dependencies : [fmt_dep]) test('format_from_dict', test_format_from_dict) test_markup_balance = executable('test_markup_balance', ['test_markup_balance.cc', 'markup_balance.cc'], include_directories: incs, dependencies : [pango_dep, glib_dep]) test('markup_balance', test_markup_balance) test('markup_cpi', find_program('test_markup_cpi.sh'), args: [paps]) test('inkscape_multipage', find_program('test_inkscape_multipage.sh'), args: [paps]) test('booklet', find_program('test_booklet.sh'), args: [paps]) dov-paps-f6dc573/src/paps.1000066400000000000000000000305601526026762000155140ustar00rootroot00000000000000.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH PAPS 1 "December 20, 2022" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME paps \- text to PostScript, PDF and SVG converter using Pango .SH SYNOPSIS .B paps .RI [ options ] " files" ... .SH DESCRIPTION .B paps reads an input file and writes a PostScript language or user specified format rendering of the file to standard output. paps accepts international text in any locale and provides internationalized text layout including text shaping and bidirectional text rendering. .P If no filename argument is provided, paps reads the standard input. If the standard input is a terminal, input is terminated by an EOF signal, usually Control-d. .SH OPTIONS .B paps follows the usual GNU command line syntax, with long options starting with two dashes (`-'). A summary of options is included below. .TP .B \-h, \-\-help Show summary of options. .TP .B \-\-landscape Landscape output. Default is portrait. For PostScript output the page keeps the physical (portrait) paper dimensions and the landscape orientation is signaled with the DSC comments \fB%%Orientation\fR and \fB%%PageOrientation\fR, which viewers use to rotate the page. For PDF and SVG output the page width and height are swapped. .TP .B \-\-columns=cl Format output into \fIcl\fR columns. Default is 1. .br Please notice this option isn't related to the terminal length as in a "80 columns terminal". .TP .B \-\-font=desc Use font based on description \fIdesc\fR. Default is \fB"Monospace 12"\fR. The format of the string representation is: .IP "[family-list] [style-options] [size]" .IP where \fIfamily-list\fR is a comma separated list of families optionally terminated by a comma, \fIstyle-options\fR is a whitespace separated list of words where each \fIword\fR describes one of style, variant, weight, or stretch, and \fIsize\fR is a decimal number for size in points, e.g. "Courier,Monospace Bold Italic 10". .TP .B \-o, \-\-output=file Output file. Default is \fBstdout\fR. Output format is set based on \fIfile\fR's extension when \-\-format is not provided. .TP .B \-\-rtl Do right-to-left (RTL) text layout and align text to the right. Text direction is detected automatically. Use this option for explicit RTL layout and right alignment. .TP .B \-\-booklet Print in booklet order. Implies \fB\-\-landscape\fR and \fB\-\-columns=2\fR, so that each column is a booklet page. The pages are imposed so that the printed sheets, printed duplex and folded, form booklets of \fB\-\-signature\fR pages each. The header and footer are drawn per column, with the booklet page number. With \fB\-\-rtl\fR the booklet is bound on the right. .TP .B \-\-signature=NUM Number of booklet pages per signature. Must be a multiple of 4. Default is 16. The last signature is padded with blank pages to whole sheets. .TP .B \-\-justify Justify the layout. Stretch complete lines to fill the entire width. .TP .B \-\-wrap=wm Use line wrapping mode \fIwm\fR. Default is \fBword\-char\fR. .IP .sp .ne 2 .mk .na \fBword\fR .ad .RS 18n .rt Wrap lines at word boundaries .RE .IP .sp .ne 3 .mk .na \fBchar\fR .ad .RS 18n .rt Wrap lines at character boundaries .RE .IP .sp .ne 3 .mk .na \fBword-char\fR .ad .RS 18n .rt Wrap lines at word boundaries, but fall back to character boundaries if there is not enough space for a full word .RE .TP .B \-\-show\-wrap Mark wrapped lines with special characters. .TP .B \-\-paper=ps Choose paper size. Accepted paper sizes are 'legal', 'letter', 'A3', 'A4', and 'A5'. Default is \fBA4\fR. .TP .B \-\-gravity=gr Set base glyph orientation. Default is \fBauto\fR. .IP .sp .ne 3 .mk .na \fBsouth\fR .ad .RS 18n .rt Glyphs stand upright .RE .IP .sp .ne 3 .mk .na \fBeast\fR .ad .RS 18n .rt Glyphs are rotated 90 degrees clockwise .RE .IP .sp .ne 3 .mk .na \fBnorth\fR .ad .RS 18n .rt Glyphs are upside-down .RE .IP .sp .ne 3 .mk .na \fBwest\fR .ad .RS 18n .rt Glyphs are rotated 90 degrees counter-clockwise .RE .IP .sp .ne 3 .mk .na \fBauto\fR .ad .RS 18n .rt Gravity is resolved from the context .RE .TP .B \-\-gravity-hint=gh Set how horizontal scripts behave in a vertical context. Default is \fBnatural\fR. .IP .sp .ne 3 .mk .na \fBnatural\fR .ad .RS 18n .rt Scripts will take their natural gravity based on the base gravity and the script .RE .IP .sp .ne 3 .mk .na \fBstrong\fR .ad .RS 18n .rt Always use the base gravity, regardless of the script .RE .IP .sp .ne 3 .mk .na \fBline\fR .ad .RS 18n .rt For scripts not in their natural direction (e.g. Latin in East gravity), choose per-script gravity such that every script respects the line progression. This means, Latin and Arabic will take opposite gravities and both flow top-to-bottom for example. .RE .TP .B \-\-format=fmt Choose output format. Default is \fBps\fR. .IP .sp .ne 2 .mk .na \fBps\fR .ad .RS 18n .rt PostScript .RE .IP .sp .ne 2 .mk .na \fBpdf\fR .ad .RS 18n .rt Portable Document Format .RE .IP .sp .ne 2 .mk .na \fBsvg\fR .ad .RS 18n .rt Scalable Vector Graphics .RE .TP .B Postscript points Each postscript point equals to 1/72 of an inch. 36 points are 1/2 of an inch. .TP .B \-\-bottom-margin=bm Set bottom margin. Default is 36 postscript points. .TP .B \-\-top-margin=tm Set top margin. Default is 36 postscript points. .TP .B \-\-left-margin=lm Set left margin. Default is 36 postscript points. .TP .B \-\-right-margin=rm Set right margin. Default is 36 postscript points. .TP .B \-\-gutter-width=gw Set gutter width. Default is 40 postscript points. .TP .B \-\-header Draw page header with document name, date and page number for each page. Header is not printed by default. .TP .B \-\-header-left=fmt Sets the formatting for the left side of the header. See FORMAT for an explanation of the header and footer formatting. Default is the current localized date. .TP .B \-\-header-center=fmt Sets the formatting for the center of the header. See FORMAT for an explanation of the header and footer formatting. Default is the filename. .TP .B \-\-header-right=fmt Sets the formatting for the center of the header. See FORMAT for an explanation of the header and footer formatting. Default is the Page number. .TP .B \-\-footer Draw page footer with document name, date and page number for each page. Footer is not printed by default. .TP .B \-\-footer-left=fmt Sets the formatting for the left side of the footer. See FORMAT for an explanation of the footer and footer formatting. The default formatting is an empty string. .TP .B \-\-footer-center=fmt Sets the formatting for the center of the footer. See FORMAT for an explanation of the footer and footer formatting. The default formatting is an empty string. .TP .B \-\-footer-right=fmt Sets the formatting for the center of the footer. See FORMAT for an explanation of the footer and footer formatting. The default formatting is an empty string. .TP .B \-\-title="text" Use \fItext\fR as the title string for page header. By default the input filename or "stdin" is used. .TP .B \-\-markup Interpret input as pango markup. Pango Text Attribute Markup Language allows marking parts of the text with tags defining additional attributes such as font face, size, weight, colors or text decoration such as underline or strikethrough. .TP .B \-\-encoding=enc Assume encoding of the input text is \fIenc\fR. By default the encoding of the current locale is used (e.g. UTF-8). .TP .B \-\-lpi=lines Set number of lines per inch. This determines the line spacing. .TP .B \-\-cpi=chars Set number of characters per inch. This is an alternative method of specifying the font size. .TP .B \-\-geometry=COLSxROWS Set the number of characters per line and the number of lines per column. The corresponding \fB\-\-cpi\fR and line spacing are computed from the page size, the margins, the number of columns and the header, so that exactly \fICOLS\fR characters fit on a line and \fIROWS\fR lines fit in a column. Either number may be omitted, e.g. \fB80x\fR or \fBx60\fR. A geometry overrides \fB\-\-cpi\fR (for COLS) and \fB\-\-lpi\fR (for ROWS). If the font is too tall for the row height it is scaled down so that lines do not overlap. .TP .B \-\-inkscape-multipage With SVG output, place all pages side by side on one canvas and describe them as Inkscape pages, so that Inkscape 1.2 or later opens the file as a multipage document. Without this option cairo's non-standard SVG 1.2 \fBpageSet\fR structure is written, which Inkscape does not understand. .TP .B \-\-page-gap=NUM Gap in points between pages with \fB\-\-inkscape-multipage\fR. (Default: 20) .TP .B \-\-g-fatal-warnings Make all glib warnings fatal. .br .SH EXIT STATUS .sp .LP The following exit values are returned: .sp .ne 2 .mk .na \fB0\fR .ad .RS 6n .rt Successful completion. .RE .sp .ne 2 .mk .na \fB1\fR .ad .RS 6n .rt An error occurred. .RE .SH HEADER AND FOOTER FORMATTING .sp .LP The header and footers may be formatted by a mini language based on the python f-strings. Text outside of squiggly brackets are entered literally in the output. Text inside squiggly brackets contain one of the following predefined list of variables: .IP .sp .ne 2 .mk .na \fBnow\fR .ad .RS 18n .rt The current time .RE .IP .sp .ne 2 .mk .na \fBmtime\fR .ad .RS 18n .rt The modification time of the file being printed .RE .IP .sp .ne 2 .mk .na \fBpage_idx\fR .ad .RS 18n .rt The page index of the page .RE .IP .sp .ne 2 .mk .na \fBnum_pages\fR .ad .RS 18n .rt The total number of pages in the document .RE .IP .sp .ne 2 .mk .na \fBfilename\fR .ad .RS 18n .rt The filename (basename) of the document .RE .IP .sp .ne 2 .mk .na \fBpath\fR .ad .RS 18n .rt The full path of the document .RE .SH EXAMPLES .LP \fBExample 1\fR Printing UTF-8 text file .P The following command can be used to print a file in any of the UTF-8 based locales if the file is in UTF-8 or compatible codeset. .sp .in +2 .nf $ \fBpaps en_US_UTF-8.txt\fR .fi .in -2 .LP By default paps will print PostScript rendering to standard output. Send the output to a printer using \fBlp\fR .sp .in +2 .nf $ \fBpaps en_US_UTF-8.txt | lp\fR .fi .in -2 .LP .sp or to a file using redirection or the \fB-o\fR option .sp .in +2 .nf $ \fBpaps en_US_UTF-8.txt > out.ps\fR $ \fBpaps -o out.ps en_US_UTF-8.txt\fR .fi .in -2 .sp .LP .sp .LP \fBExample 2\fR Specify encoding .P To print a file in specific encoding regardless of the current locale setting use the \-\-encoding option. An example for Japanese EUC encoded input file: .sp .in +2 .nf $ \fBpaps --encoding eucjp ja_JP_eucjp.txt > out.ps\fR .fi .in -2 .LP paps will still use current locale setting to prioritize the available fonts for current language. .LP \fBExample 3\fR Specify locale .P Override the \fBLC_ALL\fR environment variable to run paps in a different locale. .sp .in +2 .nf $ \fBLC_ALL=ja_JP.eucjp paps ja_JP_eucjp.txt > out.ps\fR .fi .in -2 .LP Here paps will assume the input is in Japanese EUC encoding and will use Japanese eucjp locale to render the output. If \-\-header is added, the date is printed in Japanese. .LP \fBExample 4\fR Change the header and footer .P Override the header and the footer to show only the page in the middle of the footer. .sp .in +2 .nf $ \fBpaps --separation-lines -o /tmp/foo.pdf --header --header-left='' \\ --header-center='{path}' --header-right='' --footer \\ --footer-left='Printed {now:%c}' \\ --footer-right='Page {page_idx}/{num_pages}' \\ `pwd`/paps.cc\fR .fi .in -2 .LP .SH ENVIRONMENT VARIABLES .B paps uses locale environment variables to determine its behavior. The following categories are used: .sp .ne 2 .mk .na \fBLC_CTYPE\fR .ad .RS 16n .rt to assume the encoding of the input. This can be overridden by \-\-encoding. .RE .sp .ne 2 .mk .na \fBLC_TIME\fR .ad .RS 16n .rt to format the date for header. .RE Font selection is also affected by current locale. Example 3 describes how to run paps in a different locale. .SH SEE ALSO .sp .LP \fBfc\-match\fR(1), \fBsetlocale\fR(3C) .sp .LP .SH AUTHOR paps was written by Dov Grobgeld . .PP This manual page was written by Lior Kaplan , for the Debian project (but may be used by others). dov-paps-f6dc573/src/paps.cc000066400000000000000000002177771526026762000157620ustar00rootroot00000000000000/* Pango * paps.cc: A postscript printing program using pango. * * Copyright (C) 2002, 2005, 2022 Dov Grobgeld * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "format_from_dict.h" #include "markup_balance.h" #include #include using namespace std; #ifndef _XOPEN_SOURCE #define _XOPEN_SOURCE /* for wcwidth */ // int wcwidth(wchar_t c); #endif #include #ifdef ENABLE_NLS #include #define _(str) gettext(str) #ifdef gettext_noop #define N_(str) gettext_noop(str) #else #define N_(str) (str) #endif #else /* NLS is disabled */ #define _(str) (str) #define N_(str) (str) #endif #define BUFSIZE 1024 #define DEFAULT_FONT_FAMILY "Monospace" #define DEFAULT_FONT_SIZE "12" #define HEADER_FONT_FAMILY "Monospace Bold" #define HEADER_FONT_SCALE "12" #define MAKE_FONT_NAME(f,s) f " " s /* * Cairo sets limit on the comment line for cairo_ps_surface_dsc_comment() to * 255 characters, including the initial percent characters. */ #define CAIRO_COMMENT_MAX 255 #define MARGIN_LEFT 36 #define MARGIN_RIGHT 36 #define MARGIN_TOP 36 #define MARGIN_BOTTOM 36 typedef enum { FORMAT_POSTSCRIPT = 0, FORMAT_PDF = 1, FORMAT_SVG = 2 } output_format_t ; typedef struct { double width; double height; } paper_size_t; struct PageLayout { ~PageLayout() { }; int column_width; int column_height; int num_columns; int gutter_width; /* These are all in postscript points=1/72 inch... */ int top_margin; int bottom_margin; int left_margin; int right_margin; int num_pages; double page_width; double page_height; int header_ypos; int header_sep; int header_height; int footer_height; const struct paper *paper_type = NULL; gdouble scale_x; gdouble scale_y; gdouble stretch_y; // Per-line y-scale of glyphs for --stretch-chars bool do_draw_header; bool do_draw_footer; bool do_duplex; bool do_tumble; bool do_landscape; bool do_justify; bool do_show_hyphens; bool do_draw_separation_line; bool do_draw_contour; bool do_show_wrap; bool do_use_markup; bool do_stretch_chars; bool do_booklet; /* Impose columns as booklet signatures */ int signature; /* Booklet pages per signature (multiple of 4) */ PangoDirection pango_dir; string title; string filename_path; string filename; char* header_left = nullptr; char* header_center = nullptr; char* header_right = nullptr; char* footer_left = nullptr; char* footer_center = nullptr; char* footer_right = nullptr; string header_font_desc; gdouble lpi; gdouble cpi; int geometry_cols; /* Requested characters per column line. 0 if unset */ int geometry_rows; /* Requested lines per column. 0 if unset */ dict_t document_info; }; struct LineLink { PangoLayoutLine *pango_line; PangoRectangle logical_rect; PangoRectangle ink_rect; int formfeed; bool wrapped; // Whether the paragraph was character wrapped }; typedef struct _Paragraph Paragraph; /* Structure representing a paragraph */ struct _Paragraph { const char *text; int length; int height; /* Height, in pixels */ int formfeed; bool wrapped; bool clipped; // Whether the line was clipped. Used for CPI. PangoLayout *layout; }; /* Information passed in user data when drawing outlines */ static GList *split_paragraphs_into_lines (PageLayout *page_layout, GList *paragraphs); static char *read_file (FILE *file, gchar *encoding); static GList *split_text_into_paragraphs (PangoContext *pango_context, PageLayout *page_layout, int paint_width, const char *text); static int output_pages (cairo_surface_t * surface, cairo_t *cr, GList *pango_lines, PageLayout *page_layout, PangoContext *pango_context); static void eject_column (cairo_t *cr, double title_height, PageLayout *page_layout, int column_idx, bool measure_only); static void eject_page (cairo_t *cr); static void start_page (cairo_surface_t *surface, cairo_t *cr, PageLayout *page_layout, bool measure_only); static void draw_line_to_page (cairo_t *cr, int column_idx, int column_pos, PageLayout *page_layout, PangoLayoutLine *line, bool draw_wrap_character); static int draw_page_header_line_to_page(cairo_t *cr, bool is_footer, PageLayout *page_layout, PangoContext *ctx, int page, int num_pages, dict_t& document_info, bool measure_only, int column_idx = -1); static void postscript_dsc_comments (cairo_surface_t *surface, PageLayout *page_layout); static void build_document_info (PageLayout* page_layout, dict_t& document_info); bool copy_pango_parse_enum (GType type, const char *str, int *value, bool warn, char **possible_values); static char* get_encoding(void); string get_date(); string fn_basename(const string& filename); FILE *output_fh; static const struct paper *paper_type; static bool output_format_set = false; static output_format_t output_format = FORMAT_POSTSCRIPT; static PangoGravity gravity = PANGO_GRAVITY_AUTO; static PangoGravityHint gravity_hint = PANGO_GRAVITY_HINT_NATURAL; static PangoWrapMode opt_wrap = PANGO_WRAP_WORD_CHAR; static cairo_font_face_t *paps_glyph_face = NULL; /* Special face for paps characters, e.g. newline */ static double glyph_font_size = -1; /* Render function for paps glyphs */ static cairo_status_t paps_render_glyph(cairo_scaled_font_t *scaled_font G_GNUC_UNUSED, unsigned long glyph, cairo_t *cr, cairo_text_extents_t *extents) { char ch = (unsigned char)glyph; if (ch == 'R' || ch == 'L') { // A newline sign that I created with MetaPost cairo_save(cr); cairo_scale(cr,0.005,-0.005); // TBD - figure out the scaling. if (ch == 'L') { cairo_scale(cr,-1,1); // cairo_translate(cr,-120,0); // Keep glyph protruding to the right. } cairo_translate(cr, 20,-50); cairo_move_to(cr, 0, 175); cairo_curve_to(cr, 25.69278, 175, 53.912, 177.59557, 71.25053, 158.75053); cairo_curve_to(cr, 103.52599, 123.67075, 64.54437, 77.19373, 34.99985, 34.99985); cairo_set_line_width(cr, 25); cairo_stroke(cr); cairo_move_to(cr,0,0); cairo_line_to(cr,75,0); cairo_line_to(cr,0,75); cairo_close_path(cr); cairo_fill(cr); cairo_restore(cr); } return CAIRO_STATUS_SUCCESS; } static bool _paps_arg_paper_cb(const char *option_name, const char *value, gpointer data) { bool retval = true; if (value && *value) { const struct paper *newpaper = paperinfo(value); if (newpaper != NULL) paper_type = newpaper; else { retval = false; fprintf(stderr, _("Unknown page size name: %s.\n"), value); } } else { fprintf(stderr, _("You must specify page size.\n")); retval = false; } return retval; } static bool parse_int (const char *word, int *out) { char *end; long val; int i; if (word == NULL) return false; val = strtol (word, &end, 10); i = val; if (end != word && *end == '\0' && val >= 0 && val == i) { if (out) *out = i; return true; } return false; } // A local copy of the deprecated pango_parse_enum. bool copy_pango_parse_enum (GType type, const char *str, int *value, bool warn, char **possible_values) { GEnumClass *klass = nullptr; bool ret = true; GEnumValue *v = nullptr; klass = (GEnumClass*)g_type_class_ref (type); if (G_LIKELY (str)) v = g_enum_get_value_by_nick (klass, str); if (v) { if (G_LIKELY (value)) *value = v->value; } else if (!parse_int (str, value)) { ret = false; if (G_LIKELY (warn || possible_values)) { int i; GString *s = g_string_new (nullptr); gchar *gstr; for (i = 0, v = g_enum_get_value (klass, i); v; i++ , v = g_enum_get_value (klass, i)) { if (i) g_string_append_c (s, '/'); g_string_append (s, v->value_nick); } if (warn) g_warning ("%s must be one of %s", G_ENUM_CLASS_TYPE_NAME(klass), s->str); gstr = g_string_free (s, possible_values ? false : true); if (possible_values) *possible_values = gstr; } } g_type_class_unref (klass); return ret; } static bool parse_enum (GType type, int *value, const char *name, const char *arg, gpointer data G_GNUC_UNUSED, GError **error) { char *possible_values = nullptr; bool ret; ret = copy_pango_parse_enum (type, arg, value, false, &possible_values); if (!ret && error) { g_set_error(error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, _("Argument for %1$s must be one of %2$s"), name, possible_values); } g_free (possible_values); return ret; } static bool parse_wrap (const char *name, const char *arg, gpointer data, GError **error) { return (parse_enum (PANGO_TYPE_WRAP_MODE, (int*)(void*)&opt_wrap, name, arg, data, error)); } static bool parse_gravity_hint (const char *name, const char *arg, gpointer data, GError **error) { return (parse_enum (PANGO_TYPE_GRAVITY_HINT, (int*)(void*)&gravity_hint, name, arg, data, error)); } static bool parse_gravity (const char *name, const char *arg, gpointer data, GError **error) { return (parse_enum (PANGO_TYPE_GRAVITY, (int*)(void*)&gravity, name, arg, data, error)); } static bool _paps_arg_format_cb(const char *option_name, const char *value, gpointer data) { bool retval = true; if (value && *value) { output_format_set = true; if (g_ascii_strcasecmp(value, "pdf") == 0) output_format = FORMAT_PDF; else if (g_ascii_strcasecmp(value, "ps") == 0 || g_ascii_strcasecmp(value, "postscript") == 0) output_format = FORMAT_POSTSCRIPT; else if (g_ascii_strcasecmp(value, "svg") == 0) output_format = FORMAT_SVG; else { retval = false; fprintf(stderr, _("Unknown output format: %s.\n"), value); } } else { fprintf(stderr, _("You must specify a output format.\n")); retval = false; } return retval; } static bool _paps_arg_lpi_cb(const gchar *option_name, const gchar *value, gpointer data) { bool retval = true; gchar *p = nullptr; PageLayout *page_layout = (PageLayout*)data; if (value && *value) { errno = 0; page_layout->lpi = g_strtod(value, &p); if ((p && *p) || errno == ERANGE) { fprintf(stderr, _("Given LPI value was invalid.\n")); retval = false; } } else { fprintf(stderr, _("You must specify the amount of lines per inch.\n")); retval = false; } return retval; } static bool _paps_arg_cpi_cb(const gchar *option_name, const gchar *value, gpointer data) { bool retval = true; gchar *p = nullptr; PageLayout *page_layout = (PageLayout*)data; if (value && *value) { errno = 0; page_layout->cpi = g_strtod(value, &p); if ((p && *p) || errno == ERANGE) { fprintf(stderr, _("Given CPI value was invalid.\n")); retval = false; } } else { fprintf(stderr, _("You must specify the amount of characters per inch.\n")); retval = false; } return retval; } /* * Parse a geometry string of the form COLSxROWS. Either number may be * omitted, e.g. "80x" or "x60". */ static bool _paps_arg_geometry_cb(const gchar *option_name, const gchar *value, gpointer data) { PageLayout *page_layout = (PageLayout*)data; gchar *p = nullptr; long cols = 0, rows = 0; if (value && *value) { const gchar *x = strpbrk(value, "xX"); if (x) { errno = 0; if (x > value) cols = strtol(value, &p, 10); if (x > value && (p != x || errno == ERANGE)) x = nullptr; else if (x[1]) { rows = strtol(x + 1, &p, 10); if (*p || errno == ERANGE) x = nullptr; } } if (x && cols >= 0 && rows >= 0 && (cols > 0 || rows > 0)) { page_layout->geometry_cols = (int)cols; page_layout->geometry_rows = (int)rows; return true; } } fprintf(stderr, _("Given geometry was invalid. Expected COLSxROWS.\n")); return false; } /* * Return codeset name of the environment's locale. Use UTF8 by default */ static char* get_encoding(void) { static char *encoding = nullptr; if (encoding == nullptr) encoding = nl_langinfo(CODESET); return encoding; } /* When non-null the cairo output is collected here instead of being written directly, so that it can be post-processed. */ static GString *svg_buffer = nullptr; static cairo_status_t paps_cairo_write_func(void *closure G_GNUC_UNUSED, const unsigned char *data, unsigned int length) { if (svg_buffer) g_string_append_len(svg_buffer, (const gchar *)data, length); else fwrite(data,length,1,output_fh); return CAIRO_STATUS_SUCCESS; } /* Convert the SVG 1.2 / structure that cairo emits into Inkscape's multipage format: all pages are placed side by side on one canvas and listed as in the namedview. */ static void write_inkscape_multipage(const GString *in, double page_w, double page_h, double gap) { const char *page_start = strstr(in->str, ""); const char *svg_start = strstr(in->str, "str, in->len, 1, output_fh); return; } const char *svg_end = strchr(svg_start, '>'); /* Count pages and rewrite the body */ GString *body = g_string_new(nullptr); GString *pages = g_string_new(nullptr); const char *p = page_start + strlen(""); int n_pages = 0; while (true) { const char *open = strstr(p, ""); if (!open) break; const char *close = strstr(open, ""); if (!close) break; double x = n_pages * (page_w + gap); g_string_append_printf(body, "", n_pages + 1, x); g_string_append_len(body, open + strlen(""), close - (open + strlen(""))); g_string_append(body, "\n"); g_string_append_printf(pages, "\n", x, page_w, page_h, n_pages + 1); n_pages++; p = close + strlen(""); } double total_w = n_pages * page_w + (n_pages > 1 ? (n_pages - 1) * gap : 0); /* Everything before the root tag (xml header), then a new root tag and the original */ fwrite(in->str, svg_start - in->str, 1, output_fh); fprintf(output_fh, "\n", total_w, page_h, total_w, page_h); fwrite(svg_end + 1, page_start - (svg_end + 1), 1, output_fh); fprintf(output_fh, "\n%s\n", pages->str); fwrite(body->str, body->len, 1, output_fh); fprintf(output_fh, "\n"); g_string_free(body, TRUE); g_string_free(pages, TRUE); } int main(int argc, char *argv[]) { gboolean do_landscape = false, do_rtl = false, do_justify = false, do_show_hyphens=false, do_draw_header = false, do_draw_footer=false; gboolean do_stretch_chars = false; gboolean do_draw_separation_line = false; gboolean do_use_markup = false; gboolean do_show_wrap = false; /* Whether to show wrap characters */ gboolean do_inkscape_multipage = false; int page_gap = 20; gboolean do_booklet = false; int signature = 16; gboolean do_show_version = false; // Show version and exit int num_columns = 1; int top_margin = MARGIN_TOP, bottom_margin = MARGIN_BOTTOM, right_margin = MARGIN_RIGHT, left_margin = MARGIN_LEFT; int gutter_width = 40; gboolean do_fatal_warnings = false; const gchar *font = MAKE_FONT_NAME (DEFAULT_FONT_FAMILY, DEFAULT_FONT_SIZE); gchar *encoding = nullptr; gchar *output = nullptr; gchar *htitle = nullptr; gchar *header_left = nullptr; gchar *header_center = nullptr; gchar *header_right = nullptr; gchar *footer_left = nullptr; gchar *footer_center = nullptr; gchar *footer_right = nullptr; PageLayout page_layout; GOptionContext *ctxt = g_option_context_new("[text file]"); GOptionEntry entries[] = { {"landscape", 0, 0, G_OPTION_ARG_NONE, &do_landscape, N_("Landscape output. (Default: portrait)"), nullptr}, {"columns", 0, 0, G_OPTION_ARG_INT, &num_columns, N_("Number of columns output. (Default: 1)"), "NUM"}, {"font", 0, 0, G_OPTION_ARG_STRING, &font, N_("Set font. (Default: Monospace 12)"), "DESC"}, {"output", 'o', 0, G_OPTION_ARG_STRING, &output, N_("Output file. (Default: stdout)"), "DESC"}, {"version", 'v', 0, G_OPTION_ARG_NONE, &do_show_version, N_("Current version."), nullptr}, {"rtl", 0, 0, G_OPTION_ARG_NONE, &do_rtl, N_("Do right-to-left text layout."), nullptr}, {"separation-lines", 0, 0, G_OPTION_ARG_NONE, &do_draw_separation_line, N_("Do draw separation lines between columns and headers and footers."), nullptr}, {"justify", 0, 0, G_OPTION_ARG_NONE, &do_justify, N_("Justify the layout."), nullptr}, {"hyphens", 0, 0, G_OPTION_ARG_NONE, &do_show_hyphens, N_("Show hyphens when wrapping."), nullptr}, {"wrap", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)&parse_wrap, N_("Text wrapping mode [word, char, word-char]. (Default: word-char)"), "WRAP"}, {"show-wrap", 0, 0, G_OPTION_ARG_NONE, &do_show_wrap, N_("Show characters for wrapping."), nullptr}, {"paper", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)_paps_arg_paper_cb, N_("Set paper size [legal, letter, a3, a4, a5]. (Default: a4)"), "PAPER"}, {"gravity", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)&parse_gravity, N_("Base glyph rotation [south, west, north, east, auto]. (Defaut: auto)"), "GRAVITY"}, {"gravity-hint", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)&parse_gravity_hint, N_("Base glyph orientation [natural, strong, line]. (Default: natural)"), "HINT"}, {"format", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)_paps_arg_format_cb, N_("Set output format [pdf, svg, ps]. (Default: ps)"), "FORMAT"}, {"inkscape-multipage", 0, 0, G_OPTION_ARG_NONE, &do_inkscape_multipage, N_("With SVG output, write all pages side by side in Inkscape's multipage format."), nullptr}, {"booklet", 0, 0, G_OPTION_ARG_NONE, &do_booklet, N_("Print two-column landscape pages in booklet order, with a header and footer per column. Implies --landscape and --columns=2."), nullptr}, {"signature", 0, 0, G_OPTION_ARG_INT, &signature, N_("Number of booklet pages per signature, a multiple of 4. (Default: 16)"), "NUM"}, {"page-gap", 0, 0, G_OPTION_ARG_INT, &page_gap, N_("Set gap between pages with --inkscape-multipage. (Default: 20)"), "NUM"}, {"bottom-margin", 0, 0, G_OPTION_ARG_INT, &bottom_margin, N_("Set bottom margin in postscript point units (1/72 inch). (Default: 36)"), "NUM"}, {"top-margin", 0, 0, G_OPTION_ARG_INT, &top_margin, N_("Set top margin. (Default: 36)"), "NUM"}, {"right-margin", 0, 0, G_OPTION_ARG_INT, &right_margin, N_("Set right margin. (Default: 36)"), "NUM"}, {"left-margin", 0, 0, G_OPTION_ARG_INT, &left_margin, N_("Set left margin. (Default: 36)"), "NUM"}, {"gutter-width", 0, 0, G_OPTION_ARG_INT, &gutter_width, N_("Set gutter width. (Default: 40)"), "NUM"}, {"header", 0, 0, G_OPTION_ARG_NONE, &do_draw_header, N_("Draw page header for each page."), nullptr}, {"footer", 0, 0, G_OPTION_ARG_NONE, &do_draw_footer, "Draw page footer for each page.", nullptr}, {"title", 0, 0, G_OPTION_ARG_STRING, &htitle, N_("Title string for page header (Default: filename/stdin)."), "TITLE"}, {"header-left", 0, 0, G_OPTION_ARG_STRING, &header_left, N_("Left side of the header. Default is localized date."), "HEADER_LEFT"}, {"header-center", 0, 0, G_OPTION_ARG_STRING, &header_center, N_("Center side of the header. Default is localized date."), "HEADER_CENTER"}, {"header-right", 0, 0, G_OPTION_ARG_STRING, &header_right, N_("Right side of the header. Default is localized date."), "HEADER_RIGHT"}, {"footer-left", 0, 0, G_OPTION_ARG_STRING, &footer_left, N_("Left side of the footer. Default is localized date."), "FOOTER_LEFT"}, {"footer-center", 0, 0, G_OPTION_ARG_STRING, &footer_center, N_("Center side of the footer. Default is localized date."), "FOOTER_CENTER"}, {"footer-right", 0, 0, G_OPTION_ARG_STRING, &footer_right, N_("Right side of the footer. Default is localized date."), "FOOTER_RIGHT"}, {"markup", 0, 0, G_OPTION_ARG_NONE, &do_use_markup, N_("Interpret input text as pango markup."), nullptr}, {"encoding", 0, 0, G_OPTION_ARG_STRING, &encoding, N_("Assume encoding of input text. (Default: UTF-8)"), "ENCODING"}, {"lpi", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)_paps_arg_lpi_cb, N_("Set the amount of lines per inch."), "REAL"}, {"cpi", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)_paps_arg_cpi_cb, N_("Set the amount of characters per inch."), "REAL"}, {"geometry", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer)_paps_arg_geometry_cb, N_("Set the number of characters per line and lines per column as COLSxROWS. Computes --cpi and --lpi from the page size and margins."), "COLSxROWS"}, {"stretch-chars", 0, 0, G_OPTION_ARG_NONE, &do_stretch_chars, N_("Stretch characters in y-direction to fill lines."), nullptr}, {"g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &do_fatal_warnings, N_("Make all glib warnings fatal."), NULL}, {nullptr} }; GError *error = nullptr; FILE *IN = nullptr; GList *paragraphs; GList *pango_lines; PangoContext *pango_context; PangoFontDescription *font_description; PangoDirection pango_dir = PANGO_DIRECTION_LTR; PangoFontMap *fontmap; PangoFontset *fontset; PangoFontMetrics *metrics; int total_gutter_width; int do_tumble = -1; /* -1 means not initialized */ int do_duplex = -1; const gchar *header_font_desc = MAKE_FONT_NAME (HEADER_FONT_FAMILY, HEADER_FONT_SCALE); const gchar *filename_in; gchar *text; /* int header_sep = 20; */ int max_width = 0, w; GOptionGroup *options; cairo_t *cr; cairo_surface_t *surface = nullptr; double surface_page_width = 0, surface_page_height = 0; paperinit(); /* Set locale from environment */ (void) setlocale(LC_ALL, ""); /* Setup i18n */ #ifdef ENABLE_NLS textdomain(GETTEXT_PACKAGE); bindtextdomain(GETTEXT_PACKAGE, DATADIR "/locale"); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); #endif /* Setup the paps glyph face */ paps_glyph_face = cairo_user_font_face_create(); cairo_user_font_face_set_render_glyph_func(paps_glyph_face, paps_render_glyph); /* Init PageLayout parameters set by the option parsing */ page_layout.cpi = page_layout.lpi = 0.0L; page_layout.geometry_cols = page_layout.geometry_rows = 0; options = g_option_group_new("main","","",&page_layout, nullptr); g_option_group_add_entries(options, entries); g_option_group_set_translation_domain(options, GETTEXT_PACKAGE); g_option_context_set_main_group(ctxt, options); #if 0 g_option_context_add_main_entries(ctxt, entries, nullptr); #endif /* Parse command line */ if (!g_option_context_parse(ctxt, &argc, &argv, &error)) { fprintf(stderr, _("Command line error: %s\n"), error->message); exit(1); } if (do_show_version) { printf("Version: %s\n", PACKAGE_STRING); exit(0); } if (do_fatal_warnings) g_log_set_always_fatal(G_LOG_LEVEL_MASK); if (do_rtl) pango_dir = PANGO_DIRECTION_RTL; if (do_booklet) { if (num_columns != 1 && num_columns != 2) fprintf(stderr, _("%s: --booklet requires two columns, ignoring --columns=%d.\n"), g_get_prgname (), num_columns); num_columns = 2; do_landscape = true; if (signature <= 0) { fprintf(stderr, _("%s: Invalid input: --signature=%d, using default.\n"), g_get_prgname (), signature); signature = 16; } if (signature % 4) { signature += 4 - signature % 4; fprintf(stderr, _("%s: --signature must be a multiple of 4, using %d.\n"), g_get_prgname (), signature); } } page_layout.header_left = header_left; page_layout.header_center = header_center; page_layout.header_right = header_right; page_layout.footer_left = footer_left; page_layout.footer_center = footer_center; page_layout.footer_right = footer_right; page_layout.do_draw_header = do_draw_header; page_layout.do_draw_footer = do_draw_footer; if (argc > 1) { filename_in = argv[1]; IN = fopen(filename_in, "r"); if (!IN) { fprintf(stderr, _("Failed to open %s!\n"), filename_in); exit(1); } } else { filename_in = "stdin"; IN = stdin; } // For now always write to stdout if (output == nullptr) output_fh = stdout; else { output_fh = fopen(output,"wb"); if (!output_fh) { fprintf(stderr, _("Failed to open %s for writing!\n"), output); exit(1); } } // If we have no explicit paper size, get the default. if (paper_type == NULL) { const char *paper_name = defaultpapername(); if (paper_name == NULL) { fprintf(stderr, _("No page size given, and no default paper size found\n")); exit(1); } paper_type = paperinfo(paper_name); } /* Page layout */ double page_width = paperpswidth(paper_type); double page_height = paperpsheight(paper_type); /* Deduce output format from file name if not explicitely set */ if (!output_format_set && output != nullptr) { if (g_str_has_suffix(output, ".svg") || g_str_has_suffix(output, ".SVG")) output_format = FORMAT_SVG; else if (g_str_has_suffix(output, ".pdf") || g_str_has_suffix(output, ".PDF")) output_format = FORMAT_PDF; /* Otherwise keep postscript default */ } /* Swap width and height for landscape except for postscript. A postscript page keeps the physical paper dimensions, like a printer's paper, and landscape is signaled by the DSC orientation comments, which viewers use to rotate the page. */ surface_page_width = page_width; surface_page_height = page_height; if (do_inkscape_multipage && output_format != FORMAT_SVG) { fprintf(stderr, _("--inkscape-multipage is only valid with SVG output\n")); exit(1); } if (do_inkscape_multipage) svg_buffer = g_string_new(nullptr); if (output_format != FORMAT_POSTSCRIPT && do_landscape) { surface_page_width = page_height; surface_page_height = page_width; } if (output_format == FORMAT_POSTSCRIPT) surface = cairo_ps_surface_create_for_stream(&paps_cairo_write_func, nullptr, surface_page_width, surface_page_height); else if (output_format == FORMAT_PDF) surface = cairo_pdf_surface_create_for_stream(&paps_cairo_write_func, nullptr, surface_page_width, surface_page_height); else surface = cairo_svg_surface_create_for_stream(&paps_cairo_write_func, nullptr, surface_page_width, surface_page_height); cr = cairo_create(surface); pango_context = pango_cairo_create_context(cr); pango_cairo_context_set_resolution(pango_context, 72.0); /* Native postscript resolution */ /* Setup pango */ pango_context_set_base_dir (pango_context, pango_dir); pango_context_set_language (pango_context, pango_language_get_default ()); pango_context_set_base_gravity (pango_context, gravity); pango_context_set_gravity_hint (pango_context, gravity_hint); /* create the font description */ font_description = pango_font_description_from_string (font); if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_FAMILY) == 0) pango_font_description_set_family (font_description, DEFAULT_FONT_FAMILY); if ((pango_font_description_get_set_fields (font_description) & PANGO_FONT_MASK_SIZE) == 0) pango_font_description_set_size (font_description, atoi(DEFAULT_FONT_SIZE) * PANGO_SCALE); // Keep the font size for the wrap character. glyph_font_size = pango_font_description_get_size(font_description) / PANGO_SCALE; pango_context_set_font_description (pango_context, font_description); if (num_columns <= 0) { fprintf(stderr, _("%s: Invalid input: --columns=%d, using default.\n"), g_get_prgname (), num_columns); num_columns = 1; } if (num_columns == 1) total_gutter_width = 0; else total_gutter_width = gutter_width * (num_columns - 1); if (do_landscape) { double tmp; tmp = page_width; page_width = page_height; page_height = tmp; if (do_tumble < 0) do_tumble = true; if (do_duplex < 0) do_duplex = true; } else { if (do_tumble < 0) do_tumble = true; if (do_duplex < 0) do_duplex = true; } page_layout.page_width = page_width; page_layout.page_height = page_height; page_layout.paper_type = paper_type; page_layout.num_columns = num_columns; page_layout.left_margin = left_margin; page_layout.right_margin = right_margin; page_layout.gutter_width = gutter_width; page_layout.top_margin = top_margin; page_layout.bottom_margin = bottom_margin; page_layout.header_ypos = page_layout.top_margin; page_layout.header_height = 0; page_layout.footer_height = 0; page_layout.do_show_wrap = do_show_wrap; page_layout.scale_x = 1.0L; page_layout.scale_y = 1.0L; page_layout.stretch_y = 1.0L; page_layout.header_sep = 0; // header_sep; page_layout.column_height = (int)page_height - page_layout.top_margin - page_layout.header_sep - page_layout.bottom_margin; page_layout.column_width = ((int)page_layout.page_width - page_layout.left_margin - page_layout.right_margin - total_gutter_width) / page_layout.num_columns; page_layout.do_draw_separation_line = do_draw_separation_line; page_layout.do_landscape = do_landscape; page_layout.do_justify = do_justify; page_layout.do_show_hyphens = do_show_hyphens; page_layout.do_stretch_chars = do_stretch_chars; page_layout.do_use_markup = do_use_markup; page_layout.do_booklet = do_booklet; page_layout.signature = signature; page_layout.do_tumble = do_tumble; page_layout.do_duplex = do_duplex; page_layout.pango_dir = pango_dir; page_layout.filename_path = filename_in; page_layout.filename = fn_basename(filename_in); if (htitle) page_layout.title = htitle; else page_layout.title = fn_basename(filename_in); page_layout.header_font_desc = header_font_desc; /* A geometry overrides --cpi. Rows are handled in output_pages() since they depend on the header height. */ if (page_layout.geometry_cols > 0) page_layout.cpi = page_layout.geometry_cols * 72.0 / page_layout.column_width; /* calculate x-coordinate scale */ if (page_layout.cpi > 0.0L) { gint font_size; fontmap = pango_ft2_font_map_new (); fontset = pango_font_map_load_fontset (fontmap, pango_context, font_description, pango_language_get_default()); metrics = pango_fontset_get_metrics (fontset); max_width = pango_font_metrics_get_approximate_char_width (metrics); w = pango_font_metrics_get_approximate_digit_width (metrics); if (w > max_width) max_width = w; page_layout.scale_x = 1.0L / page_layout.cpi * 72.0 * (gdouble)PANGO_SCALE / (gdouble)max_width; pango_font_metrics_unref (metrics); g_object_unref (G_OBJECT (fontset)); g_object_unref (G_OBJECT (fontmap)); font_size = pango_font_description_get_size (font_description); // update the font size to that width pango_font_description_set_size (font_description, (int)(font_size * page_layout.scale_x)); glyph_font_size = font_size * page_layout.scale_x / PANGO_SCALE; pango_context_set_font_description (pango_context, font_description); } /* With a geometry the font must also be small enough for the lines to fit in the row height, or the lines would overlap. Only shrink the font. The width then stays at the requested number of columns per line. */ if (page_layout.geometry_rows > 0) { double header_height = 0; double row_height, line_height; gint font_size; if (do_draw_header) { PangoLayout *header_layout = pango_layout_new(pango_context); PangoRectangle logical_rect; gchar *header_markup = g_markup_printf_escaped("Xg", header_font_desc); pango_layout_set_markup(header_layout, header_markup, -1); pango_layout_line_get_extents(pango_layout_get_line(header_layout, 0), nullptr, &logical_rect); header_height = (double)logical_rect.height / PANGO_SCALE; g_free(header_markup); g_object_unref(header_layout); } row_height = (page_layout.column_height - header_height) / page_layout.geometry_rows; fontmap = pango_ft2_font_map_new (); fontset = pango_font_map_load_fontset (fontmap, pango_context, font_description, pango_language_get_default()); metrics = pango_fontset_get_metrics (fontset); line_height = (double)(pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)) / PANGO_SCALE; pango_font_metrics_unref (metrics); g_object_unref (G_OBJECT (fontset)); g_object_unref (G_OBJECT (fontmap)); if (line_height > row_height && row_height > 0) { double scale = row_height / line_height; font_size = pango_font_description_get_size (font_description); pango_font_description_set_size (font_description, (int)(font_size * scale)); glyph_font_size *= scale; pango_context_set_font_description (pango_context, font_description); } } page_layout.scale_x = page_layout.scale_y = 1.0; if (encoding == nullptr) encoding = get_encoding(); text = read_file(IN, encoding); if (page_layout.do_use_markup) { // With --cpi long lines are clipped to the number of columns. Do it // here on the visible text, since tags take no columns. std::string wrapped; if (page_layout.cpi > 0.0L) { size_t col = (size_t)(page_layout.column_width / 72.0 * page_layout.cpi); wrapped = wrap_markup_to_columns(text, col); } else wrapped = text; std::string balanced = balance_markup_per_line(wrapped.c_str()); g_free(text); text = g_strdup(balanced.c_str()); } if (output_format == FORMAT_POSTSCRIPT) postscript_dsc_comments(surface, &page_layout); paragraphs = split_text_into_paragraphs(pango_context, &page_layout, page_layout.column_width, text); pango_lines = split_paragraphs_into_lines(&page_layout, paragraphs); cairo_scale(cr, page_layout.scale_x, page_layout.scale_y); output_pages(surface, cr, pango_lines, &page_layout, pango_context); g_list_free_full(pango_lines, g_free); for (GList *l = paragraphs; l; l = l->next) { Paragraph *para = (Paragraph*)l->data; g_object_unref(para->layout); g_free(para); } g_list_free(paragraphs); g_free(text); g_object_unref(pango_context); pango_font_description_free(font_description); pango_cairo_font_map_set_default(nullptr); /* release the default font map */ cairo_destroy (cr); cairo_surface_finish (surface); cairo_surface_destroy(surface); if (svg_buffer) { write_inkscape_multipage(svg_buffer, surface_page_width, surface_page_height, page_gap); g_string_free(svg_buffer, TRUE); svg_buffer = nullptr; } g_option_context_free(ctxt); return 0; } /* Read an entire file into a string */ static char * read_file (FILE *file, gchar *encoding) { GString *inbuf; char *text; char buffer[BUFSIZE]; GIConv cvh = nullptr; gsize inc_seq_bytes = 0; if (encoding != nullptr) { cvh = g_iconv_open ("UTF-8", encoding); if (cvh == (GIConv)-1) { fprintf(stderr, _("%s: Invalid encoding: %s\n"), g_get_prgname (), encoding); exit(1); } } inbuf = g_string_new (nullptr); while (1) { char *ib, *ob, obuffer[BUFSIZE * 6], *bp; gsize iblen, oblen; bp = fgets (buffer+inc_seq_bytes, BUFSIZE-inc_seq_bytes-1, file); if (inc_seq_bytes) inc_seq_bytes = 0; if (ferror (file)) { fprintf(stderr, _("%s: Error reading file.\n"), g_get_prgname ()); (void) g_string_free (inbuf, true); exit(1); } else if (bp == nullptr) break; if (cvh != nullptr) { ib = buffer; iblen = strlen (ib); ob = bp = obuffer; oblen = BUFSIZE * 6 - 1; if (g_iconv (cvh, &ib, &iblen, &ob, &oblen) == (gsize)-1) { /* * EINVAL - incomplete sequence at the end of the buffer. Move the * incomplete sequence bytes to the beginning of the buffer for * the next round of conversion. */ if (errno == EINVAL) { inc_seq_bytes = iblen; memmove (buffer, ib, inc_seq_bytes); } else { fprintf (stderr, _("%1$s: Error while converting input from '%2$s' to UTF-8.\n"), g_get_prgname(), encoding); exit(1); } } obuffer[BUFSIZE * 6 - 1 - oblen] = 0; } g_string_append (inbuf, bp); } fclose (file); /* Add a trailing new line if it is missing */ if (inbuf->len && inbuf->str[inbuf->len-1] != '\n') g_string_append(inbuf, "\n"); text = g_string_free (inbuf, false); if (encoding != nullptr && cvh != nullptr) g_iconv_close(cvh); return text; } // Turn off the use of hyphens static void layout_turn_off_hyphens(PangoLayout *layout) { // Request not to get any hypens PangoAttrList *attrs = pango_layout_get_attributes(layout); bool created = (attrs == NULL); if (created) attrs = pango_attr_list_new (); pango_attr_list_insert(attrs, pango_attr_insert_hyphens_new(FALSE)); pango_layout_set_attributes (layout, attrs); if (created) pango_attr_list_unref (attrs); /* the layout holds its own reference */ } /* Take a UTF8 string and break it into paragraphs on \n characters */ static GList * split_text_into_paragraphs (PangoContext *pango_context, PageLayout *page_layout, int paint_width, /* In pixels */ const char *text) { const char *p = text; char *next; gunichar wc; GList *result = nullptr; const char *last_para = text; /* If we are using markup we treat the entire text as a single paragraph. * I tested it and found that this is much slower than the split and * assign method used below. Otherwise we might as well use this single * chunk method always. * * I don't think we need this at all, and are better of reading one * line at a time... */ #if 0 if (0 && page_layout->do_use_markup) { Paragraph *para = g_new (Paragraph, 1); para->wrapped = false; /* No wrapped chars for markups */ para->clipped = false; para->text = text; para->length = strlen(text); para->layout = pango_layout_new (pango_context); pango_layout_set_attributes (para->layout, attrs); pango_layout_set_markup (para->layout, para->text, para->length); pango_layout_set_justify (para->layout, page_layout->do_justify); pango_layout_set_alignment (para->layout, page_layout->pango_dir == PANGO_DIRECTION_LTR ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); pango_layout_set_width (para->layout, paint_width * PANGO_SCALE); pango_layout_set_wrap (para->layout, opt_wrap); para->height = 0; result = g_list_prepend (result, para); } else #endif { while (p != nullptr && *p) { wc = g_utf8_get_char (p); next = (char *) g_utf8_next_char (p); if (wc == (gunichar)-1) { fprintf (stderr, _("%s: Invalid character in input\n"), g_get_prgname ()); wc = 0; } if (!*p || !wc || wc == '\r' || wc == '\n' || wc == '\f') { Paragraph *para = g_new (Paragraph, 1); para->wrapped = false; para->clipped = false; para->text = last_para; para->length = p - last_para; /* handle dos line breaks */ if (wc == '\r' && *next == '\n') next = (char *) g_utf8_next_char(next); para->layout = pango_layout_new (pango_context); if (page_layout->cpi > 0.0L) { /* figuring out the correct width from the pango_font_metrics_get_approximate_width() * is really hard and pango_layout_set_wrap() doesn't work properly then. * Those are not reliable to render the characters exactly according to the given CPI. * So re-calculate the width to wrap up to be comfortable with CPI. */ wchar_t *wtext = nullptr, *wnewtext = nullptr; gchar *newtext = nullptr; gsize len, col, i, wwidth = 0; PangoRectangle ink_rect, logical_rect; wtext = (wchar_t *)g_utf8_to_ucs4 (para->text, para->length, nullptr, nullptr, nullptr); if (wtext == nullptr) { fprintf (stderr, _("%s: Unable to convert UTF-8 to UCS-4.\n"), g_get_prgname ()); fail: g_free (wtext); g_free (wnewtext); g_free (newtext); exit (1); } len = g_utf8_strlen (para->text, para->length); /* the amount of characters that can be put on the line against CPI */ col = (int)(page_layout->column_width / 72.0 * page_layout->cpi); if (len > col && !page_layout->do_use_markup) { /* need to wrap them up */ wnewtext = g_new (wchar_t, wcslen (wtext) + 1); para->clipped = true; if (wnewtext == nullptr) { fprintf (stderr, _("%s: Unable to allocate the memory.\n"), g_get_prgname ()); goto fail; } for (i = 0; i < len; i++) { gssize w = wcwidth (wtext[i]); if (w >= 0) wwidth += w; if (wwidth > col) break; wnewtext[i] = wtext[i]; } wnewtext[i] = 0L; newtext = g_ucs4_to_utf8 ((const gunichar *)wnewtext, i, nullptr, nullptr, nullptr); if (newtext == nullptr) { fprintf (stderr, _("%s: Unable to convert UCS-4 to UTF-8.\n"), g_get_prgname ()); goto fail; } if (page_layout->do_use_markup) pango_layout_set_markup (para->layout, newtext, -1); else pango_layout_set_text (para->layout, newtext, -1); // Request not to get any hypens if (!page_layout->do_show_hyphens) layout_turn_off_hyphens(para->layout); pango_layout_get_extents (para->layout, &ink_rect, &logical_rect); paint_width = logical_rect.width / PANGO_SCALE; g_free (wnewtext); g_free (newtext); para->length = i; next = (char *) g_utf8_offset_to_pointer (para->text, para->length); wc = g_utf8_get_char (g_utf8_prev_char (next)); } else { if (page_layout->do_use_markup) pango_layout_set_markup (para->layout, para->text, para->length); else pango_layout_set_text (para->layout, para->text, para->length); } g_free (wtext); pango_layout_set_width (para->layout, -1); } else { if (page_layout->do_use_markup) pango_layout_set_markup (para->layout, para->text, para->length); else pango_layout_set_text (para->layout, para->text, para->length); pango_layout_set_width (para->layout, paint_width * PANGO_SCALE); pango_layout_set_wrap (para->layout, opt_wrap); if (!page_layout->do_show_hyphens) layout_turn_off_hyphens(para->layout); if (opt_wrap == PANGO_WRAP_CHAR) para->wrapped = true; /* Should we support truncation as well? */ } pango_layout_set_justify (para->layout, page_layout->do_justify); pango_layout_set_alignment (para->layout, page_layout->pango_dir == PANGO_DIRECTION_LTR ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT); para->height = 0; last_para = next; if (wc == '\f') para->formfeed = 1; else para->formfeed = 0; result = g_list_prepend (result, para); } if (!wc) /* incomplete character at end */ break; p = next; } } return g_list_reverse (result); } /* Split a list of paragraphs into a list of lines. */ GList * split_paragraphs_into_lines(PageLayout *page_layout, GList *paragraphs) { GList *line_list = nullptr; int max_height = 0; /* Read the file */ /* Now split all the pagraphs into lines */ GList *par_list; par_list = paragraphs; while(par_list) { int para_num_lines, i; LineLink *line_link; Paragraph *para = (Paragraph*)par_list->data; para_num_lines = pango_layout_get_line_count(para->layout); for (i=0; iformfeed = 0; line_link->wrapped = (para->wrapped && i < para_num_lines - 1) || (para->clipped); line_link->pango_line = pango_layout_get_line(para->layout, i); pango_layout_line_get_extents(line_link->pango_line, &ink_rect, &logical_rect); line_link->logical_rect = logical_rect; if (para->formfeed && i == (para_num_lines - 1)) line_link->formfeed = 1; line_link->ink_rect = ink_rect; line_list = g_list_prepend(line_list, line_link); if (logical_rect.height > max_height) max_height = logical_rect.height; } par_list = par_list->next; } // Stretched in draw_line_to_page(), since scaling the cairo context // would also scale the row positions. if (page_layout->do_stretch_chars && page_layout->lpi > 0.0L && max_height > 0) page_layout->stretch_y = 1.0 / page_layout->lpi * 72.0 * PANGO_SCALE / max_height; return g_list_reverse(line_list); } /* * Define PostScript document header information. */ void postscript_dsc_comments(cairo_surface_t *surface, PageLayout *pl) { char buf[CAIRO_COMMENT_MAX]; int x, y; /* * Title */ snprintf(buf, CAIRO_COMMENT_MAX, "%%%%Title: %s", pl->title.c_str()); cairo_ps_surface_dsc_comment (surface, buf); /* * Orientation */ if (pl->do_landscape) { cairo_ps_surface_dsc_comment (surface, "%%Orientation: Landscape"); x = (int)pl->page_height; y = (int)pl->page_width; } else { cairo_ps_surface_dsc_comment (surface, "%%Orientation: Portrait"); x = (int)pl->page_width; y = (int)pl->page_height; } /* * Redefine BoundingBox to cover the whole paper. Cairo creates the entry * based on the text only. This may affect further processing, such as with * convert(1). */ snprintf(buf, CAIRO_COMMENT_MAX, "%%%%BoundingBox: 0 0 %d %d", x, y); cairo_ps_surface_dsc_comment (surface, buf); /* * Duplex */ if (pl->do_duplex) { cairo_ps_surface_dsc_comment(surface, "%%Requirements: duplex"); cairo_ps_surface_dsc_begin_setup(surface); if (pl->do_tumble) cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexTumble"); else cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *Duplex DuplexNoTumble"); } else cairo_ps_surface_dsc_begin_setup(surface); gchar *comment = g_strdup_printf("%%IncludeFeature: *PageSize %s", papername(paper_type)); if (comment == NULL) { fprintf (stderr, _("%s: Unable to allocate the memory.\n"), g_get_prgname ()); exit(1); } cairo_ps_surface_dsc_comment (surface, comment); g_free (comment); } int output_pages(cairo_surface_t *surface, cairo_t *cr, GList *pango_lines, PageLayout *page_layout, PangoContext *pango_context) { int pango_column_height = page_layout->column_height * PANGO_SCALE; int row_height = 0; // Fixed line height in pango units. 0 means use natural height int title_height = 0; int num_columns = page_layout->num_columns; dict_t document_info; // Fill in the static document info build_document_info(page_layout, document_info); document_info["num_pages"] = 0; document_info["page_idx"] = 1; // Measure the header and footer. Their heights are the same on all pages. cairo_save(cr); if (page_layout->do_draw_header) title_height = draw_page_header_line_to_page(cr, false, page_layout, pango_context, 1, -1, document_info, true); if (page_layout->do_draw_footer) draw_page_header_line_to_page(cr, true, page_layout, pango_context, 1, -1, document_info, true); cairo_restore(cr); if (page_layout->geometry_rows > 0) row_height = (pango_column_height - title_height) / page_layout->geometry_rows; else if (page_layout->lpi > 0.0L) row_height = (int)(1.0 / page_layout->lpi * 72.0 * PANGO_SCALE); // Distribute the lines over the logical columns, which are the booklet // pages when printing a booklet. vector> columns(1); int column_y_pos = title_height; LineLink *prev_line_link = nullptr; for (GList *l = pango_lines; l; l = l->next) { LineLink *line_link = (LineLink*)l->data; // With a geometry the rows must fit exactly. Otherwise keep the // old behavior of breaking based on the natural line height. if ((page_layout->geometry_rows > 0 ? column_y_pos + row_height > pango_column_height : column_y_pos + line_link->logical_rect.height >= pango_column_height) || (prev_line_link && prev_line_link->formfeed)) { columns.emplace_back(); column_y_pos = title_height; } columns.back().push_back(line_link); column_y_pos += row_height > 0 ? row_height : line_link->logical_rect.height; prev_line_link = line_link; } // A booklet shouldn't get a blank last page from a trailing form feed. if (page_layout->do_booklet) while (columns.size() > 1) { bool blank = true; for (LineLink *ll : columns.back()) if (ll->ink_rect.width > 0 || ll->ink_rect.height > 0) blank = false; if (!blank) break; columns.pop_back(); } // For each output page and column slot, the logical column to draw, or // -1 for an empty slot. int num_logical = columns.size(); vector slots; if (page_layout->do_booklet) { bool rtl = page_layout->pango_dir == PANGO_DIRECTION_RTL; for (int start = 0; start < num_logical; start += page_layout->signature) { int n = min(page_layout->signature, num_logical - start); n = (n + 3) / 4 * 4; // Pad the last signature to whole sheets for (int k = 0; k < n/4; k++) { // Front and back of sheet k, as {left,right} booklet pages int sides[2][2] = {{n-1-2*k, 2*k}, {2*k+1, n-2-2*k}}; for (auto& side : sides) { // RTL booklets are bound on the right int a = rtl ? side[1] : side[0]; int b = rtl ? side[0] : side[1]; slots.push_back(a < num_logical - start ? start + a : -1); slots.push_back(b < num_logical - start ? start + b : -1); } } } } else { int n = (num_logical + num_columns - 1) / num_columns * num_columns; for (int i = 0; i < n; i++) slots.push_back(i < num_logical ? i : -1); } int num_pages = slots.size() / num_columns; document_info["num_pages"] = page_layout->do_booklet ? num_logical : num_pages; for (int i=0; i<2; i++) { bool measure_only = i==0; for (int page = 0; page < num_pages; page++) { start_page(surface, cr, page_layout, measure_only); for (int column_idx = 0; column_idx < num_columns; column_idx++) { int logical = slots[page*num_columns + column_idx]; // A booklet has a header and footer per column. Otherwise // they span the page and are drawn with the first column. bool per_column = page_layout->do_booklet; int page_no = per_column ? logical + 1 : page + 1; int header_col = per_column ? column_idx : -1; if (column_idx > 0 && (per_column || logical >= 0)) eject_column(cr, title_height/PANGO_SCALE, page_layout, column_idx, measure_only); if (logical < 0 && per_column) continue; document_info["page_idx"] = page_no; if (per_column || column_idx == 0) { if (page_layout->do_draw_header) draw_page_header_line_to_page(cr, false, page_layout, pango_context, page_no, num_pages, document_info, measure_only, header_col); if (page_layout->do_draw_footer) draw_page_header_line_to_page(cr, true, page_layout, pango_context, page_no, num_pages, document_info, measure_only, header_col); } if (logical < 0) continue; int y_pos = title_height; for (LineLink *line_link : columns[logical]) { int height = row_height > 0 ? row_height : line_link->logical_rect.height; bool draw_wrap_character = page_layout->do_show_wrap && line_link->wrapped; if (!measure_only) draw_line_to_page(cr, column_idx, y_pos+height, page_layout, line_link->pango_line, draw_wrap_character); y_pos += height; } } if (!measure_only) eject_page(cr); } } return num_pages; } void eject_column(cairo_t *cr, double title_height, PageLayout *page_layout, int column_idx, bool measure_only) { double x_pos, y_top, y_bot, total_gutter; #if 0 fprintf(stderr, "do_draw_separation_line column_idx = %d %d\n", page_layout->do_draw_separation_line, column_idx); #endif if (!page_layout->do_draw_separation_line) return; if (page_layout->pango_dir == PANGO_DIRECTION_RTL) column_idx = (page_layout->num_columns - column_idx); if (column_idx == 1) total_gutter = 1.0 * page_layout->gutter_width /2; else total_gutter = (column_idx - 0.5) * page_layout->gutter_width; x_pos = page_layout->left_margin + page_layout->column_width * column_idx + total_gutter; y_top = page_layout->top_margin + page_layout->header_height + page_layout->header_sep / 2 + title_height; y_bot = page_layout->page_height - page_layout->bottom_margin - page_layout->footer_height; if (!measure_only) { cairo_move_to(cr,x_pos, y_top); cairo_line_to(cr,x_pos, y_bot); cairo_set_line_width(cr, 0.1); cairo_stroke(cr); } } void eject_page(cairo_t *cr) { cairo_show_page(cr); } void start_page(cairo_surface_t *surface, cairo_t *cr, PageLayout *page_layout, bool measure_only) { cairo_identity_matrix(cr); if (output_format == FORMAT_POSTSCRIPT) { char buf[CAIRO_COMMENT_MAX]; int x, y; if (page_layout->do_landscape) { x = (int)page_layout->page_height; y = (int)page_layout->page_width; } else { x = (int)page_layout->page_width; y = (int)page_layout->page_height; } if (!measure_only) { cairo_ps_surface_dsc_begin_page_setup (surface); snprintf(buf, CAIRO_COMMENT_MAX, "%%%%PageBoundingBox: 0 0 %d %d", x, y); cairo_ps_surface_dsc_comment (surface, buf); } } if (page_layout->do_landscape) { if (output_format == FORMAT_POSTSCRIPT) { if (!measure_only) cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Landscape"); cairo_translate(cr, 0, page_layout->page_width); cairo_rotate(cr, -G_PI_2); } } else { if (output_format == FORMAT_POSTSCRIPT) cairo_ps_surface_dsc_comment (surface, "%%PageOrientation: Portrait"); } } void draw_line_to_page(cairo_t *cr, int column_idx, int column_pos, PageLayout *page_layout, PangoLayoutLine *line, bool draw_wrap_character) { /* Assume square aspect ratio for now */ double y_pos = page_layout->top_margin + page_layout->header_sep + column_pos / PANGO_SCALE; double x_pos = page_layout->left_margin + column_idx * (page_layout->column_width + page_layout->gutter_width); PangoRectangle ink_rect, logical_rect; /* Do RTL column layout for RTL direction */ if (page_layout->pango_dir == PANGO_DIRECTION_RTL) { x_pos = page_layout->left_margin + (page_layout->num_columns-1-column_idx) * (page_layout->column_width + page_layout->gutter_width); } pango_layout_line_get_extents(line, &ink_rect, &logical_rect); if (page_layout->pango_dir == PANGO_DIRECTION_RTL) { x_pos += page_layout->column_width - logical_rect.width / PANGO_SCALE; } cairo_save(cr); cairo_translate(cr, x_pos, y_pos); cairo_scale(cr, 1.0, page_layout->stretch_y); cairo_move_to(cr, 0, 0); pango_cairo_show_layout_line(cr, line); cairo_restore(cr); if (draw_wrap_character) { cairo_set_font_face(cr, paps_glyph_face); cairo_set_font_size(cr, glyph_font_size); if (page_layout->pango_dir == PANGO_DIRECTION_LTR) { cairo_move_to(cr, x_pos + page_layout->column_width, y_pos); cairo_show_text(cr, "R"); } else { double left_margin = page_layout->left_margin + (page_layout->num_columns-1-column_idx) * (page_layout->column_width + page_layout->gutter_width); cairo_move_to(cr, left_margin, y_pos); cairo_show_text(cr, "L"); } } } /* * Provide date string from current locale converted to UTF-8. */ string get_date() { time_t t = time(nullptr); GIConv cvh = nullptr; GString *inbuf; char *ib, *ob, obuffer[BUFSIZE * 6], *bp; gsize iblen, oblen; static char *date_utf8 = nullptr; if (date_utf8 == nullptr) { char date[256]; t = time(nullptr); strftime(date, sizeof(date), "%c", localtime(&t)); cvh = g_iconv_open("UTF-8", get_encoding()); if (cvh == (GIConv)-1) { fprintf(stderr, _("%s: Invalid encoding: %s\n"), g_get_prgname(), get_encoding()); exit(1); } inbuf = g_string_new(nullptr); ib = bp = date; iblen = strlen(ib); ob = bp = obuffer; oblen = BUFSIZE * 6 - 1; if (g_iconv(cvh, &ib, &iblen, &ob, &oblen) == (gsize)-1) { fprintf(stderr, _("%1$s: Error while converting date string from '%2$s' to UTF-8.\n"), g_get_prgname(), get_encoding()); /* Return the unconverted string. */ /* * inbuf isn't used here, but a few memory is * allocated by default. so it should be freed anyway. */ (void) g_string_free(inbuf, true); g_iconv_close(cvh); return date; } obuffer[BUFSIZE * 6 - 1 - oblen] = 0; g_string_append(inbuf, bp); date_utf8 = g_string_free(inbuf, false); g_iconv_close(cvh); } return date_utf8; } int draw_page_header_line_to_page(cairo_t *cr, bool is_footer, PageLayout *page_layout, PangoContext *ctx, int page, int num_pages, dict_t& document_info, bool measure_only, int column_idx) { PangoLayout *layout = pango_layout_new(ctx); PangoLayoutLine *line; PangoRectangle ink_rect={0,0,0,0}, logical_rect = {0,0,0,0}; /* Assume square aspect ratio for now */ double x_pos, y_pos; int height; gdouble line_pos; /* Reset gravity?? */ // Three lines: // 1. Date // 2. Filename (title) // 3. Page string header = fmt::format("{}\n" "{}\n" "{}", page_layout->header_font_desc, get_date().c_str(), page_layout->header_font_desc, page_layout->title.c_str(), page_layout->header_font_desc, page ); vector header_parts = {"","",""}; if (is_footer) { if (page_layout->footer_left) header_parts[0] = page_layout->footer_left; if (page_layout->footer_center) header_parts[1] = page_layout->footer_center; if (page_layout->footer_right) header_parts[2] = page_layout->footer_right; } else { if (page_layout->header_left) header_parts[0]=page_layout->header_left; else header_parts[0]=get_date(); if (page_layout->header_center) header_parts[1] = page_layout->header_center; else header_parts[1] = page_layout->filename; if (page_layout->header_right) header_parts[2] = page_layout->header_right; else header_parts[2] = fmt::format("{}",page); } for (auto& v : header_parts) v = format_from_dict(v, document_info); header = ""; for (auto& hp : header_parts) header += fmt::format("{}\n", page_layout->header_font_desc, hp); pango_layout_set_markup(layout, header.c_str(), -1); /* The title is in the center */ line = pango_layout_get_line(layout, 0); pango_layout_line_get_extents(line, &ink_rect, &logical_rect); /* The header spans the page, or only one column if column_idx >= 0 */ double x_left = page_layout->left_margin; double span = page_layout->page_width - page_layout->left_margin - page_layout->right_margin; if (column_idx >= 0) { if (page_layout->pango_dir == PANGO_DIRECTION_RTL) column_idx = page_layout->num_columns - 1 - column_idx; x_left += column_idx * (page_layout->column_width + page_layout->gutter_width); span = page_layout->column_width; } x_pos = x_left; height = logical_rect.height / PANGO_SCALE /3.0; /* The header is placed right after the margin */ if (is_footer) { y_pos = page_layout->page_height - page_layout->bottom_margin*0.5; page_layout->footer_height = height; } else { y_pos = page_layout->top_margin + height; page_layout->header_height = height; } cairo_move_to(cr, x_pos,y_pos); if (!measure_only) pango_cairo_show_layout_line(cr,line); /* output a right edge of header/footer */ line = pango_layout_get_line(layout, 1); pango_layout_line_get_extents(line, &ink_rect, &logical_rect); /* pagenum_rect = logical_rect; */ x_pos = x_left + span*0.5 - 0.5*logical_rect.width/PANGO_SCALE; cairo_move_to(cr, x_pos,y_pos); if (!measure_only) pango_cairo_show_layout_line(cr,line); /* output a "center" of header/footer */ line = pango_layout_get_line(layout, 2); pango_layout_line_get_extents(line, &ink_rect, &logical_rect); x_pos = x_left + span - (logical_rect.width / PANGO_SCALE ); // x_pos = page_layout->page_width - page_layout->right_margin - // ((logical_rect.width + pagenum_rect.width) / PANGO_SCALE + page_layout->gutter_width); cairo_move_to(cr, x_pos,y_pos); if(!measure_only) pango_cairo_show_layout_line(cr,line); g_object_unref(layout); /* header separator */ if (!measure_only && page_layout->do_draw_separation_line) { if (is_footer) line_pos = page_layout->page_height - page_layout->bottom_margin * 1.1; else line_pos = page_layout->top_margin + page_layout->header_height + page_layout->header_sep; line_pos += logical_rect.height/2.0/PANGO_SCALE; cairo_move_to(cr, x_left, line_pos); cairo_line_to(cr, x_left + span, line_pos); cairo_set_line_width(cr,0.1); // TBD cairo_stroke(cr); } return logical_rect.height; } // Build the document info hash table that may be used in the headers // and footers static void build_document_info(PageLayout* page_layout, dict_t& document_info) { document_info["filename"] = page_layout->filename; document_info["path"] = page_layout->filename_path; GStatBuf stat_buf; if (g_stat(page_layout->filename_path.c_str(), &stat_buf) == 0) document_info["mtime"] = paps_time_t {stat_buf.st_mtime }; else document_info["mtime"] = paps_time_t {time(nullptr) }; document_info["now"] = paps_time_t { time(nullptr) }; page_layout->document_info["num_pages"] = page_layout->num_pages; } string fn_basename(const string& filename) { return basename((char*)filename.c_str()); } dov-paps-f6dc573/src/test_booklet.sh000077500000000000000000000015611526026762000175230ustar00rootroot00000000000000#!/bin/sh # End-to-end: --booklet imposes pages in signature order, also for --rtl. # Usage: test_booklet.sh /path/to/paps paps="$1" tmp=$(mktemp -d) || exit 1 trap 'rm -rf "$tmp"' EXIT for i in 1 2 3 4 5 6 7 8; do printf 'pg%s\n\f' "$i"; done > "$tmp/in.txt" if command -v pdftotext >/dev/null; then "$paps" --booklet --signature=8 --header --header-left=x --header-center=y \ --header-right='{page_idx}' --format=pdf "$tmp/in.txt" > "$tmp/o.pdf" || exit 1 got=$(for p in 1 2 3 4; do pdftotext -f $p -l $p "$tmp/o.pdf" - | grep '^pg' | tr '\n' ' '; done) want="pg8 pg1 pg2 pg7 pg6 pg3 pg4 pg5 " [ "$got" = "$want" ] || { echo "FAIL: order '$got'"; exit 1; } "$paps" --booklet --rtl --signature=8 --format=pdf "$tmp/in.txt" > "$tmp/r.pdf" || exit 1 [ "$(pdfinfo "$tmp/r.pdf" | awk '/^Pages/{print $2}')" = 4 ] || { echo "FAIL: rtl pages"; exit 1; } fi exit 0 dov-paps-f6dc573/src/test_format_from_dict.cc000066400000000000000000000021721526026762000213510ustar00rootroot00000000000000// Test that format_from_dict localizes dates according to the locale. #include #include #include #include #include #include #include "format_from_dict.h" int main() { // Use the Swedish locale. Skip the test (exit code 77) if not installed. const char *locname = "sv_SE.UTF-8"; // format_from_dict uses the environment locale, as paps does. setenv("LC_ALL", locname, 1); if (!setlocale(LC_ALL, "")) { fprintf(stderr, "Locale %s not available. Skipping.\n", locname); return 77; } // 2026-10-02 is a Friday. std::tm tm = {}; tm.tm_year = 2026 - 1900; tm.tm_mon = 9; tm.tm_mday = 2; tm.tm_hour = 12; tm.tm_isdst = -1; paps_time_t t{std::mktime(&tm)}; dict_t dict; dict["date"] = t; std::string res = format_from_dict("{date:%A}", dict); if (res != "fredag") { fprintf(stderr, "FAIL: expected 'fredag', got '%s'\n", res.c_str()); return 1; } res = format_from_dict("{date:%c}", dict); if (res.find("fre") == std::string::npos) { fprintf(stderr, "FAIL: %%c not localized: '%s'\n", res.c_str()); return 1; } return 0; } dov-paps-f6dc573/src/test_inkscape_multipage.sh000077500000000000000000000026001526026762000217230ustar00rootroot00000000000000#!/bin/sh # End-to-end: --inkscape-multipage places pages side by side with # inkscape:page entries; it is rejected for non-SVG output. # Usage: test_inkscape_multipage.sh /path/to/paps paps="$1" tmp=$(mktemp -d) || exit 1 trap 'rm -rf "$tmp"' EXIT seq 1 400 > "$tmp/in.txt" "$paps" --format=svg --inkscape-multipage --page-gap=10 "$tmp/in.txt" \ > "$tmp/out.svg" 2> "$tmp/err" if [ $? -ne 0 ] || [ -s "$tmp/err" ]; then echo "FAIL: rc/stderr"; cat "$tmp/err"; exit 1 fi pages=$(grep -c '\|' "$tmp/out.svg"; then echo "FAIL: cairo pageSet left in output"; exit 1 fi # The second page starts after the first page width plus the gap. if ! grep '-0.01) ? 0 : 1}'; then echo "FAIL: page gap"; exit 1 fi if command -v xmllint >/dev/null 2>&1; then xmllint --noout "$tmp/out.svg" || { echo "FAIL: malformed xml"; exit 1; } fi if "$paps" --format=pdf --inkscape-multipage "$tmp/in.txt" >/dev/null 2>&1; then echo "FAIL: --inkscape-multipage accepted for pdf"; exit 1 fi exit 0 dov-paps-f6dc573/src/test_markup_balance.cc000066400000000000000000000175331526026762000210060ustar00rootroot00000000000000// Test that balance_markup_per_line makes multi-line Pango markup valid // on every individual line. #include #include #include #include #include #include "markup_balance.h" static int failures = 0; static std::vector split_lines(const std::string &s) { std::vector lines; std::string cur; for (size_t i = 0; i < s.size(); i++) { char c = s[i]; if (c == '\r' && i + 1 < s.size() && s[i + 1] == '\n') continue; // \r\n is a single line break if (c == '\n' || c == '\r' || c == '\f') { lines.push_back(cur); cur.clear(); } else cur += c; } lines.push_back(cur); return lines; } static bool line_parses(const std::string &line, std::string &plain) { GError *err = nullptr; char *text = nullptr; bool ok = pango_parse_markup(line.c_str(), -1, 0, nullptr, &text, nullptr, &err); if (ok) plain = text; g_free(text); if (err) g_error_free(err); return ok; } // Every line of the balanced output must parse, and the concatenated // plain text must equal `expected_plain` (lines joined by '\n'). static void check_valid(const char *name, const char *input, const char *expected_plain) { std::string out = balance_markup_per_line(input); std::string joined; bool first = true; for (auto &line : split_lines(out)) { std::string plain; if (!line_parses(line, plain)) { fprintf(stderr, "FAIL %s: line does not parse: '%s'\n", name, line.c_str()); failures++; return; } if (!first) joined += "\n"; joined += plain; first = false; } if (joined != expected_plain) { fprintf(stderr, "FAIL %s: expected plain '%s', got '%s'\n", name, expected_plain, joined.c_str()); failures++; } } static void check_exact(const char *name, const char *input, const char *expected) { std::string out = balance_markup_per_line(input); if (out != expected) { fprintf(stderr, "FAIL %s: expected '%s', got '%s'\n", name, expected, out.c_str()); failures++; } } static void check_wrap(const char *name, const char *input, size_t cols, const char *expected) { std::string out = wrap_markup_to_columns(input, cols); if (out != expected) { fprintf(stderr, "FAIL %s: expected '%s', got '%s'\n", name, expected, out.c_str()); failures++; } } // Wrap, then balance, then require every line to parse and be at most // `cols` visible characters wide. static void check_wrap_valid(const char *name, const char *input, size_t cols, const char *expected_plain) { std::string out = balance_markup_per_line(wrap_markup_to_columns(input, cols).c_str()); std::string joined; bool first = true; for (auto &line : split_lines(out)) { std::string plain; if (!line_parses(line, plain)) { fprintf(stderr, "FAIL %s: line does not parse: '%s'\n", name, line.c_str()); failures++; return; } if (g_utf8_strlen(plain.c_str(), -1) > (glong)cols) { fprintf(stderr, "FAIL %s: line too wide: '%s'\n", name, plain.c_str()); failures++; } if (!first) joined += "\n"; joined += plain; first = false; } if (joined != expected_plain) { fprintf(stderr, "FAIL %s: expected plain '%s', got '%s'\n", name, expected_plain, joined.c_str()); failures++; } } int main() { setlocale(LC_ALL, "C.UTF-8"); // --- CPI clipping: tags take no columns --- check_wrap("wrap_plain", "abcdef\n", 3, "abc\ndef\n"); check_wrap("wrap_fits", "abc\n", 3, "abc\n"); check_wrap("wrap_tags_free", "abcdef\n", 3, "abc\ndef\n"); check_wrap("wrap_tag_attr_gt", "y\">abcd\n", 2, "y\">ab\ncd\n"); check_wrap("wrap_entity_one_col", "&<>&\n", 2, "&<\n>&\n"); check_wrap("wrap_resets_per_line", "ab\ncd\n", 2, "ab\ncd\n"); check_wrap("wrap_crlf", "ab\r\ncd", 2, "ab\r\ncd"); check_wrap("wrap_wide_chars", "漢字漢字", 4, "漢字\n漢字"); check_wrap("wrap_utf8", "שלו×� עול×�", 4, "שלו×�\n עול\n×�"); check_wrap("wrap_zero_cols_progress", "abc", 0, "a\nb\nc"); check_wrap("wrap_unterminated_tag", "ab" and a Pango warning check_wrap_valid("wrap_long_bold", "aaaaaaaaaa\n", 4, "aaaa\naaaa\naa\n"); // Wrap inside nested elements: they must be re-opened on each line check_wrap_valid("wrap_nested", "abcdefg\n", 3, "abc\ndef\ng\n"); check_wrap_valid("wrap_span_crossing", "abcde\nfgh\n", 2, "ab\ncd\ne\nfg\nh\n"); check_wrap_valid("wrap_entities", "&&&", 2, "&&\n&"); // The issue #62 case check_valid("issue62", "something\n\nsomething else\n", "something\n\nsomething else\n"); check_exact("issue62_exact", "a\nb\n", "a\nb\n"); // No markup crossing lines: output unchanged check_exact("unchanged", "a\nplain\n", "a\nplain\n"); check_exact("empty", "", ""); // Nested elements across several lines, closed in reverse order check_exact("nested", "x\ny\nz\n", "x\ny\nz\n"); check_valid("nested_partial_close", "ab\ncd\ne\n", "ab\ncd\ne\n"); // Blank lines inside an element check_valid("blank_lines", "a\n\n\nb", "a\n\n\nb"); // '>' inside a quoted attribute value must not end the tag check_valid("gt_in_attr_dq", "b\">x\ny", "x\ny"); check_valid("gt_in_attr_sq", "x\ny", "x\ny"); // A quote of the other kind inside a quoted value check_valid("mixed_quotes", "x\ny", "x\ny"); // Self-closing elements must not be treated as opened check_exact("selfclose", "a\nb\n", "a\nb\n"); check_exact("selfclose_space", "a\nb\n", "a\nb\n"); check_valid("selfclose_inside", "a\nb", "a\nb"); // Comments and processing instructions are not elements check_exact("comment", "a\nb\n", "a\nb\n"); check_exact("pi", "a\nb\n", "a\nb\n"); // Line break flavours check_valid("crlf", "a\r\nb\r\n", "a\nb\n"); check_exact("crlf_exact", "a\r\nb", "a\r\nb"); check_valid("formfeed", "a\fb", "a\nb"); check_exact("cr_only", "a\rb", "a\rb"); // Entities and escaped markup characters check_valid("entities", "a <b> &\nc", "a &\nc"); // UTF-8 text, including multi-byte characters next to tags check_valid("utf8", "שלו×�\nעול×�", "שלו×�\nעול×�"); // Element closed on the same line it is opened, then another opened check_valid("sequence", "ab\ncd\ne", "ab\ncd\ne"); // Many levels deep check_valid("deep", "a\nb\nc\nd", "a\nb\nc\nd"); // Malformed input must not crash or hang; Pango reports the error later. // Unterminated tag: balance_markup_per_line("a\nx\ny"); // Stray closing tag with nothing open: balance_markup_per_line("a\nb"); // More closes than opens balance_markup_per_line("a\nb"); // Unclosed element at end of input check_exact("unclosed_eof", "a", "a"); if (failures) { fprintf(stderr, "%d failure(s)\n", failures); return 1; } return 0; } dov-paps-f6dc573/src/test_markup_cpi.sh000077500000000000000000000013251526026762000202140ustar00rootroot00000000000000#!/bin/sh # End-to-end: --markup together with --cpi must not cut tags in half. # Usage: test_markup_cpi.sh /path/to/paps paps="$1" tmp=$(mktemp -d) || exit 1 trap 'rm -rf "$tmp"' EXIT printf '%s\nx\ny\n' \ "$(printf 'a%.0s' $(seq 1 200))" > "$tmp/in.txt" for fmt in ps pdf; do "$paps" --markup --cpi=10 --format=$fmt "$tmp/in.txt" > "$tmp/out.$fmt" 2> "$tmp/err" rc=$? if [ $rc -ne 0 ] || [ -s "$tmp/err" ]; then echo "FAIL ($fmt): rc=$rc"; cat "$tmp/err"; exit 1 fi done if command -v pdftotext >/dev/null 2>&1; then if pdftotext "$tmp/out.pdf" - | grep -q '[<>]'; then echo "FAIL: literal markup in output"; pdftotext "$tmp/out.pdf" -; exit 1 fi fi exit 0 dov-paps-f6dc573/subprojects/000077500000000000000000000000001526026762000162375ustar00rootroot00000000000000dov-paps-f6dc573/subprojects/fmt.wrap000066400000000000000000000010601526026762000177150ustar00rootroot00000000000000[wrap-file] directory = fmt-10.2.0 source_url = https://github.com/fmtlib/fmt/archive/10.2.0.tar.gz source_filename = fmt-10.2.0.tar.gz source_hash = 3ca91733a7313a8ad41c0885929415f8ec0a2a31d4dc7e27e9331412f4ca26ac patch_filename = fmt_10.2.0-2_patch.zip patch_url = https://wrapdb.mesonbuild.com/v2/fmt_10.2.0-2/get_patch patch_hash = 2428c3a386a8390c76378f81ef804a297f4edc3b789499dd56629b7902b8ddb7 source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/fmt_10.2.0-2/fmt-10.2.0.tar.gz wrapdb_version = 10.2.0-2 [provide] fmt = fmt_dep