pax_global_header00006660000000000000000000000064142042364120014510gustar00rootroot0000000000000052 comment=db3b6a3b819b730411994138906f01d974377784 ipe-tools-7.2.24.1/000077500000000000000000000000001420423641200136565ustar00rootroot00000000000000ipe-tools-7.2.24.1/.gitignore000066400000000000000000000002271420423641200156470ustar00rootroot00000000000000*.o pdftoipe/pdftoipe figtoipe/figtoipe ipe5toxml/ipe5toxml build *.AppImage AppImageKit Ipe.AppDir *.pyc *.snap test tmpipe* *.pptx *.exe *.pdf *.svg ipe-tools-7.2.24.1/README.md000066400000000000000000000046161420423641200151440ustar00rootroot00000000000000ipe-tools ========= These are various tools and helper programs to be used with the Ipe drawing editor (http://ipe.otfried.org). svgtoipe.py ----------- A script that converts an SVG figure to Ipe format. It cannot handle all SVG features (many SVG features are not supported by Ipe anyway), but it works for gradients. ipepython --------- A Python 3 extension module that will let you open and work with Ipe documents from Python. Poweripe -------- Do you prefer to create your presentations in Ipe? But your bosses/colleagues/clients keep asking for Powerpoint files? Fear no more! Poweripe is a Python script that translates an Ipe presentation into a Powerpoint presentation. Matplotlib backend ------------------ Matplotlib is a Python module for scientific plotting. With this backend, you can create Ipe figures directly from matplotlib. pdftoipe -------- You can convert arbitrary Postscript or PDF files into Ipe documents, making them editable. The auxiliary program *pdftoipe* converts (pages from) a PDF file into an Ipe XML-file. (If your source is Postscript, you have to first convert it to PDF using Acrobat Distiller or *ps2pdf*.) Once converted to XML, the file can be opened from Ipe as usual. The conversion process should handle any graphics in the PDF file fine, but doesn't do very well on text - Ipe's text model is just too different. ipe5toxml --------- If you still have figures that were created with Ipe 5, you can use *ipe5toxml* to convert them to Ipe 6 format. You can then use *ipe6upgrade* to convert them to Ipe 7 format. figtoipe -------- Figtoipe converts a figure in FIG format into an Ipe XML-file. This is useful if you used to make figures with Xfig before discovering Ipe, of if your co-authors made figures for your article with Xfig (converting them will have the added benefit of forcing your co-authors to learn to use Ipe). Finally, there are quite a number of programs that can export to FIG format, and *figtoipe* effectively turns that into the possibility of exporting to Ipe. However, *figtoipe* is not quite complete. The drawing models of FIG and Ipe are also somewhat different, which makes it impossible to properly render some FIG files in Ipe. Ipe does not support depth ordering independent of grouping, pattern fill, and Postscript fonts. You may therefore have to edit the file after conversion. *figtoipe* is now maintained by Alexander Bürger. ipe-tools-7.2.24.1/annotate/000077500000000000000000000000001420423641200154675ustar00rootroot00000000000000ipe-tools-7.2.24.1/annotate/README.md000066400000000000000000000016441420423641200167530ustar00rootroot00000000000000annotate.py ========== This Python script takes a PDF file and creates an Ipe document that shows the pages of the PDF page by page, allowing you to annotate the PDF document, for instance using ink mode on a tablet or by simply creating new text objects. To run it, you will need the *PyPDF2* library from https://github.com/mstamy2/PyPDF2. The easiest way to install *PyPDF2* is using PIP - on Linux it will suffice to say ``` apt-get install python3-pip pip3 install PyPDF2 ``` (Or, if using Python~2, install `python-pip` and call `pip`.) Run it with a PDF file as an argument: ``` python3 annotate.py doc.pdf ``` It will create the new file `annotate-doc.ipe`. When opening `annotate-doc.ipe` using Ipe, you will need to have the original PDF file `doc.pdf` available for Ipe. You either have to copy it into Ipe's Latex directory (e.g. `~/.ipe/latexrun`), or you have to set the `TEXINPUTS` environment variable. ipe-tools-7.2.24.1/annotate/annotate.py000066400000000000000000000022531420423641200176540ustar00rootroot00000000000000# # Create an Ipe file including pages of given PDF document # import sys, os import PyPDF2 as pdf # -------------------------------------------------------------------- def create_ipe(pdffile): i = pdffile.rfind("/") if i >= 0: outfile = pdffile[i+1:] else: outfile = pdffile if outfile.endswith(".pdf"): outfile = outfile[:-4] outfile = "annotate-" + outfile + ".ipe" doc = pdf.PdfFileReader(pdffile) numPages = doc.numPages out = open(outfile, "w") out.write(""" \\usepackage{graphicx} """) for i in range(numPages): out.write(""" \includegraphics[page=%d]{%s} \n""" % (i+1, pdffile)) out.write("\n") out.close() os.system("ipescript update-styles %s" % outfile) # -------------------------------------------------------------------- if len(sys.argv) != 2: sys.stderr.write("Usage: python annotate.py \n") sys.exit(9) pdffile = sys.argv[1] create_ipe(pdffile) # -------------------------------------------------------------------- ipe-tools-7.2.24.1/appimage/000077500000000000000000000000001420423641200154415ustar00rootroot00000000000000ipe-tools-7.2.24.1/appimage/Ipe.appdata.xml000066400000000000000000000032051420423641200203110ustar00rootroot00000000000000org.otfried.ipeFSFAPGPL-3.0+IpeThe Ipe extensible drawing editor ​ ​

Ipe is a drawing editor for creating figures in PDF format. It supports making small figures for inclusion into \LaTeX{}-documents as well as making multi-page PDF presentations.

Ipe's main features are:

  • Entry of text as LaTeX source code. This makes it easy to enter mathematical expressions, and to reuse the LaTeX-macros of the main document. In the display text is displayed as it will appear in the figure.
  • Produces pure PDF, including the text. Ipe converts the LaTeX-source to PDF when the file is saved.
  • It is easy to align objects with respect to each other (for instance, to place a point on the intersection of two lines, or to draw a circle through three given points) using various snapping modes.
  • Users can provide ipelets (Ipe plug-ins) to add functionality to Ipe. This way, Ipe can be extended for each task at hand.
​ ​ Ipe.desktophttp://ipe.otfried.org Otfried Cheong ​ ​
ipe-tools-7.2.24.1/appimage/Ipe.desktop000066400000000000000000000002041420423641200175450ustar00rootroot00000000000000[Desktop Entry] Type=Application Name=Ipe GenericName=drawing program Icon=ipe Exec=startipe.sh Terminal=false Categories=Graphics; ipe-tools-7.2.24.1/appimage/LICENSE000066400000000000000000001045131420423641200164520ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, 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 them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ipe-tools-7.2.24.1/appimage/README.md000066400000000000000000000040421420423641200167200ustar00rootroot00000000000000# Ipe AppImage recipe This is a recipe for building an [AppImage](http://appimage.org/) for Ipe, based on the [fantastic work of Thomas Leitz](https://github.com/unruhschuh/Ipe.AppImage) - I only tinkered with a few details and added the Voronoi ipelet and pdftoipe. Ipe requires `pdflatex` to be installed in order to render text elements. It is not included in the AppImage so you need to have a LaTeX distribution installed on your system. You can find informations about LaTeX and how to install it at [www.tug.org](https://www.tug.org/). ## Download * [Download](https://bintray.com/otfried/generic/ipe#files/ipe) the latest AppImage for Ipe * Make the AppImage executable, e.g. in the terminal run `chmod a+x ipe-x.y.z-x86_64.AppImage` ## How to use the Ipe AppImage * To start Ipe, just click on the AppImage file. * You can also start Ipe from the command line by executing the AppImage: ``` $ ./ipe-7.2.6-x86_64.AppImage ``` * If you want to use the Ipe command line tools, such as `ipetoipe`, `iperender`, `ipescript`, just prefix the command with the AppImage: ``` $ ./ipe-7.2.6-x86_64.AppImage ipetoipe -pdf ~/test.ipe ``` * If you want to start Ipe with a command line argument, proceed as for the tools: ``` $ ./ipe-7.2.6-x86_64.AppImage ipe -sheet presentation ~/mytalk.pdf ``` Note that you will have to use **absolute filenames** in the arguments to the command line tools, because the AppImage changes directory before executing the code. ## How to create an AppImage for Ipe The AppImage for Ipe is created inside a fresh installation of CentOS 6.7. In order to build the AppImage follow these steps * Install CentOS 6.7 either on a PC or inside VirtualBox or any other virtualization program. * Start the Terminal and run `git clone https://github.com/otfried/ipe-tools` * Change directory with `cd ipe-tools/appimage` * Download and compile the needed tools and libraries with `./setup.sh`. * Start Ipe compilation and packaging process with `./recipe.sh`. This creates `ipe-7.x.y-x86_64.AppImage` inside the current directory. ipe-tools-7.2.24.1/appimage/appimage.lua000066400000000000000000000001671420423641200177330ustar00rootroot00000000000000-- Special settings for the Ipe appimage prefs.browser = "unset XDG_DATA_DIRS && unset LD_LIBRARY_PATH && xdg-open %s" ipe-tools-7.2.24.1/appimage/appimage.yml000066400000000000000000000132071420423641200177520ustar00rootroot00000000000000# # Building an AppImage for Ipe in openSuse Build Service (OBS) # # Unfortunately the C++ compiler in the appimage build service is stuck with C++11 # app: ipe # binpatch: true build: # packages which get installed in the build enviroment. Not part of the resulting image. packages: - libqt5-qtbase-devel - liblua5_2 - Lua(devel) - zlib-devel - pkgconfig(libpng) - pkgconfig(libjpeg) - pkgconfig(freetype2) - pkgconfig(cairo) # - pkgconfig(poppler) # resources which will become available as part of the source. The "appimage" source service # needs to be enabled to process these. git: - https://github.com/otfried/ipe-tools.git # - $BUILD_SOURCE_DIR point to the source directory # - $BUILD_APPDIR point to the AppDir. This directory will become the content of the AppImage script: # Build qhull - cd $BUILD_SOURCE_DIR - tar xfvz qhull-2015-src-7.2.0.tgz - cd qhull-2015.2 - make - install -d /usr/include/qhull_r - install -m 0644 src/libqhull_r/*.h /usr/include/qhull_r - install -m 0755 lib/libqhullstatic_r.a /usr/lib # Build Ipe - cd $BUILD_SOURCE_DIR - tar xzf ipe-7.2.10-src.tar.gz - cd ipe-7.2.10 - cd src - export LUA_CFLAGS=`pkg-config --cflags lua` - export LUA_LIBS=`pkg-config --libs lua` - export QHULL_CFLAGS="-I/usr/include/qhull_r" - export QHULL_LIBS="-lqhullstatic_r" - export IPEPREFIX=$BUILD_APPDIR/usr - export IPEQVORONOI=1 - export MOC=moc-qt5 - make IPEAPPIMAGE=1 - make install IPEAPPIMAGE=1 - cd $BUILD_SOURCE_DIR/ipe-tools/appimage - cp ipe.png $BUILD_APPDIR - cp Ipe.desktop $BUILD_APPDIR - cp startipe.sh $BUILD_APPDIR/usr/bin - cp appimage.lua $BUILD_APPDIR/usr/ipe/ipelets - mkdir -p $BUILD_APPDIR/usr/share/metainfo - cp Ipe.appdata.xml $BUILD_APPDIR/usr/share/metainfo # Build pdftoipe # This currently doesn't work because HEAD on ipe-tools is for poppler >= 0.73 # - cd $BUILD_SOURCE_DIR/ipe-tools/pdftoipe # - make # - install -m 0755 pdftoipe $BUILD_APPDIR/usr/bin # include dependencies - mkdir -p $BUILD_APPDIR/usr/lib/qt5 - cp -R /usr/lib64/qt5/plugins $BUILD_APPDIR/usr/lib/qt5/ - set +e - ldd $BUILD_APPDIR/usr/bin/* | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' $BUILD_APPDIR/usr/lib - find $BUILD_APPDIR/usr/lib -name "*.so*" | xargs ldd | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' $BUILD_APPDIR/usr/lib - set -e # this prevents "symbol lookup error libunity-gtk-module.so: undefined symbol: g_settings_new" on ubuntu 14.04 - rm -f $BUILD_APPDIR/usr/lib/qt5/plugins/platformthemes/libqgtk2.so || true - rmdir $BUILD_APPDIR/usr/lib/qt5/plugins/platformthemes || true # should be empty after deleting libqgtk2.so - rm -f $BUILD_APPDIR/usr/lib/libgio* || true # these are not needed if we don't use gtk # Remove unused parts of Qt - rm -f $BUILD_APPDIR/usr/lib/libQt5PrintSupport.so.5 || true - rm -f $BUILD_APPDIR/usr/lib/libQt5Network.so.5 || true - rm -f $BUILD_APPDIR/usr/lib/libQt5Sql.so.5 || true # Delete potentially dangerous libraries - rm -f $BUILD_APPDIR/usr/lib/libstdc* $BUILD_APPDIR/usr/lib/libgobject* $BUILD_APPDIR/usr/lib/libc.so.* || true # The following are assumed to be part of the base system - rm -f $BUILD_APPDIR/usr/lib/libgtk-x11-2.0.so.0 || true # this prevents Gtk-WARNINGS about missing themes - rm -f $BUILD_APPDIR/usr/lib/libdbus-1.so.3 || true # this prevents '/var/lib/dbus/machine-id' error on fedora 22/23 live cd - rm -f $BUILD_APPDIR/usr/lib/libGL.so.* || true - rm -f $BUILD_APPDIR/usr/lib/libdrm.so.* || true - rm -f $BUILD_APPDIR/usr/lib/libxcb.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libX11.so.6 || true - rm -f $BUILD_APPDIR/usr/lib/libcom_err.so.2 || true - rm -f $BUILD_APPDIR/usr/lib/libcrypt.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libdl.so.2 || true - rm -f $BUILD_APPDIR/usr/lib/libexpat.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libfontconfig.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libgcc_s.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libglib-2.0.so.0 || true - rm -f $BUILD_APPDIR/usr/lib/libgpg-error.so.0 || true - rm -f $BUILD_APPDIR/usr/lib/libgssapi_krb5.so.2 || true - rm -f $BUILD_APPDIR/usr/lib/libgssapi.so.3 || true - rm -f $BUILD_APPDIR/usr/lib/libhcrypto.so.4 || true - rm -f $BUILD_APPDIR/usr/lib/libheimbase.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libheimntlm.so.0 || true - rm -f $BUILD_APPDIR/usr/lib/libhx509.so.5 || true - rm -f $BUILD_APPDIR/usr/lib/libICE.so.6 || true - rm -f $BUILD_APPDIR/usr/lib/libidn.so.11 || true - rm -f $BUILD_APPDIR/usr/lib/libk5crypto.so.3 || true - rm -f $BUILD_APPDIR/usr/lib/libkeyutils.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libkrb5.so.26 || true - rm -f $BUILD_APPDIR/usr/lib/libkrb5.so.3 || true - rm -f $BUILD_APPDIR/usr/lib/libkrb5support.so.0 || true # rm -f $BUILD_APPDIR/usr/lib/liblber-2.4.so.2 || true # needed for debian wheezy # rm -f $BUILD_APPDIR/usr/lib/libldap_r-2.4.so.2 || true # needed for debian wheezy - rm -f $BUILD_APPDIR/usr/lib/libm.so.6 || true - rm -f $BUILD_APPDIR/usr/lib/libp11-kit.so.0 || true - rm -f $BUILD_APPDIR/usr/lib/libpcre.so.3 || true - rm -f $BUILD_APPDIR/usr/lib/libpthread.so.0 || true - rm -f $BUILD_APPDIR/usr/lib/libresolv.so.2 || true - rm -f $BUILD_APPDIR/usr/lib/libroken.so.18 || true - rm -f $BUILD_APPDIR/usr/lib/librt.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libsasl2.so.2 || true - rm -f $BUILD_APPDIR/usr/lib/libSM.so.6 || true - rm -f $BUILD_APPDIR/usr/lib/libusb-1.0.so.0 || true - rm -f $BUILD_APPDIR/usr/lib/libuuid.so.1 || true - rm -f $BUILD_APPDIR/usr/lib/libwind.so.0 || true - rm -f $BUILD_APPDIR/usr/lib/libz.so.1 || true # Show what we have done - find $BUILD_APPDIR/ ipe-tools-7.2.24.1/appimage/ipe.png000066400000000000000000000453141420423641200167330ustar00rootroot00000000000000PNG  IHDR>aiCCPICC Profile(Kaǿ(A`f{CHLeD宿شmY컻"^:f=**9R,"t%1)fm-%*t̩vOvSA\\zN=/EN1I pHYs   IDATxwTwz6`M@AA4QJF)Qb4ňƞ&(*E Ҥ,eپ;{~ܙٙ&^zkw9̹ysvGl Gg\9F#@OEChUV`{R߾bvܩ/++3L&s^^!m!lTGP,($$I>2?O5& |`&; z^9餓Ro5L&FHn vB IR{%Iuѓ{IBڷE!dUUM:B~Zvd…׍7A#.$IJ6zh׿UG~&z-&L ,X f͚%d.e/B8p@Ch-}o0t^ҎjV]ݒ$a1HxC_F~Q DŽ3&19f<ސԙ{ع)⿯Y6o`浏1pxbP1XIv$ _8K%AA)};d5m;V=6.zEQլJ1[+*%CN1T:#F/Џ3tuRq#XЏ |Ktbw6sjVF $ʀ`I{_H}w߯Odw2nӦ駟F[aMUN9cV-y`0re3ȗ]vX`S'IFx0$*Чx1V yo-># kP{O@PRC'AB6]yRRч{1ccƌIoxg?%8'tDo~#=z*zN@YT!AT`"kTP_, OXJWb(k$=gq4"ty|+>"8p fqtøy==Őz9p۷oO;| =X[Yp-%;M ,fןb&VSK^(ov{!^8|q]"ÕÓ+wXtSQV=̌6l؀W^1!Æ>)`3I3 yä2O<n;-^+--ej($[=@ELGIK Uy%ܲ EQ2>O9W 2x%xDiPUF9͋1$l=/4;f&Lc=5y&9g;w#pر2FŨQ3f cǎ`\aG{dIz=*y\jJ&j|M^x5ݕT1/2u:Q dYN2$Xr%+Wπ! ;2uj2!V! HueZ|slذ}bZX\Jac8i=ѣG3xX"h T!NUɛJl2 >I-]ԿσE3?@^ֹeP#rUg"O{߽K/o]ؒ$ {ThMGW,7L_׌^lQwEQ/Qhi 1u}Ldܓ\0̬!&m2mCOB:>K +/ihh#@>V@8 On6 g tR~ۿh_r%|52 QmgV] 6cƏ R!7LqY-rR|Of#k(I4zաPϱG|M?O04wϞ=yꩧ8sӪrA<]"2:aѵq'WtSq)s9.|m/K;nѸ>^ye&3}p`Ѣlr9^$ Ym "s<6&OW_}y<+ fРA oaÕ_vYn&d ,2>b*.y ( M.hػw/o^WkZ= 5Yk΁v.0>DΝo& |vUl{ 22Q= .+_GU&,Oݻ7seҥ:hG~tup˜˧~< #m zdSpJf̘Q#~/x:F9i c=FAIED83d]"P(Hgx1{%Ibȑ̽%DVUU/RZ˖-[rcE՗CZdZ@M9E7§_U7 k}5U X+gh `l w.J/ Z Uٗ}.zJ\xzuͷ~ 2T,> 8l03-8ogI}V;` ^"RH%mK`N7zie0~!ܑ (XN93fz+ v*:l|7@n=-!ȷT731;AIސ@'fw}8v6"If*LhK8JS! O3=[1pJƇ$t~lN,(X L4)FQPz<,ah0MB7(A'|ZڽBmB{Ldy&zyߠ4WS^^W\ݻ(Qr.U+?OAƯ'fʤ3b!li7]- LfY RJ!0lF-.0>J7|!,t>ѣYvYĩbIb}7>*2SO;T.̈rCv^<3&pWruq5װdV+yzoE:Y.?%\[T(A;Mt^bZU! b*,EcؠѣRoIؒ,cviEvQbin 33k*: i=)b&(fryq9r$8y{=FE~ϛqun8syꩧ:vxP88b !A B2T7)XbX x֬*9jpqH3)wx<,jkt^Zw@gN"LMmzŠ8E*Q|m?b8,`Xb'KNÜZӌRڹԵk۶mPr.@ `Sթ} Mvl !0 7QjOm:ij] ZVfϞ~-(|1yg< -->A^jq"kj5/zDd䵶p5PRRud\ _A*++L:P($H64@@.yBxտ9Æ l6c00 |(sΙyMKKK q;v͠xeYfСL0=x4 &8r|L[cP.P$r4fK;FYqW Lm[{1cxᇣ(f8N@W9yL/b0ΦZ6eLPѐkvoH밟&wIYSHS9=AM؍"٤Cp*X28$$S'qIaL/z%,K/]q2%AXڑiώz&Ls=k%Tgr̾v!/`1J1;'оo9fpe:Mu;A:F'SՠEYQyj;fØ tVWW>`4g@9´Y me$Ib,^8vBn8g˕WiԹ<*U N<OAWpUa@=MQ" ]%[+fMfVlg3bA4S?G|"r咟z)va#ݖE+[@t"Փ.$I:*%jQ@4d/G^26DPѳtذ6wKU(HZPP@AAu-x$QbwT֌*{ZQ-[c=mQT^R @ KN;_mG}ԻUZܽN3D>FhblSz%v]ZOE% O-XX[ͱڌ]y.&ӡS 끔\4Y؃;tчܲ]|KNpp3SP@"}%W$dMY%%&ЫW/TWԔv< H:nm4553x-nHoŦe]Nbfwތ; .xUp…|@<BA 42liLjA(ӗYrE*G43N h44iң9|lfĀrFɓ5k^{x^/ ,`ɒ%DVR6(0K=Z5{:d#Gd_UwȔtڛ>tŊ,隣eSw/X_9GۍX,R>[e1;lӟJ~iI*ƍٱc#G.hK}VMСC2|p֯_Ư7s霳bgsnQc"UUq߯~a{F[]& !ʄ# nJ6m?>皤|ᇌ9QƓh6("A6Vh|Ab4E;G H֎_{)0==4z '@$ !DZJ4Ek;⟱'9w;4Љ ,ǜqikA‰HT|!gIƢ'f$>~Na_ ocִ A3d=ÌƄG_X1=r(ݜ?DS3d 2J0+FQ;v h?""l*F2X;X=*v"^g۱Sq!l_>~0ez;d;UFDիWs=d|88ЉWN|&K`Aե64Bf frD AQ{̛jjRvŧrΰ4~cM#I];rJVX~3tDO]hƍjjj7oLjuQQQr?$0nIu`׷bQA l5ٱ9ɲLAQ1e|!a&ٓä(Umc7zG;BTHb p> _>|xB<B0|oߞS]v7|s~"߼2b$9FuPl(ڬ%/5LL03AT1u i|N20_"|矮GK8^Ǻu(*}E/UɔӘ1c3gΌ gaU?`w}J,"牜ww _Hk\Q¢h ݃{3/[x/ǯd}{\xoi!(A;?srB:";$IL2g}={&$zrRc0у?zxR:`FM),G`)ԹUlvt1${06Œ`bbçppfAݘ.rЇ!hn8f46lp{0'(˰H ZpѣG3h,O7{átPjQV_ާ~ƌ爣FGӺ$1PHD8Zv؁cR b֝q sr IDATsΡ(lic@*W_Ӈ և9cG"}Ba:2p킊JM..zxaܗї{ ?TIG׋b1h qEW_}U466 !j$/md&?wV+VVV&8w/mpPu!DWu ؁Ђ;^z3gfӻwok׮x=9dsi5|z}\p˓/Wg$(?,Dz7oNsEY-趣a]=p`xgϞ-[y1pDqmUV:rĿ/a4S^/~'<>9s+WzKuOG &lb Ip1$f.o9RTUU%hO,בv !Z?<]WW'v!֭['֮]+l"jjjĦǥ'+W v5Ŀ5U~ `ժUB! 1dȐ.'1Ȳ,,(**f>c_?/٣a>$ ׸~hOkG ewvOp(&%JRZAoRx,̜9GyolWud̛7ЬJt<&N_;3߫*>ǪUϸ[4i{xue3z.) 7ے_,QӦпH(Qda3laU1ޟfb~ .Eܳ4e'o@2 bܹ~B޷vvj o(*ȺOØM)>XSnQu<$Za0o/W 26mieI%2ϻ z(J0r7%|*g>?ff RxQ2d|̜)é(#Iʘ"-A4np*B\h@9[rHWRZ  `eu@; Z \|7s~z=+x>2B0j(nV.R q%tԹGCC昳A!^@ZY H%t .VͮHO$ZU;_vE)LpPLc1J\^upݬ^u^y֯_EQRdfw0|<|L4))ϓ9FQ ?^UHS2qz yѤ%:SpsDj5L3&Xc}ٛüJ캔\:M&W\qW\q ,^O5 :?ƐO)rg2{l''VSpX;͒__{ڷ55455r4^%KqoVM8 **C!tC"ڞYfNhÌF6vcNZ,%dA.&¡vVe6 ϝA8(RV%5풒1AP];vf^/ |}e+ի椓NhluT# UU%v:Yr9~W%av'y$qOO;}TS׌+|쮮.KB^VVqaCYz\.7t˖-KYέ9s$(-Thaɓ}ʳ 4dT%Lmc+{~K/ěoM0u*ޕʦٴqtS;Co{ Q4|uO>du~Yg?!Æ of͚5@$nZꪫf1IAEKVAU՛?D JyԔ( .o>B63]{=BA s,%wQg 3'$8v6C(}ҤI"]Sf /Ҷ T3yKt8y)-@L:}r&a0h :Qc_ G(ޗ:ЪXML2TO#AK3ϤFpQA٠ 9?-سk' $^,"Q>6G!5G^H"t%BՀBidF{Hܿu7=M)0 87rx˓O>ɨQRk3jbOX%XjOV jkZ|*5m*-l^_3 ɚ^@QiO:SӯwVV0Wn%l޴իVصVM`L=Dt_[gGiΪ4u,g춈 Yy4=ܴ}]'()pE>W@p{h,!V0%3[l=y+~ gRx8l2iʙLr&w}7O>,? ջfD@@y!a9cf?2%,-X[6??*eI9"> 55\/COep %v9\I4od w?'?^F|dOhF^ŭ?v{(|C>|N"I0 5}H-fwX4"]ZY4l?]ˊ5kS!_[km؞bn>h:%Cfӳ ZU !q-,}O =Uz=X1B"B—ys.hKo e?wm5#!+_y/׉w,"ރWpBf3"9z+؏ |}݇ߟX4s]wQZZn{,REK _"k10UQX̿nMڃN_#J.Ӑ%,Oǡ }Z9^ĺky_o!@ =NΧT?B !#-argʶ) CCI#dI!9`n;CI,5nǴ)RSb#BTƫH6zmXGuvSWd&bUɜ-T*(((sO 8p 9N![`đ%rDs]l޺#EQHS/K~p]ݡ#R;"8CZM.3XXu4,oR$'LGQ*+++z@XMد_kB{_V5ĀCK3G.T8t:MF7]rt:UnC{!ɎAMMM,]4hdРAɓ͑hVxDKAG AFV֩b3j!zh+fnjf P8\}Mw (N?rGLz,EV#;0\.!rFtp~\j@Xp RLHNk2&ZN#1یRT}1s-}>c䐎$&NŒgrxFJ푽,Ofoz=r*6md7n\sȝ(:3\tRZۜlŚWD~Y%ŽK_i5s+r۩up.&Z߽)K"DƽԨӸ'&%}B?~)s=ip&M7]sdbӧJLq9eY /`a`6`L橊B8uڬej8mVaZ9z(J$Stl 4o=dsT : ݔTgݪ勵de{ 6#bYX&duDHILIEH,A位X,hNAg ~ܵn(rEFo=d /t9a*' yxќzݭyy2fL{. #lFY2z"@&hO5 NE0FqYz ?y&LHmQ-V/HO2 !ؽm8ޕ|ɱl[N4kHF|oP Ƣ6u!آZ|E?}w5)ࠝҺ-"H1W3xZJ#,^Lus;*GN`) }zkk/:RNWB' Ju-W jZBPT\€1ӹ=z4W_}5˗/Oj= C;r܎w=*m~[lc7I jGN9;dĒ GA.Of]RPQz,r%!AWY30zĈΘ16˥B`۩A y*[V R>āGwB W0qy9n )eT0cjGeN[`4W-'f? JJ{ܤHItzIr>,zOȪ"/j냂F v0ꃴzUF ĝzϖ'm u`f|ôUUe(ek}vd[$Z*W$Jt PE{B:m%h93i3jdA FOT騋ȉi,&:qZAE^WEgcQfG察r|b(7^O>Iڵk;V+]w# /RUUk9e(\LxA0b Xe$F󓹗ÁfEuHT6,^A^*Shpk$M [AW ."M2:gk^9 @nҦ:I^/K,I^O^Cah+Qs:X`2m  <*u.ŐmBhQKA ;_ͤiA%6~ERL+rs׬LPUU*?GNOM P$IJ hdac=ơC/ٜ)di!p#EJf|L}mM>Q@_x(9EdMӯjѿߍ"%dIi71E4d]~ |dTAEАP,'>LEA-Z+ڑry+ZH*8rJ?Ծ\~AkKB}q *`T*˓`CǺAU;ܪfX*3$ 1C0O$Ɇvv3+t dqBD9cxw[PU5,2f͢OGm&M5{G'(8ɫrEIXz92'BafR*K d{\@&(4{ B0qUUUIdIz BV_QV۵]wl_>jdQ䛬`^ׇcfe8$/  Դ)3$CV s:ʜvت ʔ].mMfC^B{*pl>,Gg(2HZl[7y!a!Ͳ-ζ5P=jr7<\;)5BUΪ 8r, zb<U{mtR#uQM[ѓe#<T:d;@DmwϲEq`"k^_cqq1~;>p?fvSvEj d,z6طo+Wl%6]$Uv4Oxꘜ9exA%fHԊ+bB^^4mֈFQ339ÎbM'~\:eID8рB^oϞ=3g/?~|>k2Eڍ9Z# ^S"WYv ..8'UfW]vG\Mc;BYթDABNAU;\N."6mD Hx b̞=??!2hɦ=YZ2. GO7 S9t^y)SR\VC8?#q~M \A%/2+ppYP3, IJ~vd>.m#TYhr>Iq~XzM3fK-ECd\ ߜ!Է7=`uN5˖_oNh6 Z7Y]`Z I¡}w?z/z0pݜ {>_ 򛉱s/3VOmCz>jy.IDAT8n&fϞ&`*acs gMgjfFw, J62qɹJǟؗ'֌QDc3P ^sES,1Eh~fTOK,=l-^}gSᐩ*ħ0ȸ'u$Cb菵)|VAéX Td(qD4@qiY#: ?̞~f</?mΘ PK03᭷͡#GSOϽiS0[d?h&b6B2r* xk'il>9p(:a"r0c'Lau9=kS޷ (zsϞ[2(hpg>M$^`;oB Ǘq]5pJyZg{ȘFю5-t  $Hyc Ą`w@V_zquL} u|s4Ifi>\Bm ^ٹc+}W],b0k9}޻|+0Bn'/(+Mt$T8djZjs7p$\BPh4aoGT axq7ɶSE %n5 (f3gu̩P0* ܋/䵷$ }ql%V@S(ߢM y7y7v6jjÇ<zbiDPᐩs 9z$Imp$"iRBmK K:)&"3uX/!vS0I[e٠,so49="f^{>t-'^Č?\G^ѮT.1 {ױ$RhM uuq)$N: SQFV(J-ޠ*o87O֣G}`0(F΄|F8fAN*g\,1f m*Gߞy?%#w$iȑL]cǝƙgI' ?~,}붆mسwz-aEI4Ɔw;˯2$I,X@BA2DTLj_Q= !8ű̑5a'3>xg[!-CVhq}K7}Ò$IT׶EGoa9RBd I1ݒA՘ܙr H`y&_P`IdX$-rRjkj:h}GI9"N@;Vd!L[5|sa6$)).0*SAI*h UCӪM>=$BPUQ`0v:6k ߷i bL11S؁?D2c`  3B]Zu@B*Tj&m\ٱc%@;p:={w߫WU9saYN£T lie$8'ψLG!,e`C7ՅAE2ji^Rꛈ|AKM}em۶wR{V+}7\6C[/׮Lzj67 {wtQ NatV(3r`zLՂTe4@{+ ,E/TkRk"RWJka4<_۽ nSTb 5 c\W&$>o?ʢvpi͸%pQXqIAk}̉,c}P̋iKyD)"m۶w0 ^$V{W͌0cLr9.$pN4M2{GXgpG_?hGCibt c6dY9WGIENDB`ipe-tools-7.2.24.1/appimage/recipe.sh000077500000000000000000000141271420423641200172540ustar00rootroot00000000000000#!/bin/bash # Halt on errors set -e #scl enable devtoolset-2 source /opt/rh/devtoolset-2/enable BASE=`pwd` IPE=ipe-7.2.8 APP_DIR=$BASE/Ipe.AppDir APP_IMAGE=$BASE/$IPE-x86_64.AppImage # Cleanup rm -fr build rm -fr $APP_DIR $APP_IMAGE ###################################################### # create AppDir ###################################################### mkdir $APP_DIR mkdir -p $APP_DIR/usr/bin/platforms mkdir -p $APP_DIR/usr/lib/qt5 # Change directory to build. Everything happens in build. mkdir -p build ###################################################### # Build Ipe ###################################################### cd $BASE/build if [ "x$1" == "xlocal" ]; then echo "Using local copy of Ipe sources" cp $HOME/$IPE-src.tar.gz . else echo "Downloading Ipe sources" wget https://dl.bintray.com/otfried/generic/ipe/7.2/$IPE-src.tar.gz fi tar xfvz $IPE-src.tar.gz #mkdir $IPE #rsync -trvz --delete --exclude=.git --exclude=build/obj --exclude=build/Ipe.app $SOURCE_HOST:Devel/ipe/ $IPE/ cd $IPE/src export QT_SELECT=5 export MOC=moc-qt5 # use libpng16 export PNG_CFLAGS="-I\/usr\/local\/include\/libpng16" export PNG_LIBS="-lpng16" export LUA_CFLAGS="-I/usr/local/include" export LUA_LIBS="-L/usr/local/lib -llua -lm" export QHULL_CFLAGS="-I/usr/local/include/qhull" export QHULL_LIBS="-lqhullstatic" export IPEPREFIX=$APP_DIR/usr export IPEQVORONOI=1 make IPEAPPIMAGE=1 make install IPEAPPIMAGE=1 ###################################################### # Build pdftoipe ###################################################### cd $BASE/build cp -r $BASE/../pdftoipe . cd pdftoipe make install -m 0755 pdftoipe $APP_DIR/usr/bin ###################################################### # Build exec wrapper (for calling subprocesses) ###################################################### #cd $BASE/build #git clone git://anongit.kde.org/scratch/brauch/appimage-exec-wrapper.git #cd appimage-exec-wrapper #make #install -m 0755 exec.so $APP_DIR/usr/lib/ ###################################################### # Populate Ipe.AppDir ###################################################### cd $BASE cp AppImageKit/AppRun $APP_DIR cp ipe.png $APP_DIR cp Ipe.desktop $APP_DIR cp startipe.sh $APP_DIR/usr/bin cp appimage.lua $APP_DIR/usr/ipe/ipelets cp -R /usr/lib64/qt5/plugins $APP_DIR/usr/lib/qt5/ cp $APP_DIR/usr/lib/qt5/plugins/platforms/libqxcb.so $APP_DIR/usr/bin/platforms/ set +e ldd $APP_DIR/usr/lib/qt5/plugins/platforms/libqxcb.so | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' $APP_DIR/usr/lib ldd $APP_DIR/usr/bin/* | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' $APP_DIR/usr/lib find $APP_DIR/usr/lib -name "*.so*" | xargs ldd | grep "=>" | awk '{print $3}' | xargs -I '{}' cp -v '{}' $APP_DIR/usr/lib set -e cp /usr/local/lib/libjpeg.so.8 $APP_DIR/usr/lib cp /usr/local/lib/libpng16.so.16 $APP_DIR/usr/lib/ cp $(ldconfig -p | grep libEGL.so.1 | cut -d ">" -f 2 | xargs) $APP_DIR/usr/lib/ # Otherwise F23 cannot load the Qt platform plugin "xcb" # this prevents "symbol lookup error libunity-gtk-module.so: undefined symbol: g_settings_new" on ubuntu 14.04 rm -f $APP_DIR/usr/lib/qt5/plugins/platformthemes/libqgtk2.so || true rmdir $APP_DIR/usr/lib/qt5/plugins/platformthemes || true # should be empty after deleting libqgtk2.so rm -f $APP_DIR/usr/lib/libgio* || true # these are not needed if we don't use gtk # Removed unused parts of Qt rm -f $APP_DIR/usr/lib/libQt5PrintSupport.so.5 || true rm -f $APP_DIR/usr/lib/libQt5Network.so.5 || true rm -f $APP_DIR/usr/lib/libQt5Sql.so.5 || true # Delete potentially dangerous libraries rm -f $APP_DIR/usr/lib/libstdc* $APP_DIR/usr/lib/libgobject* $APP_DIR/usr/lib/libc.so.* || true # The following are assumed to be part of the base system rm -f $APP_DIR/usr/lib/libgtk-x11-2.0.so.0 || true # this prevents Gtk-WARNINGS about missing themes rm -f $APP_DIR/usr/lib/libdbus-1.so.3 || true # this prevents '/var/lib/dbus/machine-id' error on fedora 22/23 live cd rm -f $APP_DIR/usr/lib/libGL.so.* || true rm -f $APP_DIR/usr/lib/libdrm.so.* || true rm -f $APP_DIR/usr/lib/libxcb.so.1 || true rm -f $APP_DIR/usr/lib/libX11.so.6 || true rm -f $APP_DIR/usr/lib/libcom_err.so.2 || true rm -f $APP_DIR/usr/lib/libcrypt.so.1 || true rm -f $APP_DIR/usr/lib/libdl.so.2 || true rm -f $APP_DIR/usr/lib/libexpat.so.1 || true rm -f $APP_DIR/usr/lib/libfontconfig.so.1 || true rm -f $APP_DIR/usr/lib/libgcc_s.so.1 || true rm -f $APP_DIR/usr/lib/libglib-2.0.so.0 || true rm -f $APP_DIR/usr/lib/libgpg-error.so.0 || true rm -f $APP_DIR/usr/lib/libgssapi_krb5.so.2 || true rm -f $APP_DIR/usr/lib/libgssapi.so.3 || true rm -f $APP_DIR/usr/lib/libhcrypto.so.4 || true rm -f $APP_DIR/usr/lib/libheimbase.so.1 || true rm -f $APP_DIR/usr/lib/libheimntlm.so.0 || true rm -f $APP_DIR/usr/lib/libhx509.so.5 || true rm -f $APP_DIR/usr/lib/libICE.so.6 || true rm -f $APP_DIR/usr/lib/libidn.so.11 || true rm -f $APP_DIR/usr/lib/libk5crypto.so.3 || true rm -f $APP_DIR/usr/lib/libkeyutils.so.1 || true rm -f $APP_DIR/usr/lib/libkrb5.so.26 || true rm -f $APP_DIR/usr/lib/libkrb5.so.3 || true rm -f $APP_DIR/usr/lib/libkrb5support.so.0 || true # rm -f $APP_DIR/usr/lib/liblber-2.4.so.2 || true # needed for debian wheezy # rm -f $APP_DIR/usr/lib/libldap_r-2.4.so.2 || true # needed for debian wheezy rm -f $APP_DIR/usr/lib/libm.so.6 || true rm -f $APP_DIR/usr/lib/libp11-kit.so.0 || true rm -f $APP_DIR/usr/lib/libpcre.so.3 || true rm -f $APP_DIR/usr/lib/libpthread.so.0 || true rm -f $APP_DIR/usr/lib/libresolv.so.2 || true rm -f $APP_DIR/usr/lib/libroken.so.18 || true rm -f $APP_DIR/usr/lib/librt.so.1 || true rm -f $APP_DIR/usr/lib/libsasl2.so.2 || true rm -f $APP_DIR/usr/lib/libSM.so.6 || true rm -f $APP_DIR/usr/lib/libusb-1.0.so.0 || true rm -f $APP_DIR/usr/lib/libuuid.so.1 || true rm -f $APP_DIR/usr/lib/libwind.so.0 || true rm -f $APP_DIR/usr/lib/libz.so.1 || true # patch hardcoded '/usr/lib' in binaries away find $APP_DIR/usr/ -type f -exec sed -i -e 's|/usr/lib|././/lib|g' {} \; ###################################################### # Create AppImage ###################################################### # Convert the AppDir into an AppImage $BASE/AppImageKit/AppImageAssistant.AppDir/package $APP_DIR/ $APP_IMAGE ipe-tools-7.2.24.1/appimage/setup.sh000077500000000000000000000056241420423641200171470ustar00rootroot00000000000000#!/bin/bash # # Download and install all prerequisites for making the Ipe AppImage # # Halt on errors set -e ###################################################### # install packages ###################################################### # epel-release for newest Qt and stuff sudo yum -y install epel-release sudo yum -y install readline-devel zlib-devel cairo-devel sudo yum -y install cmake binutils fuse glibc-devel glib2-devel fuse-devel gcc zlib-devel # AppImageKit dependencies sudo yum -y install poppler-devel # Need a newer gcc, getting it from Developer Toolset 2 sudo wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtools-2.repo sudo yum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils # /opt/rh/devtoolset-2/root/usr/bin/gcc # now holds gcc and c++ 4.8.2 #scl enable devtoolset-2 source /opt/rh/devtoolset-2/enable ###################################################### # Install Qt ###################################################### #wget http://download.qt.io/official_releases/online_installers/qt-unified-linux-x86-online.run #chmod +x qt-unified-linux-x86-online.run #./qt-unified-linux-x86-online.run --script qt-installer-noninteractive.qs # wget http://download.qt.io/official_releases/qt/5.5/5.5.1/qt-opensource-linux-x64-5.5.1.run # chmod +x qt-opensource-linux-x64-5.5.1.run # ./qt-opensource-linux-x64-5.5.1.run --script qt-installer-noninteractive.qs sudo yum -y install qt5-qtbase-devel qt5-qtbase-gui # qt5-qtlocation-devel qt5-qtscript-devel qt5-qtwebkit-devel qt5-qtsvg-devel qt5-linguist qt5-qtconnectivity-devel ###################################################### # build libraries from source ###################################################### # Change directory to build. Everything happens in build. mkdir build cd build # libjpeg wget http://www.ijg.org/files/jpegsrc.v8d.tar.gz tar xfvz jpegsrc.v8d.tar.gz cd jpeg-8d ./configure && make && sudo make install cd .. # lua wget http://www.lua.org/ftp/lua-5.3.3.tar.gz tar xfvz lua-5.3.3.tar.gz cd lua-5.3.3/src #sed -i 's/^CFLAGS=/CFLAGS= -fPIC /g' Makefile cd .. make linux CFLAGS="-fPIC" sudo make install cd .. # libpng wget http://download.sourceforge.net/libpng/libpng-1.6.21.tar.gz tar xfvz libpng-1.6.21.tar.gz cd libpng-1.6.21 ./configure make check sudo make install cd .. # qhull wget http://www.qhull.org/download/qhull-2015-src-7.2.0.tgz tar xfvz qhull-2015-src-7.2.0.tgz cd qhull-2015.2 make sudo install -d /usr/local/include/qhull sudo install -m 0644 src/libqhull/*.h /usr/local/include/qhull sudo install -m 0755 lib/libqhullstatic.a /usr/local/lib cd .. ###################################################### # Build AppImageKit (into top level) ###################################################### cd .. git clone https://github.com/probonopd/AppImageKit.git cd AppImageKit/ git checkout master cmake . make clean make ###################################################### ipe-tools-7.2.24.1/appimage/startipe.sh000077500000000000000000000001561420423641200176350ustar00rootroot00000000000000#!/bin/bash CMD=$1 if [ "x${CMD}x" == "xx" ]; then ipe -style fusion "$@" else shift $CMD "$@" fi ipe-tools-7.2.24.1/figtoipe/000077500000000000000000000000001420423641200154645ustar00rootroot00000000000000ipe-tools-7.2.24.1/figtoipe/Makefile000066400000000000000000000012361420423641200171260ustar00rootroot00000000000000############################################################################# # Makefile for building figtoipe ############################################################################# CXX = g++ CXXFLAGS += -O2 -Wall -W RM = rm -f LIBS = -lz TARGET = figtoipe SOURCES = $(TARGET).cpp SRCDISTFILES = $(TARGET).1 $(SOURCES) README Makefile GPL-2 all: $(TARGET) clean: -$(RM) $(TARGET) $(TARGET): $(SOURCES) $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) dist: DATE=`date +"%Y%m%d"`; D=/tmp/$(TARGET)-$$DATE; \ rm -rf $$D; mkdir $$D; \ cp $(SRCDISTFILES) $$D; \ tar czvf $$D.tar.gz -C /tmp $(TARGET)-$$DATE .PHONY: all ipe-tools-7.2.24.1/figtoipe/figtoipe.1000066400000000000000000000054551420423641200173650ustar00rootroot00000000000000.\" 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) .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. .TH FIGTOIPE 1 "April 26, 2008" .\" 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 figtoipe \- Convert FIG figures into Ipe format .SH SYNOPSIS .B figtoipe \fP[\-g] [\-p \fIpreamble\fP] \fIFIGfile\fP \fIXMLfile\fP .SH DESCRIPTION \fBfigtoipe\fP converts files in FIG format (as created, e.g., by \fBxfig\fP) to Ipe's XML format. \fBfigtoipe\fP is not complete. The main lacking feature is the conversion of splines. Arc-boxes are replaced by rectangles. Feel free to improve this version! The drawing models of FIG and Ipe are somewhat different. Ipe does not support depth ordering independent of grouping, pattern fill, and Postscript fonts. \fBfigtoipe\fP tries to include images specified in the XFIG file. For JPEG pictures, it tries include the compressed image data into the XML file. For files not recognized as JPEG, \fBanytopnm\fP is called; its output is compressed and included in the XML file. Some output of \fBanytopnm\fP might be rejected (e.g. images larger than 5000x5000 pixels or B&W bitmaps), or misunderstood. .SH OPTIONS .B \-g group the figure in the output XML .TP .B \-c tell ipe to crop PDF output to the boundingbox .TP .B \-6 write in ipe 6 format instead of ipe 7 format .TP .B \-p \fIlatex\fP add \fIlatex\fP as a preamble to the generated XML file, e.g. .nf .in +.5i \'\\usepackage{amsmath}\' .in -.5i .fi .SH AUTHORS .ft CW .nf \&Otfried Cheong \&Alexander B\[:u]rger (image handling) .ft R .fi .SH REPORTING BUGS .ad l Please report bugs using Ipe bugzilla at .I "https://github.com/otfried/ipe-tools/issues" .SH SEE ALSO .ad l More information about Ipe can be found in .IR "The Ipe Manual" , which can be found in your Ipe installation. .SH LICENSE & WARRANTY .ad l \fBfigtoipe\fP comes with ABSOLUTELY NO WARRANTY. It 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. See the file \fBgpl.txt\fP accompanying the source for details. ipe-tools-7.2.24.1/figtoipe/figtoipe.cpp000066400000000000000000001211501420423641200177760ustar00rootroot00000000000000/* This file is part of the extensible drawing editor Ipe. Copyright (C) 1993-2019 Otfried Cheong Ipe 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. As a special exception, you have permission to link Ipe with the CGAL library and distribute executables, as long as you follow the requirements of the Gnu General Public License in regard to all of the software in the executable aside from CGAL. Ipe 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * figtoipe.cpp * * This program converts files in FIG format (as used by Xfig) to XML * format as used by Ipe 6.0. * * All versions of the FIG file format are documented here: * "http://duke.usask.ca/~macphed/soft/fig/formats.html" * * This program can read only versions 3.0, 3.1, and 3.2. * * Changes: * * 2005/10/31 - replace double backslash by single one in text. * 2005/11/14 - generate correct header for Ipe 6.0 preview 25. * 2007/08/19 - Alexander Buerger acfb@users.sf.net * + include some images with anytopnm * + correct FIG3.1 problem (bugzilla #237) * + fixed rotation of text and ellipses * + do not write invisible ellipses / polylines / arcs * + accept double values in some places (points, thickness, ...; * extends FIG format) * + skip comments between FIG objects * + replaced some %g formats as they illegally appear in the PDF * 2007/09/26 - Alexander Buerger acfb@users.sf.net * + compress bitmap images * + include compressed JPEG images * 2008/04/26 - Alexander Buerger acfb@users.sf.net * + support grayscale pnm * + added -g and -p options * 2009/10/22 - Alexander Buerger acfb@users.sf.net * + added -c option to use cropbox for output figure * + check fgets/fscanf return values * 2009/12/05 - Alexander Buerger acfb@users.sf.net * + write ipe 7 format by default * + added -6 option to write in ipe 6 format * + added simple_getopt function to parse options * + reduce filesize for images by putting 36 bytes per line * 2015/02/28 - Alexander Buerger acfb@users.sf.net * + check color array indices * + limit image filename string length in fscanf * + thanks to Jodie Cunningham * for pointing out these problems */ #include #include #include #include #include #include #include #include #include #include #define FIGTOIPE_VERSION "figtoipe 2015/02/28" const int MEDIABOX_WIDTH = 595; const int MEDIABOX_HEIGHT = 842; bool ipe7 = true; const int NFIXEDCOLORS = 32, NUSERCOLORS = 512, NCOLORS = NFIXEDCOLORS + NUSERCOLORS; // -------------------------------------------------------------------- struct Arrow { int iType; int iStyle; double iThickness; // 1/80 inch double iWidth; // Fig units double iHeight; // Fig units }; struct Point { double iX, iY; }; struct FigObject { int iType; int iSubtype; // meaning depends on type int iLinestyle; // solid, dashed, etc double iThickness; // 0 means no stroke int iPenColor; int iFillColor; int iDepth; // depth ordering int iPenStyle; // not used by FIG int iAreaFill; // how to fill: color, pattern double iStyle; // length of dash/dot pattern int iCapStyle; int iJoinStyle; int iDirection; int iForwardArrow; Arrow iForward; int iBackwardArrow; Arrow iBackward; double iCenterX, iCenterY; // center of ellipse and arc Point iArc1, iArc2, iArc3; double iAngle; // orientation of main axis of ellipse Point iRadius; // half-axes of ellipse int iArcBoxRadius; Point iPos; // position of text int iFont; int iFontFlags; double iFontSize; std::vector iString; std::vector iPoints; std::string image_filename; bool image_flipped; }; // -------------------------------------------------------------------- class FigReader { public: FigReader(FILE *fig) : iFig(fig) { } bool ReadHeader(); double Magnification() const { return iMagnification; } double UnitsPerPoint() const { return iUnitsPerPoint; } bool ReadObjects(); const std::vector &Objects() const { return iObjects; } const unsigned int *UserColors() const { return iUserColors; } private: bool GetLine(char *buf); int GetInt(); int GetColorInt(); double GetDouble(); Point GetPoint(); void GetColor(); void GetArc(FigObject &obj); void GetEllipse(FigObject &obj); void GetPolyline(FigObject &obj); void GetSpline(FigObject &obj); void GetText(FigObject &obj); void GetArrow(Arrow &a); void GetArrows(FigObject &obj); int ComputeDepth(unsigned int &i); private: FILE *iFig; int iVersion; // minor version of FIG format double iMagnification; double iUnitsPerPoint; std::vector iObjects; unsigned int iUserColors[NUSERCOLORS]; }; // -------------------------------------------------------------------- const int BUFSIZE = 0x100; // skip comment lines (in the header) bool FigReader::GetLine(char *buf) { do { if (fgets(buf, BUFSIZE, iFig) == NULL) return false; } while (buf[0] == '#'); return true; } int FigReader::GetInt() { int num = -1; if( fscanf(iFig, "%d", &num) != 1 && !feof(iFig)) fprintf(stderr, "Could not read integer value.\n"); return num; } int FigReader::GetColorInt() { int color = GetInt(); if( color < 0 || color >= NCOLORS ) { fprintf(stderr, "Color value %d out of range.\n", color); color = 0; } return color; } double FigReader::GetDouble() { double num = -1; if( fscanf(iFig, "%lg", &num) != 1 ) fprintf(stderr, "Could not read double value.\n"); return num; } Point FigReader::GetPoint() { Point p; p.iX = GetDouble(); p.iY = GetDouble(); return p; } void FigReader::GetArrow(Arrow &a) { a.iType = GetInt(); a.iStyle = GetInt(); a.iThickness = GetDouble(); a.iWidth = GetDouble(); a.iHeight = GetDouble(); } void FigReader::GetArrows(FigObject &obj) { if (obj.iForwardArrow) GetArrow(obj.iForward); if (obj.iBackwardArrow) GetArrow(obj.iBackward); } // -------------------------------------------------------------------- bool FigReader::ReadHeader() { char line[BUFSIZE]; if (fgets(line, BUFSIZE, iFig) != line) return false; if (strncmp(line, "#FIG", 4)) return false; // check FIG version int majorVersion; sscanf(line + 4, "%d.%d", &majorVersion, &iVersion); if (majorVersion != 3 || iVersion<0 || iVersion>2 ) { fprintf(stderr, "Figtoipe supports FIG versions 3.0 - 3.2 only.\n"); return false; } fprintf(stderr, "FIG format version %d.%d\n", majorVersion, iVersion); // skip orientation and justification if (!GetLine(line)) return false; if (!GetLine(line)) return false; bool metric = false; if (!GetLine(line)) return false; if (!strncmp(line, "Metric", 6)) metric = true; (void) metric; // not yet used int magnification = 100, resolution=1200; if (iVersion == 2) { // Version 3.2: // papersize if (!GetLine(line)) return false; // export and print magnification if (!GetLine(line)) return false; sscanf(line, "%d", &magnification); // multi-page mode if (!GetLine(line)) return false; // transparent color if (!GetLine(line)) return false; } // resolution and coord_system if (!GetLine(line)) return false; int coord_system; sscanf(line, "%d %d", &resolution, &coord_system); iUnitsPerPoint = (resolution / 72.0); iMagnification = magnification / 100.0; return true; } // link start and end of compounds together, // and assign depth to compound object int FigReader::ComputeDepth(unsigned int &i) { if (iObjects.at(i).iType != 6) return iObjects.at(i++).iDepth; int pos = i; int depth = 1000; ++i; while (iObjects.at(i).iType != -6) { int od = ComputeDepth(i); if (od < depth) depth = od; } iObjects.at(pos).iDepth = depth; iObjects.at(pos).iSubtype = i; ++i; return depth; } // -------------------------------------------------------------------- // objects are appended to list bool FigReader::ReadObjects() { int level = 0; for (;;) { int objType = GetInt(); //fprintf(stderr, "object type %d\n", objType); if (objType == -1 && fgetc(iFig)=='#' ) { char buf[1024]; if( fgets(buf, sizeof(buf), iFig) == NULL ) { fprintf(stderr, "Read error while skipping comment.\n"); return false; } continue; } if (objType == -1) { // EOF if (level > 0) return false; unsigned int i = 0; while (i < iObjects.size()) ComputeDepth(i); return true; } if (objType == 0) { // user-defined color GetColor(); } else { FigObject obj; obj.iType = objType; switch (obj.iType) { case 1: // ELLIPSE GetEllipse(obj); break; case 2: // POLYLINE GetPolyline(obj); break; case 3: // SPLINE GetSpline(obj); break; case 4: // TEXT GetText(obj); break; case 5: // ARC GetArc(obj); break; case 6: // COMPOUND (void) GetInt(); // read and ignore bounding box (void) GetInt(); (void) GetInt(); (void) GetInt(); ++level; break; case -6: // END of COMPOUND if (level == 0) return false; --level; break; default: fprintf(stderr, "Unknown object type in FIG file.\n"); return false; } iObjects.push_back(obj); } } } void FigReader::GetColor() { int colorNum = GetInt(); // color number int rgb = 0; if( fscanf(iFig," #%x", &rgb) != 1 ) // RGB string in hex fprintf(stderr, "Could not read rgb string.\n"); if( colorNum=NCOLORS ) { fprintf(stderr, "User color number %d out of range, replacing with %d.\n", colorNum, NFIXEDCOLORS); colorNum = NFIXEDCOLORS; } iUserColors[colorNum - NFIXEDCOLORS] = rgb; } void FigReader::GetEllipse(FigObject &obj) { obj.iSubtype = GetInt(); obj.iLinestyle = GetInt(); obj.iThickness = GetDouble(); obj.iPenColor = GetColorInt(); obj.iFillColor = GetColorInt(); obj.iDepth = GetInt(); obj.iPenStyle = GetInt(); // not used obj.iAreaFill = GetInt(); obj.iStyle = GetDouble(); obj.iDirection = GetInt(); // always 1 obj.iAngle = GetDouble(); // radians, the angle of the x-axis obj.iCenterX = GetDouble(); obj.iCenterY = GetDouble(); obj.iRadius = GetPoint(); (void) GetPoint(); // start (void) GetPoint(); // end } void FigReader::GetPolyline(FigObject &obj) { obj.iSubtype = GetInt(); obj.iLinestyle = GetInt(); obj.iThickness = GetDouble(); obj.iPenColor = GetColorInt(); obj.iFillColor = GetColorInt(); obj.iDepth = GetInt(); obj.iPenStyle = GetInt(); // not used obj.iAreaFill = GetInt(); obj.iStyle = GetDouble(); obj.iJoinStyle = GetInt(); obj.iCapStyle = GetInt(); obj.iArcBoxRadius = GetInt(); obj.iForwardArrow = GetInt(); obj.iBackwardArrow = GetInt(); int nPoints = GetInt(); GetArrows(obj); if (obj.iSubtype == 5) { // Imported image int orientation; char image_filename[1024]; // orientation and filename if( fscanf(iFig, "%d %1020s", &orientation, image_filename) != 2 ) { fprintf(stderr, "Could not read image orientation and/or filename. Exit.\n"); exit(-1); } obj.image_flipped = (orientation==1); obj.image_filename = std::string(image_filename); } for (int i = 0; i < nPoints; ++i) obj.iPoints.push_back( GetPoint() ); } void FigReader::GetSpline(FigObject &obj) { /* 0: opened approximated spline 1: closed approximated spline 2: opened interpolated spline 3: closed interpolated spline 4: opened x-spline (FIG 3.2) 5: closed x-spline (FIG 3.2) */ obj.iSubtype = GetInt(); obj.iLinestyle = GetInt(); obj.iThickness = GetDouble(); obj.iPenColor = GetColorInt(); obj.iFillColor = GetColorInt(); obj.iDepth = GetInt(); obj.iPenStyle = GetInt(); // not used obj.iAreaFill = GetInt(); obj.iStyle = GetDouble(); obj.iCapStyle = GetInt(); obj.iForwardArrow = GetInt(); obj.iBackwardArrow = GetInt(); int nPoints = GetInt(); GetArrows(obj); for (int i = 0; i < nPoints; ++i) obj.iPoints.push_back( GetPoint() ); if (iVersion == 2) { // shape factors exist in FIG 3.2 only for (int i = 0; i < nPoints; ++i) { (void) GetDouble(); // double shapeFactor } } else { if (obj.iSubtype > 1) { for (int i = 0; i < nPoints; ++i) { (void) GetDouble(); // double lx (void) GetDouble(); // double ly (void) GetDouble(); // double rx (void) GetDouble(); // double ry } } } } void FigReader::GetText(FigObject &obj) { obj.iSubtype = GetInt(); obj.iThickness = 1; // stroke obj.iPenColor = GetColorInt(); obj.iDepth = GetInt(); obj.iPenStyle = GetInt(); // not used obj.iFont = GetInt(); obj.iFontSize = GetDouble(); obj.iAngle = GetDouble(); obj.iFontFlags = GetInt(); (void) GetDouble(); // height (void) GetDouble(); // length obj.iPos = GetPoint(); // skip blank fgetc(iFig); std::vector string; for (;;) { int ch = fgetc(iFig); if (ch == EOF) break; if (ch < 0x80) { string.push_back(char(ch)); } else { // convert to UTF-8 string.push_back(char(0xc0 + ((ch >> 6) & 0x3))); string.push_back(char(ch & 0x3f)); } if (string.size() >= 4 && !strncmp(&string[string.size() - 4], "\\001", 4)) { string.resize(string.size() - 4); break; } // fig seems to store "\" as "\\" if (string.size() >= 2 && !strncmp(&string[string.size() - 2], "\\\\", 2)) { string.resize(string.size() - 1); } } string.push_back('\0'); obj.iString = string; } void FigReader::GetArc(FigObject &obj) { obj.iSubtype = GetInt(); obj.iLinestyle = GetInt(); obj.iThickness = GetDouble(); obj.iPenColor = GetColorInt(); obj.iFillColor = GetColorInt(); obj.iDepth = GetInt(); obj.iPenStyle = GetInt(); // not used obj.iAreaFill = GetInt(); obj.iStyle = GetDouble(); obj.iCapStyle = GetInt(); obj.iDirection = GetInt(); obj.iForwardArrow = GetInt(); obj.iBackwardArrow = GetInt(); obj.iCenterX = GetDouble(); obj.iCenterY = GetDouble(); obj.iArc1 = GetPoint(); obj.iArc2 = GetPoint(); obj.iArc3 = GetPoint(); GetArrows(obj); } // -------------------------------------------------------------------- unsigned int ColorTable[] = { 0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00, 0xffffff, 0x000090, 0x0000b0, 0x0000d0, 0x87ceff, 0x009000, 0x00b000, 0x00d000, 0x009090, 0x00b0b0, 0x00d0d0, 0x900000, 0xb00000, 0xd00000, 0x900090, 0xb000b0, 0xd000d0, 0x803000, 0xa04000, 0xc06000, 0xff8080, 0xffa0a0, 0xffc0c0, 0xffe0e0, 0xffd700 }; class FigWriter { public: FigWriter(FILE *xml, const std::string& figname, double mag, double upp, const unsigned int *uc) : iXml(xml), iFigName(figname), iMagnification(mag), iUnitsPerPoint(upp), iUserColors(uc) { } void WriteObjects(const std::vector &objects, int start, int fin); private: void WriteEllipse(const FigObject &obj); void WriteImage(const FigObject &obj); void WritePolyline(const FigObject &obj); void WriteSpline(const FigObject &obj); void WriteText(const FigObject &obj); void WriteArc(const FigObject &obj); void WriteStroke(const FigObject &obj); void WriteFill(const FigObject &obj); void WriteLineStyle(const FigObject &obj); void WriteArrows(const FigObject &obj); double X(double x); double Y(double y); unsigned int rgbColor(int colornum); private: FILE *iXml; const std::string iFigName; double iMagnification; double iUnitsPerPoint; const unsigned int *iUserColors; }; double FigWriter::X(double x) { return (x / iUnitsPerPoint) * iMagnification; } double FigWriter::Y(double y) { return MEDIABOX_HEIGHT - X(y); } unsigned int FigWriter::rgbColor(int colornum) { if (colornum < 0 || colornum >= NCOLORS) colornum = 0; if (colornum < NFIXEDCOLORS) return ColorTable[colornum]; else return iUserColors[colornum - NCOLORS]; } void FigWriter::WriteStroke(const FigObject &obj) { if (obj.iThickness == 0) // no stroke return; unsigned int rgb = rgbColor(obj.iPenColor); fprintf(iXml, " stroke=\"%g %g %g\"", ((rgb >> 16) & 0xff) / 255.0, ((rgb >> 8) & 0xff) / 255.0, (rgb & 0xff) / 255.0); } void FigWriter::WriteFill(const FigObject &obj) { // unfilled if (obj.iAreaFill == -1) return; int fill = obj.iAreaFill; if (fill > 40) { fprintf(stderr, "WARNING: fill pattern %d replaced by solid filling.\n", fill); fill = 20; } if (obj.iFillColor < 1) { // BLACK & DEFAULT fprintf(iXml, " fill=\"%g\"", 1.0 - (fill / 20.0)); } else { unsigned int rgb = rgbColor(obj.iFillColor); double r = ((rgb >> 16) & 0xff) / 255.0; double g = ((rgb >> 8) & 0xff) / 255.0; double b = (rgb & 0xff) / 255.0; if (fill < 20) { // mix down to black double scale = fill / 20.0; r *= scale; g *= scale; b *= scale; } else if (fill > 20) { // mix up to white double scale = (40 - fill) / 20.0; // 40 is pure white r = 1.0 - (1.0 - r) * scale; g = 1.0 - (1.0 - g) * scale; b = 1.0 - (1.0 - b) * scale; } fprintf(iXml, " fill=\"%g %g %g\"", r, g, b); } } void FigWriter::WriteLineStyle(const FigObject &obj) { if (obj.iThickness == 0) return; fprintf(iXml, " pen=\"%g\"", iMagnification * 72.0 * (obj.iThickness / 80.0)); switch (obj.iLinestyle) { case -1: // Default case 0: // Solid break; case 1: fprintf(iXml, " dash=\"dashed\""); break; case 2: fprintf(iXml, " dash=\"dotted\""); break; case 3: fprintf(iXml, " dash=\"dash dotted\""); break; case 4: fprintf(iXml, " dash=\"dash dot dotted\""); break; case 5: // Dash-triple-dotted (maybe put this in a stylesheet) fprintf(iXml, " dash=\"[4 2 1 2 1 2 1 2] 0\""); break; } } void FigWriter::WriteArrows(const FigObject &obj) { if (obj.iForwardArrow) fprintf(iXml, " arrow=\"%g\"", X(obj.iForward.iHeight)); if (obj.iBackwardArrow) fprintf(iXml, " backarrow=\"%g\"", X(obj.iBackward.iHeight)); } // -------------------------------------------------------------------- class DepthCompare { public: DepthCompare(const std::vector &objects) : iObjects(objects) { /* nothing */ } int operator()(int lhs, int rhs) const { return (iObjects.at(lhs).iDepth > iObjects.at(rhs).iDepth); } private: const std::vector &iObjects; }; void FigWriter::WriteObjects(const std::vector &objects, int start, int fin) { // collect indices of objects std::vector objs; int i = start; while (i < fin) { objs.push_back(i); if (objects.at(i).iType == 6) i = objects.at(i).iSubtype; // link to END OF COMPOUND ++i; } // now sort the objects DepthCompare comp(objects); std::stable_sort(objs.begin(), objs.end(), comp); // now render them for (unsigned int j = 0; j < objs.size(); ++j) { i = objs[j]; if (i<0 || i >= (int)objects.size()) continue; switch (objects[i].iType) { case 1: // ELLIPSE WriteEllipse(objects[i]); break; case 2: // POLYLINE WritePolyline(objects[i]); break; case 3: // SPLINE WriteSpline(objects[i]); break; case 4: // TEXT WriteText(objects[i]); break; case 5: // ARC WriteArc(objects[i]); break; case 6: // COMPOUND fprintf(iXml, "\n"); // recursively render elements of the compound WriteObjects(objects, i+1, objects[i].iSubtype); fprintf(iXml, "\n"); break; } } } // -------------------------------------------------------------------- void FigWriter::WriteEllipse(const FigObject &obj) { if (obj.iThickness == 0 && obj.iAreaFill==-1 ) { fprintf(stderr, "WARNING: ellipse with neither fill nor line ignored.\n"); return; } fprintf(iXml, "\n"); const double ca = cos(obj.iAngle); const double sa = sin(obj.iAngle); fprintf(iXml, "%g %g %g %g %g %g e\n\n", X( obj.iRadius.iX * ca), X( obj.iRadius.iX * sa), X(-obj.iRadius.iY * sa), X( obj.iRadius.iY * ca), X( obj.iCenterX), Y( obj.iCenterY) ); } // -------------------------------------------------------------------- namespace { unsigned int JPEG_read1( std::ifstream& jpeg_in ) { unsigned char c; jpeg_in.read( reinterpret_cast(&c), 1 ); if( jpeg_in.fail() ) throw std::string( "Failed to read 1 byte." ); return c; } unsigned int JPEG_read2( std::ifstream& jpeg_in ) { return (JPEG_read1(jpeg_in) << 8) + JPEG_read1(jpeg_in); } typedef enum { jpeg_SOF0 = 0xC0, jpeg_SOF1 = 0xC1, jpeg_SOF2 = 0xC2, jpeg_SOF3 = 0xC3, jpeg_SOF5 = 0xC5, jpeg_SOF6 = 0xC6, jpeg_SOF7 = 0xC7, jpeg_SOF9 = 0xC9, jpeg_SOF10 = 0xCA, jpeg_SOF11 = 0xCB, jpeg_SOF13 = 0xCD, jpeg_SOF14 = 0xCE, jpeg_SOF15 = 0xCF, jpeg_SOI = 0xD8, jpeg_EOI = 0xD9, jpeg_SOS = 0xDA, jpeg_APP0 = 0xE0, jpeg_APP12 = 0xEC, jpeg_COM = 0xFE } JPEG_markers; int JPEG_next_marker( std::ifstream& jpeg_in ) { unsigned int c; do { c = JPEG_read1(jpeg_in); } while( c != 0xFF ); do { c = JPEG_read1(jpeg_in); } while( c == 0xFF ); return c; } void JPEG_skip_segment( std::ifstream& jpeg_in ) { unsigned int l = JPEG_read2(jpeg_in); jpeg_in.seekg( l-2, std::ios::cur ); } typedef enum { IMAGE_GRAY=0, IMAGE_RGB=1, IMAGE_CMYK=2 } IPE_COLORSPACE; bool ReadJPEGData( std::ifstream& jpeg_in, int& image_width, int& image_height, int& image_colorspace, int& bitspercomponent ) { int required_segments = 0; try { const unsigned int soi = JPEG_read2(jpeg_in); if( (soi & 0xFF) != jpeg_SOI ) return false; while( required_segments != 3 && !jpeg_in.eof() ) { const unsigned int marker = JPEG_next_marker(jpeg_in); switch( marker ) { case jpeg_APP0: { unsigned int l = JPEG_read2(jpeg_in); const char* JFIF = "JFIF"; for( int i=0; i<5; ++i ) if( JFIF[i] != (int)JPEG_read1(jpeg_in) ) return false; jpeg_in.seekg( l-5-2, std::ios::cur ); required_segments |= 1; break; } case jpeg_SOF0: case jpeg_SOF1: case jpeg_SOF2: case jpeg_SOF3: { unsigned int l = JPEG_read2(jpeg_in); bitspercomponent = JPEG_read1(jpeg_in); image_height = JPEG_read2(jpeg_in); image_width = JPEG_read2(jpeg_in); unsigned int ncomponents = JPEG_read1(jpeg_in); switch( ncomponents ) { case 1: image_colorspace = IMAGE_GRAY; break; case 3: image_colorspace = IMAGE_RGB; break; case 4: image_colorspace = IMAGE_CMYK; break; default: return false; } if( l != 8 + 3*ncomponents ) throw std::string("Unexpected SOFx length."); required_segments |= 2; break; } default: JPEG_skip_segment(jpeg_in); } } } catch( std::string msg ) { fprintf(stderr, "Error while reading JPEG: %s.\n", msg.c_str() ); } return required_segments == 3; } std::string make_safe_filename( const std::string& filename ) { std::ostringstream s_sf; s_sf << '\''; for( unsigned i=0; i0 && lastdash != std::string::npos ) filename = iFigName.substr( 0, lastdash+1 ); filename += obj.image_filename; // try to build safe filename const std::string safe_filename = make_safe_filename( filename ); // image data to be filled by reading procedure int image_width=0, image_height=0; int image_colorspace = IMAGE_RGB; int bitspercomponent = 8; std::string image_data, filter; std::ifstream jpeg_in( filename.c_str() ); // try reading a JPEG file if( ReadJPEGData( jpeg_in, image_width, image_height, image_colorspace, bitspercomponent ) ) { // determine file size jpeg_in.seekg( 0, std::ios::end ); const unsigned jpeg_size = jpeg_in.tellg(); jpeg_in.seekg( 0, std::ios::beg ); // copy file contents image_data.resize( jpeg_size ); jpeg_in.read( const_cast(image_data.data()), jpeg_size ); if( jpeg_in.fail() ) { fprintf(stderr, "WARNING: reading jpeg data failed. Skipping image.\n"); return; } filter = "DCTDecode"; } else { // other images are read by anytopnm and then compressed // build anytopnm command line std::string cmd = "anytopnm "; cmd += safe_filename; // open pipe to read anytopnm output FILE* anytopnm = popen( cmd.c_str(), "r" ); // running anytopnm somehow failed if( !anytopnm ) { fprintf(stderr, "WARNING: anytopnm failed to run. Skipping image.\n"); return; } try { // check image type; should be P4 (pbm), P5 (pgm) or P6 (ppm) char type_P, fmt; if( fscanf( anytopnm, "%c%c", &type_P, &fmt ) != 2 ) throw std::string("anytopnm output not understood"); if( type_P!='P' || ( fmt!='4' && fmt!='5' && fmt!='6' ) ) throw std::string("anytopnm output not understood"); if( fscanf( anytopnm, "%d %d", &image_width, &image_height ) != 2 ) throw std::string("anytopnm output not understood (w&h)"); if( image_width<=0 || image_width>5000 ) throw std::string("image width out of range [0,5000])"); if( image_height<=0 || image_height>5000 ) throw std::string("image height out of range [0,5000])"); if( fmt=='4' ) { // bitmap throw std::string("bitmap not implemented"); } else { int maxcolor; if( fscanf( anytopnm, "%d", &maxcolor ) != 1 ) throw std::string("anytopnm output not understood (maxcolor)"); if( maxcolor<=0 || maxcolor>65535 ) throw std::string("anytopnm output not understood (maxcolor)"); bitspercomponent = 8; image_colorspace = (fmt=='5') ? IMAGE_GRAY : IMAGE_RGB; // skip single whitespace (void)fgetc( anytopnm ); std::ostringstream idata; for( int r=0; r=256 ) { int component2 = fgetc( anytopnm ); if( component2<0 ) throw std::string("anytopnm output: eof"); if( maxcolor == 65535 ) { component = component2; } else { component = (component<<8) | component2; component = int(255.0*component/float(maxcolor)); } } else if( maxcolor!=255 ) { component = int(255.0*component/float(maxcolor)); } const unsigned char c = (unsigned char)component; idata.write( (const char*)&c, 1 ); } } } image_data = idata.str(); } pclose( anytopnm ); } catch( std::string msg ) { pclose( anytopnm ); fprintf(stderr, "WARNING: anytopnm problem. Skipping image.\n"); return; } if( true ) { // compress image const uLongf zbuf_size = compressBound( image_data.size() ); uLongf compressed_size = zbuf_size; char zbuf[zbuf_size]; const int ok = compress2( (Bytef*)zbuf, &compressed_size, (const Bytef*)image_data.data(), image_data.size(), 9 ); if( ok == Z_OK ) { image_data = std::string(zbuf, zbuf+compressed_size); filter = "FlateDecode"; } else { fprintf(stderr, "Failed to compress image (%d)." " Will store uncompressed image.\n", ok ); } } } if( bitspercomponent != 8 ) { fprintf(stderr, "WARNING: Unsupported n.o. bits per component. Skipping image.\n"); return; } fprintf(iXml, "0 && image_height>0 ) fprintf(iXml, "width=\"%d\" height=\"%d\" ", image_width, image_height ); const char* image_colorspaces[3] = { "DeviceGray", "DeviceRGB", "DeviceCMYK" }; fprintf(iXml, "ColorSpace=\"%s\" BitsPerComponent=\"8\" ", image_colorspaces[image_colorspace] ); if( !filter.empty() ) fprintf(iXml, " length=\"%ld\" Filter=\"%s\"", long(image_data.size()), filter.c_str() ); const double x1 = X(obj.iPoints[0].iX), y1 = Y(obj.iPoints[0].iY); const double x2 = X(obj.iPoints[2].iX), y2 = Y(obj.iPoints[2].iY); if( obj.image_flipped ) { const double r = (x2-x1)/(y2-y1); const double tx = (x1+x2)/2, ty = (y1+y2)/2; fprintf(iXml, " matrix=\"0 %f %f 0 %f %f\"", 1/r, r, tx-r*ty, ty-tx/r ); } fprintf(iXml, " rect=\"%g %g %g %g\"", x1, y1, x2, y2 ); fprintf(iXml, ">\n" ); int linebreak=0; for( unsigned i=0; i 0 ) fputc( '\n', iXml ); fprintf(iXml, "\n"); return; } void FigWriter::WritePolyline(const FigObject &obj) { /* 1: polyline 2: box 3: polygon 4: arc-box 5: imported-picture bounding-box */ if (obj.iPoints.size() < 2) { fprintf(stderr, "WARNING: polyline with less than two vertices ignored.\n"); return; } if (obj.iThickness == 0 && obj.iAreaFill==-1 ) { fprintf(stderr, "WARNING: polyline with neither fill nor line ignored.\n"); return; } if (obj.iSubtype == 4) fprintf(stderr, "WARNING: turning arc-box into rectangle.\n"); if (obj.iSubtype == 5) { WriteImage( obj ); return; } fprintf(iXml, "\n"); for (unsigned int i = 0; i < obj.iPoints.size(); ++i) { double x = obj.iPoints[i].iX; double y = obj.iPoints[i].iY; if (i == 0) { fprintf(iXml, "%g %g m\n", X(x), Y(y)); } else if (i == obj.iPoints.size() - 1 && obj.iSubtype > 1) { fprintf(iXml, "h\n"); // close path } else { fprintf(iXml, "%g %g l\n", X(x), Y(y)); } } fprintf(iXml, "\n"); } void FigWriter::WriteSpline(const FigObject &obj) { /* 0: opened approximated spline 1: closed approximated spline 2: opened interpolated spline 3: closed interpolated spline 4: opened x-spline (FIG 3.2) 5: closed x-spline (FIG 3.2) */ FigObject obj1 = obj; obj1.iJoinStyle = 0; obj1.iSubtype = (obj.iSubtype & 1) ? 3 : 1; fprintf(stderr, "WARNING: spline replaced by polyline.\n"); WritePolyline(obj1); } void FigWriter::WriteText(const FigObject &obj) { const double tx = X(obj.iPos.iX), ty = Y(obj.iPos.iY); fprintf(iXml, ""); int font = obj.iFont; if (obj.iFontFlags & 2) { // "special" Latex text font = 0; // needs no special treatment } else if (obj.iFontFlags & 4) { // Postscript font: set font to default font = 0; fprintf(stderr, "WARNING: postscript font ignored.\n"); } switch (font) { case 0: // Default font fprintf(iXml, "%s", &obj.iString[0]); break; case 1: // Roman fprintf(iXml, "\\textrm{%s}", &obj.iString[0]); break; case 2: // Bold fprintf(iXml, "\\textbf{%s}", &obj.iString[0]); break; case 3: // Italic fprintf(iXml, "\\emph{%s}", &obj.iString[0]); break; case 4: // Sans Serif fprintf(iXml, "\\textsf{%s}", &obj.iString[0]); break; case 5: // Typewriter fprintf(iXml, "\\texttt{%s}", &obj.iString[0]); break; } fprintf(iXml, "\n"); } void FigWriter::WriteArc(const FigObject &obj) { if (obj.iThickness == 0 && obj.iAreaFill==-1 ) { fprintf(stderr, "WARNING: arc with neither fill nor line ignored.\n"); return; } // 0: pie-wedge (closed); 1: open ended arc fprintf(iXml, "\n"); Point beg = (obj.iDirection == 0) ? obj.iArc3 : obj.iArc1; Point end = (obj.iDirection == 0) ? obj.iArc1 : obj.iArc3; fprintf(iXml, "%g %g m\n", X(beg.iX), Y(beg.iY)); double dx = obj.iArc1.iX - obj.iCenterX; double dy = obj.iArc1.iY - obj.iCenterY; double radius = sqrt(dx*dx + dy*dy); fprintf(iXml, "%g 0 0 %g %g %g %g %g a\n\n", X(radius), X(radius), X(obj.iCenterX), Y(obj.iCenterY), X(end.iX), Y(end.iY)); } // -------------------------------------------------------------------- static void print_help_message() { fprintf(stderr, "figtoipe is part of the extensible drawing editor Ipe.\n" " Copyright (C) 1993-2019 Otfried Cheong \n" " This is free software with ABSOLUTELY NO WARRANTY.\n" "\n" "Use: figtoipe [-g] [-c] [-p preamble] \n" " converts a file in FIG format to Ipe's XML format\n" " -g -- puts the produced figure into a group\n" " -c -- use cropbox for size of figure\n" " -6 -- write in ipe 6 format instead of ipe 7 format\n" " -p preamble -- inserts a preamble (e.g. '\\usepackage{amsmath}')\n" ); exit(9); } // -------------------------------------------------------------------- static bool simple_getopt( const char* option, bool has_arg, const char* &value, int &argc, char** argv ) { bool found = false; for( int a=1; a\n" "\n"); } fprintf(xml, "\n", ipe7 ? "70000" : "60028", FIGTOIPE_VERSION); if( ipe7 ) { fprintf(xml, "\n"); } else { fprintf(xml, "\n", 0, 0, MEDIABOX_WIDTH, MEDIABOX_HEIGHT, cropbox ? " bbox=\"cropbox\"" : ""); } if( !preamble.empty() ) fprintf(xml, "%s\n\n", preamble.c_str()); if( ipe7 ) { fprintf(xml, "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n" "\n"); fprintf(xml, "\n\n\n", MEDIABOX_WIDTH, MEDIABOX_HEIGHT, MEDIABOX_WIDTH, MEDIABOX_HEIGHT, cropbox ? "" : " crop=\"no\""); } fprintf(xml, "\n"); if( group ) fprintf(xml, "\n"); fw.WriteObjects(fr.Objects(), 0, fr.Objects().size()); if( group ) fprintf(xml, "\n"); fprintf(xml, "\n"); fprintf(xml, "\n"); fclose(xml); return 0; } // -------------------------------------------------------------------- ipe-tools-7.2.24.1/figtoipe/readme.txt000066400000000000000000000026531420423641200174700ustar00rootroot00000000000000 Figtoipe ======== This is Figtoipe, a program that reads FIG files (as generated by xfig) and generates an XML file readable by Ipe. Compile by saying make A changelog is in the source file "figtoipe.cpp". Before reporting a bug, check that you have the latest version, and check that it is not yet mentioned in the FAQ on the Ipe webpage. You can report bugs on the issue tracking system at "https://github.com/otfried/ipe-tools/issues". Check the existing reports to see whether your bug has already been reported. Please do not send bug reports directly to us (the first thing we would do with the report is to enter it into the bug tracking system). Suggestions for features, or random comments on Figtoipe can be sent to the Ipe discussion mailing list at . You can also send suggestions or comments directly to us by Email, but you should then not expect a reply. Alexander Bürger, acfb@users.sourceforge.net Otfried Cheong, ipe@otfried.org Ipe webpage: http://ipe.otfried.org -------------------------------------------------------------------- figtoipe comes with ABSOLUTELY NO WARRANTY. It 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. See the file gpl.txt for details. ipe-tools-7.2.24.1/gpl.txt000066400000000000000000001045131420423641200152050ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, 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 them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ipe-tools-7.2.24.1/ipe5toxml/000077500000000000000000000000001420423641200156045ustar00rootroot00000000000000ipe-tools-7.2.24.1/ipe5toxml/Makefile000066400000000000000000000004761420423641200172530ustar00rootroot00000000000000# # Makefile for ipe5toxml # ifdef COMSPEC TARGET = ipe5toxml.exe LDFLAGS = -mconsole else TARGET = ipe5toxml endif LIBS = -lm all: $(TARGET) sources = ipe5toxml.c objects = ipe5toxml.o $(TARGET): $(objects) $(CC) $(LDFLAGS) -o $(TARGET) $(objects) $(LIBS) .PHONY: clean clean: @-rm -f $(objects) $(TARGET) ipe-tools-7.2.24.1/ipe5toxml/ipe5toxml.1000066400000000000000000000006511420423641200176160ustar00rootroot00000000000000.TH IPE5TOXML "1" "December 2011" "Ipe" "User Commands" .SH NAME ipe5toxml \- Convert Ipe 5 file to Ipe 6 format .SH SYNOPSIS .B ipe5toxml \fIfile.ipe file.xml\fR .SH DESCRIPTION \fBipe5toxml\fR converts an Ipe version 5 format file to one understood by Ipe version 6. Use \fBipe6upgrade\fR to convert the output XML file to one understood by Ipe version 7. .SH AUTHOR Otfried Cheong .SH "SEE ALSO" \fBipe6upgrade\fR(1) ipe-tools-7.2.24.1/ipe5toxml/ipe5toxml.c000066400000000000000000001012061420423641200176760ustar00rootroot00000000000000/* * ipe5toxml.c * * This program converts files in Ipe format (as used by Ipe up to * version 5.0) to XML format as used by Ipe 6.0. */ #define IPE5TOXML_VERSION "ipe5toxml 2015/04/04" #include #include #include #include #include /* Ipe Object Types */ #define IPE_LINE 0 #define IPE_TEXT 1 #define IPE_CIRCLE 2 #define IPE_MARK 3 #define IPE_ARC 4 #define IPE_BITMAP 5 #define IPE_SPLINE 6 #define IPE_BEGINGROUP 7 #define IPE_ENDGROUP 8 #define IPE_SEGMENTS 9 const double SPLINE_MULTI_THRESHOLD_SQUARED = 0.01; const double IpePi = 3.1415926535897932385; /* Ipe Color Type the empty color has entry -1 for red */ typedef struct _IpeColor { double red, green, blue; } IpeColor; /* Ipe Font Type */ #define IPE_ROMAN 0 #define IPE_ITALIC 1 #define IPE_BOLD 2 #define IPE_MATH 3 typedef int bool; typedef struct { double x, y; } vertex; typedef struct { double xmin, xmax, ymin, ymax; } bbox; /* the Environment where the IUM is called */ typedef struct _IpeEnvironment { IpeColor stroke, fill; /* current stroke and fill colors */ unsigned short linestyle; /* solid, dashed etc. as 16 bits */ double linewidth; /* linewidth of stroke */ short arrow; /* two bits for two arrows */ double arsize; /* size of arrows */ double marksize; /* size of marks */ double gridsize; /* grid size */ double snapangle; /* snap angle */ short marktype; /* type of mark (1 .. 5) */ short font; /* font of text object */ double fontsize; /* fontsize */ bool axisset; /* is an axis system defined ? */ vertex origin; /* if so, this is the origin */ double axisdir; /* and this the base direction */ } IpeEnvironment; typedef struct _Line { /* also used for splines */ bool closed; /* true if closed curve (polygon) */ short arrow; /* two bits for two arrows */ double arsize; /* size of arrows */ int n; /* number of vertices */ vertex *v; /* pointer to array of vertices */ char *vtype; /* pointer to array of vertex type */ /* indicators N L E C */ } Line; typedef struct _Circle { vertex center; /* center of circle */ double radius; /* radius of circle */ bool ellipse; /* is object an ellipse ? */ double tfm[4]; /* tfm values from file */ } Circle; typedef struct _Mark { vertex pos; /* position */ short type; /* type of mark (1 .. 5) */ double size; /* size of mark */ } Mark; typedef struct _Arc { short arrow; /* two bits for two arrows */ double arsize; /* size of arrows */ vertex center; /* center of arc */ double radius; /* radius of arc */ double begangle, endangle; /* two angles in radians */ } Arc; typedef struct _Text { char *str; /* the string */ short font; /* font */ double fontsize; /* LaTeX fontsize */ vertex pos; /* position of text */ bool minipage; /* true if text is minipage */ vertex ll, ur; /* ll and ur vertex of bounding box*/ } Text; typedef struct _Bitmap { vertex ll, ur; /* lower left, upper right corner */ short width, height; /* no of bits in bitmap */ unsigned long *words; /* pointer to width*height pixels */ bool in_color; /* color bitmap ? */ } Bitmap; typedef struct _IpeObject { int type; /* type of this object */ IpeColor stroke, fill; /* stroke and fill color of object */ unsigned short linestyle; /* solid, dashed etc. as 16 bits */ double linewidth; /* linewidth of stroke */ struct _IpeObject *next; /* pointer to next object */ union W { Line *line; Circle *circle; Mark *mark; Text *text; Arc *arc; Bitmap *bitmap; } w ; } IpeObject; /* the current Ipe environment when IUM is called */ extern IpeEnvironment ipe_environment; /* input and output for IUM */ extern IpeObject *ium_input; /* selected objects from Ipe */ extern IpeObject *ium_output; /* IUM generated things to Ipe */ #define MAX_LINE_LENGTH 1024 #define FALSE 0 #define TRUE 1 #ifdef __cplusplus #define NEWOBJECT(Type) (new Type) #define NEWARRAY(Type, Size) (new Type[Size]) #define FREEARRAY(Ptr) delete [] Ptr #else #define NEWOBJECT(Type) ((Type *) malloc(sizeof(Type))) #define NEWARRAY(Type, Size) ((Type *) malloc(Size * sizeof(Type))) #define FREEARRAY(Ptr) free(Ptr) #endif #ifdef sun #define BITS char #else #define BITS void #endif #define SETXY(v, x, y) v.x = x; v.y = y #define XCOORD x #define YCOORD y /* the current Ipe environment when IUM is called */ IpeEnvironment ipe_environment; /* IUM input and output */ IpeObject *ium_input; IpeObject *ium_output; static char *ipename; static char *xmlname; static FILE *fh; static char linebuf[MAX_LINE_LENGTH]; static int grouplevel = 0; static int firstpage = 0; static bool in_settings = TRUE; /******************** reading ******************************************/ static void assert_n(int n_expected, int n_actual) { if (n_expected != n_actual) { fprintf(stderr, "Fatal error: failed to parse input\n"); exit(9); } return; } static void assert_fgets(char* s, int size, FILE* stream) { if (fgets(s, size, stream) != s) { fprintf(stderr, "Fatal error: failed to read input\n"); exit(9); } return; } static char *read_next(void) { int ch2, ch1, ch; char *p; /* read words until we find a sole "%" */ ch2 = ch1 = ch = ' '; while ((ch = fgetc(fh)) != EOF && !(ch1 == '%' && isspace(ch2) && isspace(ch))) { ch2 = ch1; ch1 = ch; } if (ch == EOF) { /* read error (could be EOF, then it's a format error) */ fprintf(stderr, "Error reading IPE file %s\n", ipename); exit(9); } /* next word is keyword */ p = linebuf; while ((*p = fgetc(fh)), *p != EOF && (p < (linebuf + sizeof(linebuf) -1)) && !isspace(*p)) p++; *p = '\0'; /* fprintf(stderr, " %s ", linebuf);*/ return linebuf; } /* temporary storage for read_env and read_entry */ static struct { bool closed; vertex xy; bool minipage; double wd, ht, dp; double radius; double begangle, endangle; char *str; int n; vertex *v; char *vtype; bool ellipse; double tfm[4]; unsigned long *words; int xbits, ybits; bool bmcolor; } rd; static void read_env(IpeEnvironment *ienv) { char *wk; double x, y; int i; ienv->stroke.red = ienv->stroke.green = ienv->stroke.blue = -1; ienv->fill.red = ienv->fill.green = ienv->fill.blue = -1; ienv->linestyle = 0; ienv->linewidth = 0.4; ienv->arrow = 0; ienv->axisset = FALSE; rd.closed = FALSE; /* init transformation to identity */ rd.tfm[0] = rd.tfm[3] = 1.0; rd.tfm[1] = rd.tfm[2] = 0.0; rd.minipage = FALSE; rd.ellipse = FALSE; rd.str = NULL; rd.v = NULL; rd.vtype = NULL; while (TRUE) { wk = read_next(); if (!strcmp(wk, "sk")) { assert_n(1, fscanf(fh, "%lf", &ienv->stroke.red)); ienv->stroke.blue = ienv->stroke.green = ienv->stroke.red; } else if (!strcmp(wk, "fi")) { assert_n(1, fscanf(fh, "%lf", &ienv->fill.red)); ienv->fill.blue = ienv->fill.green = ienv->fill.red; } else if (!strcmp(wk, "skc")) { assert_n(3, fscanf(fh, "%lf%lf%lf", &ienv->stroke.red, &ienv->stroke.green, &ienv->stroke.blue)); } else if (!strcmp(wk, "fic")) { assert_n(3, fscanf(fh, "%lf%lf%lf", &ienv->fill.red, &ienv->fill.green, &ienv->fill.blue)); } else if (!strcmp(wk, "ss")) { assert_n(2, fscanf(fh, "%hu%lf", &ienv->linestyle, &ienv->linewidth)); } else if (!strcmp(wk, "ar")) { assert_n(2, fscanf(fh, "%hu%lf", &ienv->arrow, &ienv->arsize)); } else if (!strcmp(wk, "cl")) { rd.closed = TRUE; } else if (!strcmp(wk, "f")) { assert_n(2, fscanf(fh, "%hu%lf", &ienv->font, &ienv->fontsize)); } else if (!strcmp(wk, "grid")) { assert_n(2, fscanf(fh, "%lf%lf", &ienv->gridsize, &ienv->snapangle)); } else if (!strcmp(wk, "ty")) { assert_n(1, fscanf(fh, "%hu", &ienv->marktype)); } else if (!strcmp(wk, "sz")) { assert_n(1, fscanf(fh, "%lf", &ienv->marksize)); } else if (!strcmp(wk, "xy")) { assert_n(2, fscanf(fh, "%lf%lf", &x, &y)); SETXY(rd.xy, x, y); } else if (!strcmp(wk, "px")) { assert_n(2, fscanf(fh, "%d%d", &rd.xbits, &rd.ybits)); } else if (!strcmp(wk, "bb")) { rd.minipage = TRUE; assert_n(2, fscanf(fh, "%lf%lf", &rd.wd, &rd.ht)); rd.dp = rd.ht; } else if (!strcmp(wk, "tbb")) { rd.minipage = FALSE; assert_n(2, fscanf(fh, "%lf%lf%lf", &rd.wd, &rd.ht, &rd.dp)); } else if (!strcmp(wk, "ang")) { assert_n(2, fscanf(fh, "%lf%lf", &rd.begangle, &rd.endangle)); } else if (!strcmp(wk, "r")) { assert_n(1, fscanf(fh, "%lf", &rd.radius)); } else if (!strcmp(wk, "tfm")) { rd.ellipse = TRUE; assert_n(4, fscanf(fh, "%lf%lf%lf%lf", &rd.tfm[0], &rd.tfm[1], &rd.tfm[2], &rd.tfm[3])); } else if (!strcmp(wk, "axis")) { ienv->axisset = TRUE; assert_n(3, fscanf(fh, "%lf%lf%lf", &x, &y, &ienv->axisdir)); SETXY(ienv->origin, x, y); } else if (!strcmp(wk, "#")) { /* vertices of a polyline */ int ch; assert_n(1, fscanf(fh, "%d", &rd.n)); rd.v = NEWARRAY(vertex, rd.n); rd.vtype = NEWARRAY(char, rd.n); for (i = 0; i < rd.n; i++ ) { assert_n(2, fscanf(fh, "%lf%lf", &x, &y)); SETXY(rd.v[i], x, y); /* find character */ do { ch = fgetc(fh); } while (ch != EOF && isspace(ch) && ch != '\n'); if (ch == '\n') { rd.vtype[i] = ' '; } else { rd.vtype[i] = (char) (ch & 0xff); /* skip to next line */ while ((ch = fgetc(fh)) != EOF && ch != '\n') ; } } } else if (!strcmp(wk, "s")) { /* get string */ assert_fgets(linebuf, MAX_LINE_LENGTH, fh); linebuf[strlen(linebuf) - 1] = '\0'; if (!rd.str) { /* first string */ rd.str = strdup(linebuf); } else { char *ns = NEWARRAY(char, (strlen(rd.str) + strlen(linebuf) + 2)); strcpy(ns, rd.str); strcat(ns, "\n"); strcat(ns, linebuf); free(rd.str); rd.str = ns; } } else if (!strcmp(wk, "bits")) { /* get bitmap */ unsigned long nwords; long i, incolor, nchars; int ch, mode; char *p, *strbits, buf[3]; short red, green, blue; assert_n(2, fscanf(fh, "%ld%d", &nwords, &mode)); incolor = mode & 1; if (mode & 0x8) { /* read RAW bitmap */ do { if ((ch = fgetc(fh)) == EOF) { fprintf(stderr, "EOF while reading RAW bitmap\n"); exit(9); } } while (ch != '\n'); rd.bmcolor = incolor ? TRUE : FALSE; rd.words = NEWARRAY(unsigned long, nwords); if (mode & 0x01) { /* color bitmap: 32 bits per pixel */ if (fread((BITS *) rd.words, sizeof(unsigned long), ((unsigned int) nwords), fh) != nwords) { fprintf(stderr, "Error reading RAW bitmap\n"); exit(9); } } else { /* gray bitmap: 8 bits per pixel */ register char *inp, *end; register unsigned long *out; char *pix = NEWARRAY(char, nwords); if (fread((BITS *) pix, sizeof(char), ((unsigned int) nwords), fh) != nwords) { fprintf(stderr, "Error reading RAW bitmap\n"); exit(9); } /* convert to unsigned longs */ inp = pix; end = pix + nwords; out = rd.words; while (inp < end) { *out++ = (*inp << 16) | (*inp << 8) | (*inp); inp++; } FREEARRAY(pix); } } else { /* read Postscript style bitmap */ nchars = (incolor ? 6 : 2) * nwords; p = strbits = NEWARRAY(char, nchars); for (i = 0; i < nchars; i++) { do { if ((ch = fgetc(fh)) == EOF) { fprintf(stderr, "EOF while reading bitmap\n"); exit(9); } } while (!(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f'))); *p++ = ch; } p = strbits; rd.words = NEWARRAY(unsigned long, nwords); rd.bmcolor = incolor ? TRUE : FALSE; buf[2] = '\0'; for (i = 0; i < ((int) nwords); ) { buf[0] = *p++; buf[1] = *p++; red = ((short) strtol(buf, NULL, 16)); if (incolor) { buf[0] = *p++; buf[1] = *p++; green = ((short) strtol(buf, NULL, 16)); buf[0] = *p++; buf[1] = *p++; blue = ((short) strtol(buf, NULL, 16)); rd.words[i++] = (blue * 0x10000) | (green * 0x100) | red; } else { rd.words[i++] = (red * 0x10000) | (red * 0x100) | red; } } free(strbits); } } else if (!strcmp(wk, "End")) { return; } else { if (in_settings) { /* unknown keyword in settings: ignore this line */ assert_fgets(linebuf, MAX_LINE_LENGTH, fh); } else { /* unknown keyword in an object: this is serious */ fprintf(stderr, "Illegal keyword %s in IPE file %s\n", wk, ipename); exit(9); } } } } static void addtobox(bbox *bb, double x, double y) { if (x < bb->xmin) bb->xmin = x; if (x > bb->xmax) bb->xmax = x; if (y < bb->ymin) bb->ymin = y; if (y > bb->ymax) bb->ymax = y; } static IpeObject *read_entry(bbox *bb) { char *wk; int i; IpeObject *iobj; IpeEnvironment ienv; iobj = NEWOBJECT(IpeObject); iobj->next = NULL; wk = read_next(); if (!strcmp(wk, "Group")) { iobj->type = IPE_BEGINGROUP; grouplevel++; return iobj; } else if (!strcmp(wk, "End")) { iobj->type = IPE_ENDGROUP; grouplevel--; return ((grouplevel >= 0) ? iobj : NULL); } else if (!strcmp(wk, "Line")) { iobj->type = IPE_LINE; } else if (!strcmp(wk, "Segments")) { iobj->type = IPE_SEGMENTS; } else if (!strcmp(wk, "Spline")) { iobj->type = IPE_SPLINE; } else if (!strcmp(wk, "Text")) { iobj->type = IPE_TEXT; } else if (!strcmp(wk, "Circle")) { iobj->type = IPE_CIRCLE; } else if (!strcmp(wk, "Arc")) { iobj->type = IPE_ARC; } else if (!strcmp(wk, "Mark")) { iobj->type = IPE_MARK; } else if (!strcmp(wk, "Bitmap")) { iobj->type = IPE_BITMAP; } else { fprintf(stderr, "Illegal keyword %s in IPE file %s\n", wk, ipename); exit(9); } /* read header, now read data */ read_env(&ienv); /* read data, now fill in object */ iobj->stroke = ienv.stroke; iobj->fill = ienv.fill; iobj->linestyle = ienv.linestyle; iobj->linewidth = ienv.linewidth; switch (iobj->type) { /* we treat polylines and splines alike */ case IPE_LINE: case IPE_SEGMENTS: case IPE_SPLINE: iobj->w.line = NEWOBJECT(Line); iobj->w.line->closed = rd.closed; iobj->w.line->arrow = ienv.arrow; iobj->w.line->arsize = ienv.arsize; iobj->w.line->n = rd.n; iobj->w.line->v = rd.v; { int k; for (k = 0; k < rd.n; k++) addtobox(bb, rd.v[k].x, rd.v[k].y); } iobj->w.line->vtype = rd.vtype; if (iobj->type == IPE_SEGMENTS) { /* check keys */ int i; for (i = 0; i < rd.n; i++) { switch (rd.vtype[i]) { case 'N': case 'E': case 'L': case 'C': /* good */ break; default: fprintf(stderr, "Illegal code '%c' in Segments object\n", rd.vtype[i]); exit(9); } } } break; case IPE_ARC: iobj->w.arc = NEWOBJECT(Arc); iobj->w.arc->arrow = ienv.arrow; iobj->w.arc->arsize = ienv.arsize; iobj->w.arc->center = rd.xy; iobj->w.arc->radius = rd.radius; iobj->w.arc->begangle = rd.begangle; iobj->w.arc->endangle = rd.endangle; /* ignore in bbox computation */ break; case IPE_CIRCLE: iobj->w.circle = NEWOBJECT(Circle); iobj->w.circle->center = rd.xy; iobj->w.circle->radius = rd.radius; iobj->w.circle->ellipse = rd.ellipse; for (i = 0; i < 4; i++) { iobj->w.circle->tfm[i] = rd.tfm[i]; } /* just use bounding box of circle, ignoring tfm */ addtobox(bb, rd.xy.x - rd.radius, rd.xy.y - rd.radius); addtobox(bb, rd.xy.x + rd.radius, rd.xy.y + rd.radius); break; case IPE_MARK: iobj->w.mark = NEWOBJECT(Mark); iobj->w.mark->pos = rd.xy; iobj->w.mark->type = ienv.marktype; iobj->w.mark->size = ienv.marksize; addtobox(bb, rd.xy.x, rd.xy.y); break; case IPE_BITMAP: iobj->w.bitmap = NEWOBJECT(Bitmap); iobj->w.bitmap->ll = rd.xy; iobj->w.bitmap->width = rd.xbits; iobj->w.bitmap->height = rd.ybits; iobj->w.bitmap->words = rd.words; iobj->w.bitmap->in_color = rd.bmcolor; iobj->w.bitmap->ur.x = rd.xy.x + rd.wd; iobj->w.bitmap->ur.y = rd.xy.y + rd.ht; addtobox(bb, rd.xy.x, rd.xy.y); addtobox(bb, rd.xy.x + rd.wd, rd.xy.y + rd.ht); break; case IPE_TEXT: iobj->w.text = NEWOBJECT(Text); iobj->w.text->pos = rd.xy; iobj->w.text->font = ienv.font; iobj->w.text->fontsize = ienv.fontsize; iobj->w.text->minipage = rd.minipage; iobj->w.text->ll = rd.xy; iobj->w.text->ll.y -= rd.dp; iobj->w.text->ur = iobj->w.text->ll; iobj->w.text->ur.x += rd.wd; iobj->w.text->ur.y += rd.ht; iobj->w.text->str = rd.str; addtobox(bb, rd.xy.x, rd.xy.y - rd.dp); addtobox(bb, rd.xy.x + rd.wd, rd.xy.y + rd.ht - rd.dp); break; } /* now set all values in IpeObject, return it */ return iobj; } /******************** writing ******************************************/ static void write_color(IpeColor *color) { if (color->red == color->green && color->red == color->blue) { if (color->red == 0.0) fprintf(fh, "black"); else if (color->red == 1.0) fprintf(fh, "white"); else fprintf(fh, "%g", color->red); } else if (color->red == 1.0 && color->green == 0.0 && color->blue == 0.0) fprintf(fh, "red"); else if (color->red == 0.0 && color->green == 1.0 && color->blue == 0.0) fprintf(fh, "green"); else if (color->red == 0.0 && color->green == 0.0 && color->blue == 1.0) fprintf(fh, "blue"); else fprintf(fh, "%g %g %g", color->red, color->green, color->blue); } static void write_colors(IpeObject *iobj) { if (iobj->stroke.red != -1) { fprintf(fh, " stroke=\""); write_color(&iobj->stroke); fprintf(fh, "\""); } if (iobj->fill.red != -1) { fprintf(fh, " fill=\""); write_color(&iobj->fill); fprintf(fh, "\""); } } static void write_dashes(short dash) { static int p[32]; int len = 0; unsigned int onoff = 1; unsigned int rot = dash; int count = 0; int good; int i; int k = 0; if (!(rot & 0x0001)) rot = 0xffff ^ rot; for (i = 0; i < 16; i++) { if (onoff != (rot & 1)) { p[len++] = count; count = 0; onoff = 1 - onoff; } rot >>= 1; count++; } p[len++] = count; if (onoff) p[len++] = 0; for (i = 0; i < len; i++) p[i+len] = p[i]; // now determine period do { k++; good = TRUE; for (i = 0; i < len; i++) if (p[i] != p[i+k]) { good = FALSE; } } while (!good); // k is period of what we want fprintf(fh, "[%d", p[0]); for (i = 1; i < k; i++) fprintf(fh, " %d", p[i]); fprintf(fh, "] 0"); } static void write_linestyle(IpeObject *iobj) { if (iobj->stroke.red == -1) { fprintf(fh, " dash=\"void\""); } else if (iobj->linestyle != 0 && iobj->linestyle != 0xffff) { fprintf(fh, " dash=\""); write_dashes(iobj->linestyle); fprintf(fh, "\""); } fprintf(fh, " pen=\"%g\"", iobj->linewidth); } static int cmp_spl_vtx(vertex *v0, vertex *v1) { double dx = v1->x - v0->x; double dy = v1->y - v0->y; return (dx*dx + dy*dy < SPLINE_MULTI_THRESHOLD_SQUARED); } static void midpoint(vertex *res, vertex *u, vertex *v) { res->x = 0.5 * (u->x + v->x); res->y = 0.5 * (u->y + v->y); } static void thirdpoint(vertex *res, vertex *u, vertex *v) { res->x = (1.0/3.0) * ((2 * u->x) + v->x); res->y = (1.0/3.0) * ((2 * u->y) + v->y); } static void convert_spline_to_bezier(FILE *fh, int n, vertex *v) { int i; vertex q0, q1, q2, q3; vertex u, w; for (i = 0; i < n - 3; i++ ) { thirdpoint(&q1, &v[i+1], &v[i+2]); thirdpoint(&q2, &v[i+2], &v[i+1]); thirdpoint(&u, &v[i+1], &v[i]); midpoint(&q0, &u, &q1); thirdpoint(&w, &v[i+2], &v[i+3]); midpoint(&q3, &w, &q2); if (i == 0) fprintf(fh, "\n%g %g m\n", q0.x, q0.y); fprintf(fh, "%g %g %g %g %g %g c\n", q1.x, q1.y, q2.x, q2.y, q3.x, q3.y); } } static void write_entry(IpeObject *iobj) /* write a single Ipe Object to output file */ { int i; char *p; switch (iobj->type) { case IPE_BEGINGROUP: if (grouplevel > 0) fprintf(fh, "\n"); else { if (firstpage) { fprintf(fh, "\n\n\n"); firstpage = 0; } else { fprintf(fh, "\n"); } } break; case IPE_SPLINE: fprintf(fh, "w.line->arrow & 2) fprintf(fh, " arrow=\"%g\"", iobj->w.line->arsize); if (iobj->w.line->arrow & 1) fprintf(fh, " backarrow=\"%g\"", iobj->w.line->arsize); fprintf(fh, ">"); if (iobj->w.line->n == 2) { /* line segment */ fprintf(fh, "\n%g %g m\n", iobj->w.line->v[0].x, iobj->w.line->v[0].y); fprintf(fh, "%g %g l\n", iobj->w.line->v[1].x, iobj->w.line->v[1].y); } else if (iobj->w.line->n == 3) { /* quadratic B-spline */ if (iobj->w.line->closed) { /* closed quadratic B-spline */ int i; for (i = 0; i < 3; ++i) { vertex q0, q2; midpoint(&q0, &iobj->w.line->v[i], &iobj->w.line->v[(i+1) % 3]); midpoint(&q2, &iobj->w.line->v[(i+1) % 3], &iobj->w.line->v[(i+2) % 3]); if (i == 0) fprintf(fh, "\n%g %g m", q0.x, q0.y); fprintf(fh, "\n%g %g ", iobj->w.line->v[(i+1) % 3].x, iobj->w.line->v[(i+1) % 3].y); fprintf(fh, "%g %g q", q2.x, q2.y); } fprintf(fh, " h\n"); } else { /* open quadratic B-spline */ vertex q0, q2; midpoint(&q0, &iobj->w.line->v[0], &iobj->w.line->v[1]); midpoint(&q2, &iobj->w.line->v[1], &iobj->w.line->v[2]); fprintf(fh, "\n%g %g m\n", q0.x, q0.y); fprintf(fh, "%g %g ", iobj->w.line->v[1].x, iobj->w.line->v[1].y); fprintf(fh, "%g %g q\n", q2.x, q2.y); } } else if (iobj->w.line->closed) { /* Closed cubic B-spline */ for (i = 0; i < iobj->w.line->n; i++ ) fprintf(fh, "\n%g %g", iobj->w.line->v[i].x, iobj->w.line->v[i].y); fprintf(fh, " u\n"); } else { /* Check whether first and last point have multiplicity 3 */ int n = iobj->w.line->n; if (n >= 8 && cmp_spl_vtx(&iobj->w.line->v[0], &iobj->w.line->v[1]) && cmp_spl_vtx(&iobj->w.line->v[0], &iobj->w.line->v[2]) && cmp_spl_vtx(&iobj->w.line->v[n-1], &iobj->w.line->v[n-2]) && cmp_spl_vtx(&iobj->w.line->v[n-1], &iobj->w.line->v[n-3])) { /* Yes, can convert to Ipe 6 B-Spline object */ fprintf(fh, "\n%g %g m", iobj->w.line->v[2].x, iobj->w.line->v[2].y); for (i = 3; i < iobj->w.line->n - 2; i++ ) fprintf(fh, "\n%g %g", iobj->w.line->v[i].x, iobj->w.line->v[i].y); fprintf(fh, " s\n"); } else { /* Have to convert to Ipe 6 Bezier path */ convert_spline_to_bezier(fh, n, iobj->w.line->v); } } fprintf(fh, "\n"); break; case IPE_LINE: case IPE_SEGMENTS: fprintf(fh, "w.line->arrow & 2) fprintf(fh, " arrow=\"%g\"", iobj->w.line->arsize); if (iobj->w.line->arrow & 1) fprintf(fh, " backarrow=\"%g\"", iobj->w.line->arsize); fprintf(fh, ">\n"); for (i = 0; i < iobj->w.line->n; i++ ) { fprintf(fh, "%g %g ", (iobj->w.line->v[i].x), (iobj->w.line->v[i].y) ); if (iobj->type == IPE_SEGMENTS) { switch (iobj->w.line->vtype[i]) { case 'N': fprintf(fh, "m\n"); break; case 'L': case 'E': fprintf(fh, "l\n"); break; case 'C': fprintf(fh, "l h\n"); break; } } else { if (i == 0) { fprintf(fh, "m\n"); } else if (i + 1 == iobj->w.line->n) { if (iobj->w.line->closed) fprintf(fh, "l h\n"); else fprintf(fh, "l\n"); } else { fprintf(fh, "l\n"); } } } fprintf(fh, "\n"); break; case IPE_MARK: fprintf(fh, "fill.red = -1; write_colors(iobj); fprintf(fh, " pos=\"%g %g\"", (iobj->w.mark->pos.XCOORD), (iobj->w.mark->pos.YCOORD)); fprintf(fh, " shape=\"%d\"", iobj->w.mark->type); fprintf(fh, " size=\"%g\"/>\n", (iobj->w.mark->size)); break; case IPE_CIRCLE: fprintf(fh, "\n"); if (iobj->w.circle->ellipse) { double r = (iobj->w.circle->radius); fprintf(fh, "%g %g %g %g %g %g e\n", r * iobj->w.circle->tfm[0], r * iobj->w.circle->tfm[1], r * iobj->w.circle->tfm[2], r * iobj->w.circle->tfm[3], (iobj->w.circle->center.XCOORD), (iobj->w.circle->center.YCOORD)); } else { fprintf(fh, "%g 0 0 %g %g %g e\n", (iobj->w.circle->radius), (iobj->w.circle->radius), (iobj->w.circle->center.XCOORD), (iobj->w.circle->center.YCOORD)); } fprintf(fh, "\n"); break; case IPE_ARC: // ignore zero radius arcs if (iobj->w.arc->radius != 0.0) { fprintf(fh, "w.arc->arrow & 2) fprintf(fh, " arrow=\"%g\"", (iobj->w.arc->arsize)); if (iobj->w.arc->arrow & 1) fprintf(fh, " backarrow=\"%g\"", (iobj->w.arc->arsize)); fprintf(fh, ">\n"); { double alpha = (iobj->w.arc->begangle * IpePi / 180.0 ); double beta = (iobj->w.arc->endangle * IpePi / 180.0 ); double radius = iobj->w.arc->radius; double x = (iobj->w.arc->center.x); double y = (iobj->w.arc->center.y); while (beta <= alpha) beta += IpePi + IpePi; fprintf(fh, "%g %g m\n", x + radius * cos(alpha), y + radius * sin(alpha)); fprintf(fh, "%g 0 0 %g %g %g ", radius, radius, x, y); fprintf(fh, "%g %g a\n", x + radius * cos(beta), y + radius * sin(beta)); } fprintf(fh, "\n"); } break; case IPE_TEXT: fprintf(fh, "fill.red = -1; write_colors(iobj); fprintf(fh, " pos=\"%g %g\"", (iobj->w.text->pos.XCOORD), (iobj->w.text->pos.YCOORD)); fprintf(fh, " size=\"%.2g\"", iobj->w.text->fontsize); if (iobj->w.text->minipage) { fprintf(fh, " type=\"minipage\" valign=\"top\" width=\"%g\"", (iobj->w.text->ur.XCOORD - iobj->w.text->ll.XCOORD)); } else { fprintf(fh, " type=\"label\" valign=\"bottom\""); } fprintf(fh, ">"); switch (iobj->w.text->font) { case IPE_ROMAN: default: break; case IPE_ITALIC: fprintf(fh, "\\textit{"); break; case IPE_BOLD: fprintf(fh, "\\textbf{"); break; case IPE_MATH: fprintf(fh, "$"); break; } for (p = iobj->w.text->str; *p; p++) { switch (*p) { case '<': fprintf(fh, "<"); break; case '>': fprintf(fh, ">"); break; case '&': fprintf(fh, "&"); break; case '\r': /* skip CR */ break; default: fputc(*p, fh); break; } } switch (iobj->w.text->font) { case IPE_ROMAN: default: break; case IPE_ITALIC: case IPE_BOLD: fprintf(fh, "}"); break; case IPE_MATH: fprintf(fh, "$"); break; } fprintf(fh, "\n"); break; case IPE_BITMAP: fprintf(fh, "w.bitmap->ll.XCOORD, iobj->w.bitmap->ll.YCOORD, iobj->w.bitmap->ur.XCOORD, iobj->w.bitmap->ur.YCOORD); fprintf(fh, " width=\"%d\" height=\"%d\"", iobj->w.bitmap->width, iobj->w.bitmap->height); if (iobj->w.bitmap->in_color) fprintf(fh, " ColorSpace=\"DeviceRGB\""); else fprintf(fh, " ColorSpace=\"DeviceGray\""); fprintf(fh, " BitsPerComponent=\"8\">\n"); /* write bitmap in hex */ if (iobj->w.bitmap->in_color) { int nwords = iobj->w.bitmap->width * iobj->w.bitmap->height; int i; /* write a color bitmap: 32 bits per pixel */ for (i = 0; i < nwords; ++i) { int val = iobj->w.bitmap->words[i] & 0x00ffffff; fprintf(fh, "%06x", val); } } else { int nwords = iobj->w.bitmap->width * iobj->w.bitmap->height; int i; /* write a color bitmap: 32 bits per pixel */ for (i = 0; i < nwords; ++i) { int val = iobj->w.bitmap->words[i] & 0x000000ff; fprintf(fh, "%02x", val ); } } fprintf(fh, "\n\n"); break; default: /* this should never happen */ fprintf(stderr, "Fatal error: trying to write unknown type %d\n", iobj->type); exit(1); } return; } static void ipetoxml(void) { IpeObject *last, *iobj; char *wk; char preamble[MAX_LINE_LENGTH]; char pspreamble[MAX_LINE_LENGTH]; int no_pages = 0; bbox bb = { 99999.0, -99999.0, 99999.0, -99999.0 }; ium_input = NULL; /* read IPE file */ if (!(fh = fopen(ipename, "rb"))) { fprintf(stderr, "Cannot open IPE file %s\n", ipename); exit(9); } grouplevel = 0; preamble[0] = '\0'; pspreamble[0] = '\0'; wk = read_next(); if (!strcmp(wk, "Preamble")) { int nlines; int ch; char *p = preamble; assert_n(1, fscanf(fh, "%d", &nlines)); /* skip to next line */ while ((ch = fgetc(fh)) != '\n' && ch != EOF) ; while (nlines > 0 && ch != EOF && (p < (preamble + sizeof(preamble) - 1))) { ch = fgetc(fh); if (ch == EOF) { fprintf(stderr, "EOF while reading preamble\n"); exit(9); } if (ch != '%') *p++ = ch; if (ch == '\n') nlines--; } *p = '\0'; wk = read_next(); } if (!strcmp(wk, "PSpreamble")) { int nlines; int ch; char *p = pspreamble; assert_n(1, fscanf(fh, "%d", &nlines)); /* skip to next line */ while ((ch = fgetc(fh)) != '\n' && ch != EOF) ; while (nlines > 0 && ch != EOF && (p < (pspreamble + sizeof(pspreamble) -1 ))) { ch = fgetc(fh); if (ch == EOF) { fprintf(stderr, "EOF while reading PSpreamble\n"); exit(9); } *p++ = ch; if (ch == '\n') nlines--; } *p = '\0'; wk = read_next(); } if (!strcmp(wk, "Pages")) { assert_n(1, fscanf(fh, "%d", &no_pages)); } else if (strcmp(wk, "Group")) { fprintf(stderr, "Not an IPE file: %s\n", ipename); exit(9); } last = NULL; while ((iobj = read_entry(&bb)) != NULL) { if (last) last->next = iobj; else ium_input = iobj; last = iobj; } fclose(fh); /* testing only? */ if (xmlname == 0) return; /* write file in XML format */ if (!(fh = fopen(xmlname, "wb"))) { fprintf(stderr, "Cannot open XML file %s for writing\n", xmlname); exit(9); } fprintf(fh, "\n", IPE5TOXML_VERSION); { char *p = preamble; while (*p && *p != '}') p++; if (*p) p++; while (*p && (*p == ' ' || *p == '\n' || *p == '\r')) p++; if (*p) { fprintf(fh, ""); while (*p) { char *q = strstr(p, "\\usepackage{ipe}"); if (q) { while (p < q) fputc(*p++, fh); p = q + 16; } else { while (*p) fputc(*p++, fh); } } fprintf(fh, "\n"); } } /* if (pspreamble[0]) { fprintf(fh, "%s\n", pspreamble); } */ if (no_pages > 0) { firstpage = 1; grouplevel = 0; } else { fprintf(fh, "\n"); grouplevel = 1; } for (iobj = ium_input; iobj; iobj = iobj->next) { write_entry(iobj); } if (no_pages == 0) fprintf(fh, "\n"); fprintf(fh, "\n"); if (fclose(fh) == EOF) { fprintf(stderr, "Write error on XML file %s\n", xmlname); exit(9); } } int main(int argc, char **argv) { if (argc >= 3 && !strcmp(argv[1], "-test")) { /* test mode */ int i; for (i = 2; i < argc; i++) { ipename = argv[i]; xmlname = 0; fprintf(stderr, "Testing %s\n", ipename); ipetoxml(); } } else { if (argc != 3) { /* something is wrong here, we should have exactly two arguments */ fprintf(stderr, "Usage: %s file.ipe file.xml\n", argv[0]); exit(9); } ipename = argv[1]; xmlname = argv[2]; ipetoxml(); } return 0; } ipe-tools-7.2.24.1/ipepython/000077500000000000000000000000001420423641200156755ustar00rootroot00000000000000ipe-tools-7.2.24.1/ipepython/ipepython.cpp000066400000000000000000000506071420423641200204300ustar00rootroot00000000000000// // ipepython.cpp // // Python extension module to use Ipelib through Lua. // // Based on Gustavo Niemeyer's Lunatic Python bridge. // http://labix.org/lunatic-python // #include extern "C" { #include #include #include } // to include a small table with some info about Ipe configuration #define IPELUA_CONFIG 0 // for testing direct execution of Lua code, not used for Ipe bindings #define IPELUA_EVAL 0 #if IPELUA_CONFIG #include "ipebase.h" #endif // -------------------------------------------------------------------- // in ipelua extern "C" int luaopen_ipe(lua_State *L); #define POBJECT "POBJECT" typedef struct { PyObject_HEAD int ref; int refiter; } LuaObject; typedef struct { PyObject *o; int asindx; } py_object; extern PyTypeObject LuaObject_Type; #define LuaObject_Check(op) PyObject_TypeCheck(op, &LuaObject_Type) static lua_State *LuaState = nullptr; // -------------------------------------------------------------------- static int py_convert_custom(lua_State *L, PyObject *o, int asindx) { py_object *obj = (py_object*) lua_newuserdata(L, sizeof(py_object)); if (!obj) luaL_error(L, "failed to allocate userdata object"); Py_INCREF(o); obj->o = o; obj->asindx = asindx; luaL_getmetatable(L, POBJECT); lua_setmetatable(L, -2); return 1; } static int py_convert(lua_State *L, PyObject *o) { int ret = 0; if (o == Py_None) { // Not really needed, but this way we may check for errors with ret == 0. lua_pushnil(L); ret = 1; } else if (o == Py_True) { lua_pushboolean(L, 1); ret = 1; } else if (o == Py_False) { lua_pushboolean(L, 0); ret = 1; } else if (PyUnicode_Check(o) || PyBytes_Check(o)) { PyObject *bstr = PyUnicode_AsEncodedString(o, "utf-8", nullptr); Py_ssize_t len; char *s; PyErr_Clear(); PyBytes_AsStringAndSize(bstr ? bstr : o, &s, &len); lua_pushlstring(L, s, len); if (bstr) Py_DECREF(bstr); ret = 1; } else if (PyLong_Check(o)) { lua_pushnumber(L, (lua_Number)PyLong_AsLong(o)); ret = 1; } else if (PyFloat_Check(o)) { lua_pushnumber(L, (lua_Number)PyFloat_AsDouble(o)); ret = 1; } else if (LuaObject_Check(o)) { lua_rawgeti(L, LUA_REGISTRYINDEX, ((LuaObject*)o)->ref); ret = 1; } else { int asindx = PyDict_Check(o) || PyList_Check(o) || PyTuple_Check(o); ret = py_convert_custom(L, o, asindx); } return ret; } static py_object* luaPy_to_pobject(lua_State *L, int n) { if(!lua_getmetatable(L, n)) return nullptr; luaL_getmetatable(L, POBJECT); int is_pobject = lua_rawequal(L, -1, -2); lua_pop(L, 2); return is_pobject ? (py_object *) lua_touserdata(L, n) : nullptr; } // -------------------------------------------------------------------- static PyObject *LuaObject_New(lua_State *L, int n) { LuaObject *obj = PyObject_New(LuaObject, &LuaObject_Type); if (obj) { lua_pushvalue(L, n); obj->ref = luaL_ref(L, LUA_REGISTRYINDEX); obj->refiter = 0; } return (PyObject*) obj; } static PyObject *LuaConvert(lua_State *L, int n) { PyObject *ret = nullptr; switch (lua_type(L, n)) { case LUA_TNIL: Py_INCREF(Py_None); ret = Py_None; break; case LUA_TSTRING: { size_t len; const char *s = lua_tolstring(L, n, &len); ret = PyUnicode_FromStringAndSize(s, len); if (!ret) { PyErr_Clear(); ret = PyBytes_FromStringAndSize(s, len); } break; } case LUA_TNUMBER: { lua_Number num = lua_tonumber(L, n); if (num != (long)num) { ret = PyFloat_FromDouble(num); } else { ret = PyLong_FromLong((long)num); } break; } case LUA_TBOOLEAN: ret = lua_toboolean(L, n) ? Py_True : Py_False; Py_INCREF(ret); break; case LUA_TUSERDATA: { py_object *obj = luaPy_to_pobject(L, n); if (obj) { Py_INCREF(obj->o); ret = obj->o; break; } // Otherwise go on and handle as custom. } default: ret = LuaObject_New(L, n); break; } return ret; } // -------------------------------------------------------------------- static PyObject *LuaCall(lua_State *L, PyObject *args) { PyObject *ret = nullptr; PyObject *arg; int nargs, rc, i; if (!PyTuple_Check(args)) { PyErr_SetString(PyExc_TypeError, "tuple expected"); lua_settop(L, 0); return nullptr; } nargs = PyTuple_Size(args); for (i = 0; i != nargs; i++) { arg = PyTuple_GetItem(args, i); if (arg == nullptr) { PyErr_Format(PyExc_TypeError, "failed to get tuple item #%d", i); lua_settop(L, 0); return nullptr; } rc = py_convert(L, arg); if (!rc) { PyErr_Format(PyExc_TypeError, "failed to convert argument #%d", i); lua_settop(L, 0); return nullptr; } } if (lua_pcall(L, nargs, LUA_MULTRET, 0) != 0) { PyErr_Format(PyExc_Exception, "error: %s", lua_tostring(L, -1)); return nullptr; } nargs = lua_gettop(L); if (nargs == 1) { ret = LuaConvert(L, 1); if (!ret) { PyErr_SetString(PyExc_TypeError, "failed to convert return"); lua_settop(L, 0); Py_DECREF(ret); return nullptr; } } else if (nargs > 1) { ret = PyTuple_New(nargs); if (!ret) { PyErr_SetString(PyExc_RuntimeError, "failed to create return tuple"); lua_settop(L, 0); return nullptr; } for (i = 0; i != nargs; i++) { arg = LuaConvert(L, i+1); if (!arg) { PyErr_Format(PyExc_TypeError, "failed to convert return #%d", i); lua_settop(L, 0); Py_DECREF(ret); return nullptr; } PyTuple_SetItem(ret, i, arg); } } else { Py_INCREF(Py_None); ret = Py_None; } lua_settop(L, 0); return ret; } // -------------------------------------------------------------------- static int LuaObject_methodCall(lua_State *L) { int n = lua_gettop(L); // number of arguments to method call lua_pushvalue(L, lua_upvalueindex(2)); // push method lua_insert(L, 1); // move to bottom of stack lua_pushvalue(L, lua_upvalueindex(1)); // push object containing the method lua_insert(L, 2); // make it the first argument lua_call(L, n + 1, LUA_MULTRET); return lua_gettop(L); // return results } // -------------------------------------------------------------------- static void LuaObject_dealloc(LuaObject *self) { luaL_unref(LuaState, LUA_REGISTRYINDEX, self->ref); if (self->refiter) luaL_unref(LuaState, LUA_REGISTRYINDEX, self->refiter); Py_TYPE(self)->tp_free((PyObject *)self); } static PyObject *LuaObject_getattr(PyObject *obj, PyObject *attr) { lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); if (lua_isnil(LuaState, -1)) { lua_pop(LuaState, 1); PyErr_SetString(PyExc_RuntimeError, "lost reference"); return nullptr; } if (!lua_isstring(LuaState, -1) && !lua_istable(LuaState, -1) && !lua_isuserdata(LuaState, -1)) { lua_pop(LuaState, 1); PyErr_SetString(PyExc_RuntimeError, "not an indexable value"); return nullptr; } PyObject *ret = nullptr; int rc = py_convert(LuaState, attr); if (rc) { bool byName = lua_type(LuaState, -1) == LUA_TSTRING; lua_gettable(LuaState, -2); if (byName && lua_type(LuaState, -1) == LUA_TFUNCTION && lua_isuserdata(LuaState, -2)) { // We are retrieving a method of an object. // Build a closure packing the method with the object itself lua_pushcclosure(LuaState, LuaObject_methodCall, 2); } ret = LuaConvert(LuaState, -1); } else { PyErr_SetString(PyExc_ValueError, "can't convert attr/key"); } lua_settop(LuaState, 0); return ret; } static int LuaObject_setattr(PyObject *obj, PyObject *attr, PyObject *value) { int ret = -1; int rc; lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); if (lua_isnil(LuaState, -1)) { lua_pop(LuaState, 1); PyErr_SetString(PyExc_RuntimeError, "lost reference"); return -1; } if (!lua_istable(LuaState, -1)) { lua_pop(LuaState, -1); PyErr_SetString(PyExc_TypeError, "Lua object is not a table"); return -1; } rc = py_convert(LuaState, attr); if (rc) { if (nullptr == value) { lua_pushnil(LuaState); rc = 1; } else { rc = py_convert(LuaState, value); } if (rc) { lua_settable(LuaState, -3); ret = 0; } else { PyErr_SetString(PyExc_ValueError, "can't convert value"); } } else { PyErr_SetString(PyExc_ValueError, "can't convert key/attr"); } lua_settop(LuaState, 0); return ret; } static PyObject *LuaObject_str(PyObject *obj) { PyObject *ret = nullptr; const char *s; lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); if (luaL_callmeta(LuaState, -1, "__tostring")) { s = lua_tostring(LuaState, -1); lua_pop(LuaState, 1); if (s) ret = PyUnicode_FromString(s); } if (!ret) { int type = lua_type(LuaState, -1); switch (type) { case LUA_TTABLE: case LUA_TFUNCTION: ret = PyUnicode_FromFormat("", lua_typename(LuaState, type), lua_topointer(LuaState, -1)); break; case LUA_TUSERDATA: case LUA_TLIGHTUSERDATA: ret = PyUnicode_FromFormat("", lua_typename(LuaState, type), lua_touserdata(LuaState, -1)); break; case LUA_TTHREAD: ret = PyUnicode_FromFormat("", lua_typename(LuaState, type), (void*)lua_tothread(LuaState, -1)); break; default: ret = PyUnicode_FromFormat("", lua_typename(LuaState, type)); break; } } lua_pop(LuaState, 1); return ret; } static int LuaObject_pcmp(lua_State *L) { int op = lua_tointeger(L, -3); switch(op) { case Py_EQ: lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPEQ)); break; case Py_NE: lua_pushboolean(L, !lua_compare(L, -2, -1, LUA_OPEQ)); break; case Py_GT: lua_insert(LuaState, -2); // flip order of arguments case Py_LT: lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLT)); break; case Py_GE: lua_insert(LuaState, -2); case Py_LE: lua_pushboolean(L, lua_compare(L, -2, -1, LUA_OPLE)); } return 1; } static PyObject* LuaObject_richcmp(PyObject *lhs, PyObject *rhs, int op) { if (!LuaObject_Check(rhs)) return Py_False; lua_pushcfunction(LuaState, LuaObject_pcmp); lua_pushinteger(LuaState, op); lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)lhs)->ref); lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject *)rhs)->ref); if (lua_pcall(LuaState, 3, 1, 0) != LUA_OK) { PyErr_SetString(PyExc_RuntimeError, lua_tostring(LuaState, -1)); return nullptr; } return lua_toboolean(LuaState, -1) ? Py_True : Py_False; } static PyObject *LuaObject_call(PyObject *obj, PyObject *args) { lua_settop(LuaState, 0); lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); return LuaCall(LuaState, args); } static PyObject *LuaObject_iternext(LuaObject *obj) { PyObject *ret = nullptr; lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); // refuse to iterate on anything except tables, as we haven't implemented it if (lua_type(LuaState, -1) != LUA_TTABLE) return nullptr; if (obj->refiter == 0) lua_pushnil(LuaState); else lua_rawgeti(LuaState, LUA_REGISTRYINDEX, obj->refiter); if (lua_next(LuaState, -2) != 0) { // Remove value. lua_pop(LuaState, 1); ret = LuaConvert(LuaState, -1); // Save key for next iteration. if (!obj->refiter) obj->refiter = luaL_ref(LuaState, LUA_REGISTRYINDEX); else lua_rawseti(LuaState, LUA_REGISTRYINDEX, obj->refiter); } else if (obj->refiter) { luaL_unref(LuaState, LUA_REGISTRYINDEX, obj->refiter); obj->refiter = 0; } return ret; } // -------------------------------------------------------------------- static int LuaObject_length(LuaObject *obj) { int len; lua_rawgeti(LuaState, LUA_REGISTRYINDEX, ((LuaObject*)obj)->ref); len = luaL_len(LuaState, -1); lua_settop(LuaState, 0); return len; } static PyObject *LuaObject_subscript(PyObject *obj, PyObject *key) { return LuaObject_getattr(obj, key); } static int LuaObject_ass_subscript(PyObject *obj, PyObject *key, PyObject *value) { return LuaObject_setattr(obj, key, value); } static PyMappingMethods LuaObject_as_mapping = { (lenfunc)LuaObject_length, /* mp_length */ (binaryfunc)LuaObject_subscript, /* mp_subscript */ (objobjargproc)LuaObject_ass_subscript, /* mp_ass_subscript */ }; // -------------------------------------------------------------------- static int LuaObject_parith(lua_State *L) { int op = lua_tointeger(L, -1); lua_pop(L, 1); // remove op lua_arith(LuaState, op); return 1; } static PyObject *LuaObject_arith(int op, PyObject *lhs, PyObject *rhs) { lua_pushcfunction(LuaState, LuaObject_parith); if (!(py_convert(LuaState, lhs) && (rhs == nullptr || py_convert(LuaState, rhs)))) { PyErr_SetString(PyExc_TypeError, "failed to convert argument"); lua_settop(LuaState, 0); return nullptr; } lua_pushinteger(LuaState, op); if (lua_pcall(LuaState, rhs == nullptr ? 2 : 3, 1, 0) != LUA_OK) { PyErr_SetString(PyExc_RuntimeError, lua_tostring(LuaState, -1)); return nullptr; } return LuaConvert(LuaState, -1); } static PyObject *LuaObject_add(PyObject *lhs, PyObject *rhs) { return LuaObject_arith(LUA_OPADD, lhs, rhs); } static PyObject *LuaObject_subtract(PyObject *lhs, PyObject *rhs) { return LuaObject_arith(LUA_OPSUB, lhs, rhs); } static PyObject *LuaObject_multiply(PyObject *lhs, PyObject *rhs) { return LuaObject_arith(LUA_OPMUL, lhs, rhs); } static PyObject *LuaObject_power(PyObject *lhs, PyObject *rhs) { return LuaObject_arith(LUA_OPPOW, lhs, rhs); } static PyObject *LuaObject_negative(PyObject *rhs) { return LuaObject_arith(LUA_OPUNM, rhs, nullptr); } static PyNumberMethods LuaObject_as_number = { (binaryfunc) LuaObject_add, /* nb_add */ (binaryfunc) LuaObject_subtract, /* nb_subtract */ (binaryfunc) LuaObject_multiply, /* nb_multiply */ nullptr, /* nb_remainder */ nullptr, /* nb_divmod */ nullptr, /* nb_power */ (unaryfunc) LuaObject_negative, /* nb_negative */ nullptr, /* nb_positive */ nullptr, /* nb_absolute */ nullptr, /* nb_bool */ nullptr, /* nb_invert */ nullptr, /* nb_lshift */ nullptr, /* nb_rshift */ nullptr, /* nb_and */ (binaryfunc) LuaObject_power, /* nb_xor */ nullptr, /* nb_or */ nullptr, /* nb_int */ nullptr, /* nb_reserved */ nullptr, /* nb_float */ nullptr, /* nb_inplace_add */ nullptr, /* nb_inplace_subtract */ nullptr, /* nb_inplace_multiply */ nullptr, /* nb_inplace_remainder */ nullptr, /* nb_inplace_power */ nullptr, /* nb_inplace_lshift */ nullptr, /* nb_inplace_rshift */ nullptr, /* nb_inplace_and */ nullptr, /* nb_inplace_xor */ nullptr, /* nb_inplace_or */ nullptr, /* nb_floor_divide */ nullptr, /* nb_true_divide */ nullptr, /* nb_inplace_floor_divide */ nullptr, /* nb_inplace_true_divide */ nullptr, /* nb_index */ nullptr, /* nb_matrix_multiply */ nullptr, /* nb_inplace_matrix_multiply */ }; // -------------------------------------------------------------------- PyTypeObject LuaObject_Type = { PyVarObject_HEAD_INIT(nullptr, 0) "lua.custom", /*tp_name*/ sizeof(LuaObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)LuaObject_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ LuaObject_str, /*tp_repr*/ &LuaObject_as_number, /*tp_as_number*/ 0, /*tp_as_sequence*/ &LuaObject_as_mapping, /*tp_as_mapping*/ 0, /*tp_hash*/ (ternaryfunc)LuaObject_call, /*tp_call*/ LuaObject_str, /*tp_str*/ LuaObject_getattr, /*tp_getattro*/ LuaObject_setattr, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "custom lua object", /*tp_doc*/ 0, /*tp_traverse*/ 0, /*tp_clear*/ LuaObject_richcmp, /*tp_richcompare*/ 0, /*tp_weaklistoffset*/ PyObject_SelfIter, /*tp_iter*/ (iternextfunc)LuaObject_iternext, /*tp_iternext*/ 0, /*tp_methods*/ 0, /*tp_members*/ 0, /*tp_getset*/ 0, /*tp_base*/ 0, /*tp_dict*/ 0, /*tp_descr_get*/ 0, /*tp_descr_set*/ 0, /*tp_dictoffset*/ 0, /*tp_init*/ PyType_GenericAlloc, /*tp_alloc*/ PyType_GenericNew, /*tp_new*/ PyObject_Del, /*tp_free*/ 0, /*tp_is_gc*/ }; // -------------------------------------------------------------------- #if IPELUA_EVAL static PyObject *Lua_run(PyObject *args, int eval) { char *s; int len; if (!PyArg_ParseTuple(args, "s#", &s, &len)) return nullptr; char *buf = nullptr; if (eval) { buf = (char *) malloc(strlen("return ")+len+1); strcpy(buf, "return "); strncat(buf, s, len); s = buf; len = strlen("return ")+len; } if (luaL_loadbuffer(LuaState, s, len, "") != 0) { PyErr_Format(PyExc_RuntimeError, "error loading code: %s", lua_tostring(LuaState, -1)); free(buf); return nullptr; } free(buf); if (lua_pcall(LuaState, 0, 1, 0) != 0) { PyErr_Format(PyExc_RuntimeError, "error executing code: %s", lua_tostring(LuaState, -1)); return nullptr; } PyObject *ret = LuaConvert(LuaState, -1); lua_settop(LuaState, 0); return ret; } static PyObject *Lua_execute(PyObject *self, PyObject *args) { return Lua_run(args, 0); } static PyObject *Lua_eval(PyObject *self, PyObject *args) { return Lua_run(args, 1); } static PyObject *Lua_globals(PyObject *self, PyObject *args) { PyObject *ret = nullptr; lua_getglobal(LuaState, "_G"); if (lua_isnil(LuaState, -1)) { PyErr_SetString(PyExc_RuntimeError, "lost globals reference"); lua_pop(LuaState, 1); return nullptr; } ret = LuaConvert(LuaState, -1); if (!ret) PyErr_Format(PyExc_TypeError, "failed to convert table"); lua_settop(LuaState, 0); return ret; } #endif // -------------------------------------------------------------------- static PyMethodDef ipe_methods[] = { #if IPELUA_EVAL {"execute", Lua_execute, METH_VARARGS, nullptr}, {"eval", Lua_eval, METH_VARARGS, nullptr}, {"globals", Lua_globals, METH_NOARGS, nullptr}, #endif {nullptr, nullptr} }; static struct PyModuleDef ipelua_module = { PyModuleDef_HEAD_INIT, "ipe", "Ipelib interface through Python-Lua bridge", -1, ipe_methods }; // -------------------------------------------------------------------- #if IPELUA_CONFIG static void push_string(lua_State *L, ipe::String str) { lua_pushlstring(L, str.data(), str.size()); } static void setup_config(lua_State *L) { lua_getglobal(L, "ipe"); lua_newtable(L); // config table push_string(L, ipe::Platform::latexDirectory()); lua_setfield(L, -2, "latexdir"); lua_pushinteger(L, ipe::IPELIB_VERSION); lua_setfield(L, -2, "version"); lua_setfield(L, -2, "config"); // set as ipe.config lua_pop(L, 1); } #endif static bool populate_module(PyObject *m, lua_State *L) { lua_getglobal(L, "ipe"); lua_pushnil(L); while (lua_next(L, -2) != 0) { PyObject *obj = LuaConvert(L, -1); lua_pop(L, 1); if (obj == nullptr || lua_type(L, -1) != LUA_TSTRING) return false; Py_INCREF(obj); if (PyModule_AddObject(m, lua_tostring(L, -1), obj) < 0) { Py_DECREF(m); Py_DECREF(obj); return false; } } return true; } // -------------------------------------------------------------------- PyMODINIT_FUNC PyInit_ipe(void) { PyObject *m; if (PyType_Ready(&LuaObject_Type) < 0 || (m = PyModule_Create(&ipelua_module)) == nullptr) return nullptr; if (!LuaState) { LuaState = luaL_newstate(); luaL_openlibs(LuaState); luaopen_ipe(LuaState); #if IPELUA_CONFIG setup_config(LuaState); #endif bool ok = populate_module(m, LuaState); lua_settop(LuaState, 0); if (!ok) return nullptr; } return m; } // -------------------------------------------------------------------- ipe-tools-7.2.24.1/ipepython/readme.md000066400000000000000000000034411420423641200174560ustar00rootroot00000000000000# Ipe Python module This is an extension module that will let you read and write Ipe documents from Python 3. It makes the Ipe Lua-bindings available using a Python-Lua bridge based on code written by [Gustavo Niemeyer](http://labix.org/lunatic-python). ## Installation You'll need to fill in the arguments to find the Lua and Ipe header files and library in `setup.py`. Then say ``` python3 setup.py build python3 setup.py install --user ``` ## Documentation After loading the module: ``` import ipe ``` you can use the functions documented in the [Lua bindings](http://ipe.otfried.org/manual/lua.html). For instance, load an Ipe document by saying: ``` doc = ipe.Document("filename.pdf") ``` Have a look at `test.py` for an example. **Tables:** Several methods in the module return *Lua tables*. These are similar to Python dictionaries, but are a distinct type. For instance, ``` props = doc.properties() ``` will set `props` to be a table with keys such as `author`, `title`, `created`, etc. You can access the elements of a table using either attribute syntax (`props.title`) or dictionary syntax (`props['title']`). You can also can iterate over the elements of a table in a `for` loop: ``` for k in props: print(k, props[k]) ``` **Sequences:** A document is a sequence of pages, a page is a sequence of graphical objects. In Lua, indices **start with one**, not zero! You can use Python's `len` function to determine the length of a sequence. For instance, loop over the pages of a document like this: ``` for pageNo in range(1, len(doc) + 1): print("Page %d has %d objects" % (pageNo, len(doc[pageNo]))) ``` **Iterators:** The Lua documentation shows loops using `page:objects()` and `document:pages()`. This does not work from Python. You should use a loop over the indices instead. ipe-tools-7.2.24.1/ipepython/setup.py000066400000000000000000000015371420423641200174150ustar00rootroot00000000000000from distutils.core import setup, Extension # compile options to find Lua and Ipe headers and libraries lua_includes = [ '-I/usr/include/lua5.3' ] lua_libs = [ '-llua5.3' ] ipe_srcdir = '../../ipe/src' ipe_libdir = '../../ipe/build/lib' ipe_includes = [ '-I%s/include' % ipe_srcdir ] ipe_libs = [ '-L%s' % ipe_libdir, '-lipelua', '-lipe' ] ipemodule = Extension('ipe', sources = ['ipepython.cpp'], extra_compile_args = lua_includes + ipe_includes, extra_link_args = lua_libs + ipe_libs, ) setup(name = 'ipe', version = '2019.09.10', description = 'Use Ipelib from Python', url = 'https://github.com/otfried/ipe-tools/tree/python3/ipepython', author = 'Otfried Cheong', author_email = 'ipe@otfried.org', license = 'GPL3', ext_modules = [ipemodule], ) ipe-tools-7.2.24.1/ipepython/test.py000066400000000000000000000022051420423641200172250ustar00rootroot00000000000000# # Access Ipe document from Python # import sys, os import math assert sys.hexversion >= 0x3060000 import ipe print("Ipe configuration: ", ipe.config.latexdir, ipe.config.version) print("Testing ipe.Vector and ipe.Matrix:") v1 = ipe.Vector() v2 = ipe.Vector(3, 2) v3 = ipe.Direction(math.pi/4.0) print(v1, v2, v3) print(v1 + v2, v2 + v3, v3 - v2) print(v2.len(), v3.len(), v2.angle(), v3.angle()) print(v2 == v1 + v2, v2 == v2 + v3) print("Dot products: ", v1 ^ v2, v2 ^ v3) print(3 * v2, v3 * 7) print(-v2, -v3) m1 = ipe.Matrix(1.0, 0, 0, 2.0, 5, 8) m2 = ipe.Matrix(1.0, 2, 3, -1.0, 0, 0) print(m1, m2) print(m1 * m2) print(m1 * v2, m1 * v3, m2 * v3) if len(sys.argv) == 2: ipename = sys.argv[1] else: ipename = "../poweripe/demo-presentation.ipe" doc = ipe.Document(ipename) print("Document has ", len(doc), "pages and ", doc.countTotalViews(), "views") props = doc.properties() print("Some document properties: ", props.title, props.created, props.creator) print("All document properties:") for k in props: print(" -", k, props[k]) for pageNo in range(1, len(doc) + 1): print("Page %d has %d objects" % (pageNo, len(doc[pageNo]))) ipe-tools-7.2.24.1/matplotlib/000077500000000000000000000000001420423641200160255ustar00rootroot00000000000000ipe-tools-7.2.24.1/matplotlib/.gitignore000066400000000000000000000000401420423641200200070ustar00rootroot00000000000000__pycahce__/ out/ tmp.py *.pyc ipe-tools-7.2.24.1/matplotlib/README.md000066400000000000000000000045751420423641200173170ustar00rootroot00000000000000matplotlib backend ================== This is an Ipe backend for the [Matplotlib plotting library](http://matplotlib.org/) for Python, written by Soyeon Baek and Otfried Cheong. You can create Ipe files directly from Matplotlib. To use the backend, copy the file *backend_ipe.py* somewhere on your Python path. (The current directory will do.) You activate the backend like this: ```python import matplotlib matplotlib.use('module://backend_ipe') ``` The Ipe backend allows you to save in Ipe format: ```python plt.savefig("my_plot.ipe", format="ipe") ``` Options ------- Some plots need to measure the size of text to place labels correctly (see the *legend_demo* test for an example). The Ipe backend can use a background Latex process to measure the dimensions of text as it will appear in the Ipe document. By default this is not enabled, as most plots don't need it and it slows down the processing of the plot. If you want to enable text size measuring, set the matplotlib option *ipe.textsize* to True, for instance like this: ```python import matplotlib matplotlib.use('module://backend_ipe') import matplotlib.pyplot as plt matplotlib.rcParams['ipe.textsize'] = True ``` (Note that the *ipe* options are only available after the backend has been loaded, here caused by importing *pyplot*.) If you want your plot to include an Ipe stylesheet, specify this using the option *ipe.stylesheet*, with a full pathname. (If you don't know where your style sheets are, use Ipe -> Help -> Show Configuration.) Here is an example: ```python import matplotlib matplotlib.use('module://backend_ipe') import matplotlib.pyplot as plt matplotlib.rcParams['ipe.stylesheet'] = "/sw/ipe/share/ipe/7.1.6/styles/basic.isy" ``` You can set the preamble of the Ipe document using the option *ipe.preamble*. This is useful, for instance, when you want to use font sizes that are not available with the standard fonts (the test *watermark_image* needs this). You can then switch to a Postscript font that can be scaled to any size: ```python import matplotlib matplotlib.use('module://backend_ipe') import matplotlib.pyplot as plt matplotlib.rcParams['ipe.preamble'] = r""" \usepackage{times} """ ``` Problems? --------- If you need to report a problem, please include your matplotlib version. You can find it as follows: ```python import matplotlib print matplotlib.__version__ ``` ipe-tools-7.2.24.1/matplotlib/backend_ipe.py000066400000000000000000000430051420423641200206250ustar00rootroot00000000000000""" This is a matplotlib backend to save in the Ipe file format. (ipe7.sourceforge.net). (c) 2014 Soyeon Baek, Otfried Cheong You can find the most current version at: http://www.github.com/otfried/ipe-tools/matplotlib You can use this backend by saving it anywhere on your PYTHONPATH. Use it as an external backend from matplotlib like this: import matplotlib matplotlib.use('module://backend_ipe') """ # -------------------------------------------------------------------- from __future__ import division, print_function, unicode_literals import os, base64, tempfile, urllib, gzip, io, sys, codecs, re from six import string_types import matplotlib from matplotlib import rcParams from matplotlib._pylab_helpers import Gcf from matplotlib.backend_bases import RendererBase, GraphicsContextBase from matplotlib.backend_bases import FigureManagerBase, FigureCanvasBase from matplotlib.figure import Figure from matplotlib.transforms import Bbox from matplotlib.cbook import is_writable_file_like, maxdict from matplotlib.path import Path from xml.sax.saxutils import escape as escape_xml_text from math import sin, cos, radians from matplotlib.backends.backend_pgf import LatexManagerFactory, \ LatexManager, common_texification import atexit from matplotlib.rcsetup import validate_bool, validate_path_exists negative_number = re.compile(u"^\u2212([0-9]+)(\.[0-9]*)?$") rcParams.validate['ipe.textsize'] = validate_bool rcParams.validate['ipe.stylesheet'] = validate_path_exists rcParams.validate['ipe.preamble'] = lambda s : s # ---------------------------------------------------------------------- # SimpleXMLWriter class # # Based on an original by Fredrik Lundh, but modified here to: # 1. Support modern Python idioms # 2. Remove encoding support (it's handled by the file writer instead) # 3. Support proper indentation # 4. Minify things a little bit # -------------------------------------------------------------------- # The SimpleXMLWriter module is # # Copyright (c) 2001-2004 by Fredrik Lundh # # By obtaining, using, and/or copying this software and/or its # associated documentation, you agree that you have read, understood, # and will comply with the following terms and conditions: # # Permission to use, copy, modify, and distribute this software and # its associated documentation for any purpose and without fee is # hereby granted, provided that the above copyright notice appears in # all copies, and that both that copyright notice and this permission # notice appear in supporting documentation, and that the name of # Secret Labs AB or the author not be used in advertising or publicity # pertaining to distribution of the software without specific, written # prior permission. # # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE # OF THIS SOFTWARE. # -------------------------------------------------------------------- def escape_cdata(s): s = s.replace(u"&", u"&") s = s.replace(u"<", u"<") s = s.replace(u">", u">") return s def escape_attrib(s): s = s.replace(u"&", u"&") s = s.replace(u"'", u"'") s = s.replace(u"\"", u""") s = s.replace(u"<", u"<") s = s.replace(u">", u">") return s ## # XML writer class. # # @param file A file or file-like object. This object must implement # a write method that takes an 8-bit string. class XMLWriter: def __init__(self, file): self.__write = file.write if hasattr(file, "flush"): self.flush = file.flush self.__open = 0 # true if start tag is open self.__tags = [] self.__data = [] self.__indentation = u" " * 64 def __flush(self, indent=True): # flush internal buffers if self.__open: if indent: self.__write(u">\n") else: self.__write(u">") self.__open = 0 if self.__data: data = u''.join(self.__data) self.__write(escape_cdata(data)) self.__data = [] ## Opens a new element. Attributes can be given as keyword # arguments, or as a string/string dictionary. The method returns # an opaque identifier that can be passed to the close # method, to close all open elements up to and including this one. # # @param tag Element tag. # @param attrib Attribute dictionary. Alternatively, attributes # can be given as keyword arguments. # @return An element identifier. def start(self, tag, attrib={}, **extra): self.__flush() tag = escape_cdata(tag) self.__data = [] self.__tags.append(tag) self.__write(self.__indentation[:len(self.__tags) - 1]) self.__write(u"<%s" % tag) if attrib or extra: attrib = attrib.copy() attrib.update(extra) attrib = sorted(attrib.items()) for k, v in attrib: if not v == '': k = escape_cdata(k) v = escape_attrib(v) self.__write(u" %s=\"%s\"" % (k, v)) self.__open = 1 return len(self.__tags)-1 ## # Adds a comment to the output stream. # # @param comment Comment text, as a Unicode string. def comment(self, comment): self.__flush() self.__write(self.__indentation[:len(self.__tags)]) self.__write(u"\n" % escape_cdata(comment)) ## # Adds character data to the output stream. # # @param text Character data, as a Unicode string. def data(self, text): self.__data.append(text) ## # Closes the current element (opened by the most recent call to # start). # # @param tag Element tag. If given, the tag must match the start # tag. If omitted, the current element is closed. def end(self, tag=None, indent=True): if tag: assert self.__tags, "unbalanced end(%s)" % tag assert escape_cdata(tag) == self.__tags[-1],\ "expected end(%s), got %s" % (self.__tags[-1], tag) else: assert self.__tags, "unbalanced end()" tag = self.__tags.pop() if self.__data: self.__flush(indent) elif self.__open: self.__open = 0 self.__write(u"/>\n") return if indent: self.__write(self.__indentation[:len(self.__tags)]) self.__write(u"\n" % tag) ## # Closes open elements, up to (and including) the element identified # by the given identifier. # # @param id Element identifier, as returned by the start method. def close(self, id): while len(self.__tags) > id: self.end() ## # Adds an entire element. This is the same as calling start, # data, and end in sequence. The text argument # can be omitted. def element(self, tag, text=None, attrib={}, **extra): self.start(*(tag, attrib), **extra) if text: self.data(text) self.end(indent=False) def insertSheet(self, fname): self.__flush() data = open(fname, "rb").read() i = data.find("= 0: self.__write(data[i:].decode("utf-8")) # ---------------------------------------------------------------------- class RendererIpe(RendererBase): """ The renderer handles drawing/rendering operations. Refer to backend_bases.RendererBase for documentation of the classes methods. """ def __init__(self, width, height, ipewriter, basename): self.width = width self.height = height self.writer = XMLWriter(ipewriter) self.basename = basename RendererBase.__init__(self) # use same latex as Ipe (default is xelatex) rcParams['pgf.texsystem'] = "pdflatex" self.latexManager = None if rcParams.get("ipe.textsize", False): self.latexManager = LatexManagerFactory.get_latex_manager() self._start_id = self.writer.start( u'ipe', version=u"70005", creator="matplotlib") pre = rcParams.get('ipe.preamble', "") if pre != "": self.writer.start(u'preamble') self.writer.data(pre) self.writer.end(indent=False) sheet = rcParams.get('ipe.stylesheet', "") if sheet != "": self.writer.insertSheet(sheet) self.writer.start(u'ipestyle', name=u"opacity") for i in range(10,100,10): self.writer.element(u'opacity', name=u'%02d%%'% i, value=u'%g'% (i/100.0)) self.writer.end() self.writer.start(u'page') def finalize(self): self.writer.close(self._start_id) self.writer.flush() def draw_path(self, gc, path, transform, rgbFace=None): capnames = ('butt', 'round', 'projecting') cap = capnames.index(gc.get_capstyle()) joinnames = ('miter', 'round', 'bevel') join = joinnames.index(gc.get_joinstyle()) # filling has_fill = rgbFace is not None offs, dl = gc.get_dashes() attrib = {} if offs != None and dl != None: if type(dl) == float: dashes = "[%g] %g" % (dl, offs) else: dashes = "[" + " ".join(["%g" % x for x in dl]) + "] %g" % offs attrib['dash'] = dashes if has_fill: attrib['fill'] = "%g %g %g" % tuple(rgbFace)[:3] opaq = gc.get_rgb()[3] if rgbFace is not None and len(rgbFace) > 3: opaq = rgbFace[3] self.gen_opacity(attrib, opaq) self._print_ipe_clip(gc) self.writer.start( u'path', attrib=attrib, stroke="%g %g %g" % gc.get_rgb()[:3], pen="%g" % gc.get_linewidth(), cap="%d" % cap, join="%d" % join, fillrule="wind" ) self.writer.data(self._make_ipe_path(gc, path, transform)) self.writer.end() self._print_ipe_clip_end() def draw_image(self, gc, x, y, im, dx=None, dy=None, transform=None): h,w = im.get_size_out() if dx is not None: w = dx if dy is not None: h = dy rows, cols, buffer = im.as_rgba_str() self._print_ipe_clip(gc) self.writer.start( u'image', width=u"%d" % cols, height=u"%d" % rows, ColorSpace=u"DeviceRGB", BitsPerComponent=u"8", matrix=u"1 0 0 -1 %g %g" % (x, y), rect="%g %g %g %g" % (0, -h, w, 0) ) for i in xrange(rows * cols): rgb = buffer[4*i:4*i+3] self.writer.data(u"%02x%02x%02x" % (ord(rgb[0]), ord(rgb[1]), ord(rgb[2]))) self.writer.end() self._print_ipe_clip_end() def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None): if negative_number.match(s): s = u"$" + s.replace(u'\u2212', u'-') + "$" attrib = {} if mtext: # if text anchoring can be supported, get the original coordinates # and add alignment information x, y = mtext.get_transform().transform_point(mtext.get_position()) attrib['halign'] = mtext.get_ha() attrib['valign'] = mtext.get_va() if angle != 0.0: ra = radians(angle) sa = sin(ra); ca = cos(ra) attrib['matrix'] = "%g %g %g %g %g %g" % (ca, sa, -sa, ca, x, y) x, y = 0, 0 self.gen_opacity(attrib, gc.get_rgb()[3]) self.writer.start( u'text', stroke="%g %g %g" % gc.get_rgb()[:3], type="label", size="%g" % prop.get_size_in_points(), pos="%g %g" % (x,y), attrib=attrib ) s = common_texification(s) self.writer.data(u"%s" % s) self.writer.end(indent=False) def _make_ipe_path(self, gc, path, transform): elem = "" for points, code in path.iter_segments(transform): if code == Path.MOVETO: x, y = tuple(points) elem += "%g %g m\n" % (x, y) elif code == Path.CLOSEPOLY: elem += "h\n" elif code == Path.LINETO: x, y = tuple(points) elem += "%g %g l\n" % (x, y) elif code == Path.CURVE3: cx, cy, px, py = tuple(points) elem += "%g %g %g %g q\n" % (cx, cy, px, py) elif code == Path.CURVE4: c1x, c1y, c2x, c2y, px, py = tuple(points) elem += ("%g %g %g %g %g %g c\n" % (c1x, c1y, c2x, c2y, px, py)) return elem def _print_ipe_clip(self, gc): bbox = gc.get_clip_rectangle() self.use_clip_box = bbox is not None if self.use_clip_box: p1, p2 = bbox.get_points() x1, y1 = p1 x2, y2 = p2 self.writer.start( u'group', clip=u"%g %g m %g %g l %g %g l %g %g l h" % (x1, y1, x2, y1, x2, y2, x1, y2) ) # check for clip path clippath, clippath_trans = gc.get_clip_path() self.use_clip_group = clippath is not None if self.use_clip_group: self.writer.start( u'group', clip=u"%s" % self._make_ipe_path(gc, clippath, clippath_trans) ) def _print_ipe_clip_end(self): if self.use_clip_group: self.writer.end() if self.use_clip_box: self.writer.end() def flipy(self): return True def get_canvas_width_height(self): return self.width, self.height def get_text_width_height_descent(self, s, prop, ismath): if self.latexManager: s = common_texification(s) w, h, d = self.latexManager.get_width_height_descent(s, prop) return w, h, d else: return 1, 1, 1 def new_gc(self): return GraphicsContextIpe() def points_to_pixels(self, points): return points def gen_opacity(self, attrib, opaq): if opaq > 0.99: return opaq += 0.05 o = int(opaq * 10) * 10 if o > 90: o = 90 if o < 10: o = 10 attrib["opacity"] = u"%02d%%" % o # -------------------------------------------------------------------- class GraphicsContextIpe(GraphicsContextBase): pass # -------------------------------------------------------------------- class FigureCanvasIpe(FigureCanvasBase): filetypes = FigureCanvasBase.filetypes.copy() filetypes['ipe'] = 'Ipe 7 file format' def print_ipe(self, filename, *args, **kwargs): if isinstance(filename, string_types): fh_to_close = ipewriter = io.open(filename, 'w', encoding='utf-8') elif is_writable_file_like(filename): if not isinstance(filename, io.TextIOBase): if sys.version_info[0] >= 3: ipewriter = io.TextIOWrapper(filename, 'utf-8') else: ipewriter = codecs.getwriter('utf-8')(filename) else: ipewriter = filename fh_to_close = None else: raise ValueError("filename must be a path or a file-like object") return self._print_ipe(filename, ipewriter, fh_to_close, **kwargs) def _print_ipe(self, filename, ipewriter, fh_to_close=None, **kwargs): try: self.figure.set_dpi(72.0) width, height = self.figure.get_size_inches() w, h = width*72, height*72 renderer = RendererIpe(w, h, ipewriter, filename) self.figure.draw(renderer) renderer.finalize() finally: if fh_to_close is not None: ipewriter.close() def get_default_filetype(self): return 'ipe' # -------------------------------------------------------------------- # Provide the standard names that backend.__init__ is expecting class FigureManagerIpe(FigureManagerBase): def __init__(self, *args): FigureManagerBase.__init__(self, *args) FigureCanvas = FigureCanvasIpe FigureManager = FigureManagerIpe def new_figure_manager(num, *args, **kwargs): FigureClass = kwargs.pop('FigureClass', Figure) thisFig = FigureClass(*args, **kwargs) return new_figure_manager_given_figure(num, thisFig) def new_figure_manager_given_figure(num, figure): """ Create a new figure manager instance for the given figure. """ canvas = FigureCanvasIpe(figure) manager = FigureManagerIpe(canvas, num) return manager # -------------------------------------------------------------------- def _cleanup(): LatexManager._cleanup_remaining_instances() # This is necessary to avoid a spurious error # caused by the atexit at the end of the PGF backend LatexManager.__del__ = lambda self : None atexit.register(_cleanup) # -------------------------------------------------------------------- ipe-tools-7.2.24.1/matplotlib/run_test.py000066400000000000000000000027261420423641200202510ustar00rootroot00000000000000# # Run all the tests in tests subdirectory # and save plots in ipe and svg format # from __future__ import division, print_function, unicode_literals import os, sys def fix_file(f): data = open("tests/%s" % f, "r").readlines() os.rename("tests/%s" % f, "tests/%s.bak" % f) out = open("tests/%s" % f, "w") for l in data: ll = l.strip() if ll == "import matplotlib as mpl": continue if ll == "print mpl.__file__": continue if ll == "mpl.use('module://backend_ipe')": continue if ll == "#mpl.use('module://backend_ipe')": continue if ll == "#plt.show()" or ll == "plt.show()": continue if ll[:12] == "#plt.savefig": continue if ll[:11] == "plt.savefig": continue out.write(l) out.close() def runall(form): tests = [f[:-3] for f in os.listdir("tests") if f[-3:] == ".py"] for f in tests: # doesn't work on my matplotlib version if f == "power_norm_demo": continue run(form, f) def run(form, f): sys.stderr.write("# %s\n" % f) t = open("tmp.py", "w") t.write("""# %s import matplotlib as mpl """ % f) if form=="ipe": t.write("mpl.use('module://backend_ipe')\n") t.write(open("tests/%s.py" % f, "r").read()) t.write("plt.savefig('out/%s.%s', format='%s')\n" % (f, form, form)) t.close() os.system("python tmp.py") if len(sys.argv) != 3: sys.stderr.write("Usage: run_test.py \n") sys.exit(9) form = sys.argv[1] test = sys.argv[2] if test == 'all': runall(form) else: run(form, test) ipe-tools-7.2.24.1/matplotlib/tests/000077500000000000000000000000001420423641200171675ustar00rootroot00000000000000ipe-tools-7.2.24.1/matplotlib/tests/barchart_demo.py000066400000000000000000000017761420423641200223460ustar00rootroot00000000000000# a bar plot with errorbars import numpy as np import matplotlib.pyplot as plt N = 5 menMeans = (20, 35, 30, 35, 27) menStd = (2, 3, 4, 1, 2) ind = np.arange(N) # the x locations for the groups width = 0.35 # the width of the bars fig, ax = plt.subplots() rects1 = ax.bar(ind, menMeans, width, color='r', yerr=menStd) womenMeans = (25, 32, 34, 20, 25) womenStd = (3, 5, 2, 3, 3) rects2 = ax.bar(ind+width, womenMeans, width, color='y', yerr=womenStd) # add some text for labels, title and axes ticks ax.set_ylabel('Scores') ax.set_title('Scores by group and gender') ax.set_xticks(ind+width) ax.set_xticklabels( ('G1', 'G2', 'G3', 'G4', 'G5') ) ax.legend( (rects1[0], rects2[0]), ('Men', 'Women') ) def autolabel(rects): # attach some text labels for rect in rects: height = rect.get_height() ax.text(rect.get_x()+rect.get_width()/2., 1.05*height, '%d'%int(height), ha='center', va='bottom') autolabel(rects1) autolabel(rects2) ipe-tools-7.2.24.1/matplotlib/tests/barh_demo.py000066400000000000000000000010141420423641200214550ustar00rootroot00000000000000 """ Simple demo of a horizontal bar chart. """ import matplotlib.pyplot as plt; plt.rcdefaults() import numpy as np import matplotlib.pyplot as plt # Example data people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim') y_pos = np.arange(len(people)) performance = 3 + 10 * np.random.rand(len(people)) error = np.random.rand(len(people)) plt.barh(y_pos, performance, xerr=error, align='center', alpha=0.4) plt.yticks(y_pos, people) plt.xlabel('Performance') plt.title('How fast do you want to go today?') ipe-tools-7.2.24.1/matplotlib/tests/clip_test.py000066400000000000000000000013331420423641200215270ustar00rootroot00000000000000import numpy as np import matplotlib.cm as cm import matplotlib.mlab as mlab import matplotlib.pyplot as plt from matplotlib.path import Path from matplotlib.patches import PathPatch delta = 0.025 x = y = np.arange(-3.0, 3.0, delta) X, Y = np.meshgrid(x, y) Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) Z = Z2-Z1 # difference of Gaussians path = Path([[0, 1], [1, 0], [0, -1], [-1, 0], [0, 1]]) patch = PathPatch(path, facecolor='none') plt.gca().add_patch(patch) im = plt.imshow(Z, interpolation='bilinear', cmap=cm.gray, origin='lower', extent=[-3,3,-3,3], clip_path=patch, clip_on=True) im.set_clip_path(patch) ipe-tools-7.2.24.1/matplotlib/tests/collections_demo.py000066400000000000000000000067601420423641200230740ustar00rootroot00000000000000 import matplotlib.pyplot as plt from matplotlib import collections, transforms from matplotlib.colors import colorConverter import numpy as np nverts = 50 npts = 100 # Make some spirals r = np.array(range(nverts)) theta = np.array(range(nverts)) * (2*np.pi)/(nverts-1) xx = r * np.sin(theta) yy = r * np.cos(theta) spiral = list(zip(xx,yy)) # Make some offsets rs = np.random.RandomState([12345678]) xo = rs.randn(npts) yo = rs.randn(npts) xyo = list(zip(xo, yo)) # Make a list of colors cycling through the rgbcmyk series. colors = [colorConverter.to_rgba(c) for c in ('r','g','b','c','y','m','k')] fig, axes = plt.subplots(2,2) ((ax1, ax2), (ax3, ax4)) = axes # unpack the axes col = collections.LineCollection([spiral], offsets=xyo, transOffset=ax1.transData) trans = fig.dpi_scale_trans + transforms.Affine2D().scale(1.0/72.0) col.set_transform(trans) # the points to pixels transform # Note: the first argument to the collection initializer # must be a list of sequences of x,y tuples; we have only # one sequence, but we still have to put it in a list. ax1.add_collection(col, autolim=True) # autolim=True enables autoscaling. For collections with # offsets like this, it is neither efficient nor accurate, # but it is good enough to generate a plot that you can use # as a starting point. If you know beforehand the range of # x and y that you want to show, it is better to set them # explicitly, leave out the autolim kwarg (or set it to False), # and omit the 'ax1.autoscale_view()' call below. # Make a transform for the line segments such that their size is # given in points: col.set_color(colors) ax1.autoscale_view() # See comment above, after ax1.add_collection. ax1.set_title('LineCollection using offsets') # The same data as above, but fill the curves. col = collections.PolyCollection([spiral], offsets=xyo, transOffset=ax2.transData) trans = transforms.Affine2D().scale(fig.dpi/72.0) col.set_transform(trans) # the points to pixels transform ax2.add_collection(col, autolim=True) col.set_color(colors) ax2.autoscale_view() ax2.set_title('PolyCollection using offsets') # 7-sided regular polygons col = collections.RegularPolyCollection(7, sizes = np.fabs(xx)*10.0, offsets=xyo, transOffset=ax3.transData) trans = transforms.Affine2D().scale(fig.dpi/72.0) col.set_transform(trans) # the points to pixels transform ax3.add_collection(col, autolim=True) col.set_color(colors) ax3.autoscale_view() ax3.set_title('RegularPolyCollection using offsets') # Simulate a series of ocean current profiles, successively # offset by 0.1 m/s so that they form what is sometimes called # a "waterfall" plot or a "stagger" plot. nverts = 60 ncurves = 20 offs = (0.1, 0.0) yy = np.linspace(0, 2*np.pi, nverts) ym = np.amax(yy) xx = (0.2 + (ym-yy)/ym)**2 * np.cos(yy-0.4) * 0.5 segs = [] for i in range(ncurves): xxx = xx + 0.02*rs.randn(nverts) curve = list(zip(xxx, yy*100)) segs.append(curve) col = collections.LineCollection(segs, offsets=offs) ax4.add_collection(col, autolim=True) col.set_color(colors) ax4.autoscale_view() ax4.set_title('Successive data offsets') ax4.set_xlabel('Zonal velocity component (m/s)') ax4.set_ylabel('Depth (m)') # Reverse the y-axis so depth increases downward ax4.set_ylim(ax4.get_ylim()[::-1]) ipe-tools-7.2.24.1/matplotlib/tests/color_cycle_demo.py000066400000000000000000000017511420423641200230460ustar00rootroot00000000000000""" Demo of custom color-cycle settings to control colors for multi-line plots. This example demonstrates two different APIs: 1. Setting the default rc-parameter specifying the color cycle. This affects all subsequent plots. 2. Setting the color cycle for a specific axes. This only affects a single axes. """ import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 2 * np.pi) offsets = np.linspace(0, 2*np.pi, 4, endpoint=False) # Create array with shifted-sine curve along each column yy = np.transpose([np.sin(x + phi) for phi in offsets]) plt.rc('lines', linewidth=4) fig, (ax0, ax1) = plt.subplots(nrows=2) plt.rc('axes', color_cycle=['r', 'g', 'b', 'y']) ax0.plot(yy) ax0.set_title('Set default color cycle to rgby') ax1.set_color_cycle(['c', 'm', 'y', 'k']) ax1.plot(yy) ax1.set_title('Set axes color cycle to cmyk') # Tweak spacing between subplots to prevent labels from overlapping plt.subplots_adjust(hspace=0.3) ipe-tools-7.2.24.1/matplotlib/tests/colormaps_reference.py000066400000000000000000000042141420423641200235570ustar00rootroot00000000000000 import numpy as np import matplotlib.pyplot as plt cmaps = [('Sequential', ['Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']), ('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool', 'copper', 'gist_heat', 'gray', 'hot', 'pink', 'spring', 'summer', 'winter']), ('Diverging', ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'seismic']), ('Qualitative', ['Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3']), ('Miscellaneous', ['gist_earth', 'terrain', 'ocean', 'gist_stern', 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar', # 'nipy_spectral', 'jet', 'rainbow', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism'])] nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps) gradient = np.linspace(0, 1, 256) gradient = np.vstack((gradient, gradient)) def plot_color_gradients(cmap_category, cmap_list): fig, axes = plt.subplots(nrows=nrows) fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99) axes[0].set_title(cmap_category + ' colormaps', fontsize=14) for ax, name in zip(axes, cmap_list): ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name)) pos = list(ax.get_position().bounds) x_text = pos[0] - 0.01 y_text = pos[1] + pos[3]/2. fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10) # Turn off *all* ticks & spines, not just the ones with colormaps. for ax in axes: ax.set_axis_off() for cmap_category, cmap_list in cmaps: plot_color_gradients(cmap_category, cmap_list) ipe-tools-7.2.24.1/matplotlib/tests/date_demo.py000066400000000000000000000022731420423641200214660ustar00rootroot00000000000000 import datetime import numpy as np import matplotlib.pyplot as plt import matplotlib.dates as mdates import matplotlib.cbook as cbook years = mdates.YearLocator() # every year months = mdates.MonthLocator() # every month yearsFmt = mdates.DateFormatter('%Y') # load a numpy record array from yahoo csv data with fields date, # open, close, volume, adj_close from the mpl-data/example directory. # The record array stores python datetime.date as an object array in # the date column datafile = cbook.get_sample_data('goog.npy') r = np.load(datafile).view(np.recarray) fig, ax = plt.subplots() ax.plot(r.date, r.adj_close) # format the ticks ax.xaxis.set_major_locator(years) ax.xaxis.set_major_formatter(yearsFmt) ax.xaxis.set_minor_locator(months) datemin = datetime.date(r.date.min().year, 1, 1) datemax = datetime.date(r.date.max().year+1, 1, 1) ax.set_xlim(datemin, datemax) # format the coords message box def price(x): return '$%1.2f'%x ax.format_xdata = mdates.DateFormatter('%Y-%m-%d') ax.format_ydata = price ax.grid(True) # rotates and right aligns the x labels, and moves the bottom of the # axes up to make room for them fig.autofmt_xdate() ipe-tools-7.2.24.1/matplotlib/tests/donut_demo.py000066400000000000000000000030271420423641200217000ustar00rootroot00000000000000 import numpy as np import matplotlib.path as mpath import matplotlib.patches as mpatches import matplotlib.pyplot as plt def wise(v): if v == 1: return "CCW" else: return "CW" def make_circle(r): t = np.arange(0, np.pi * 2.0, 0.01) t = t.reshape((len(t), 1)) x = r * np.cos(t) y = r * np.sin(t) return np.hstack((x, y)) Path = mpath.Path fig, ax = plt.subplots() inside_vertices = make_circle(0.5) outside_vertices = make_circle(1.0) codes = np.ones(len(inside_vertices), dtype=mpath.Path.code_type) * mpath.Path.LINETO codes[0] = mpath.Path.MOVETO for i, (inside, outside) in enumerate(((1, 1), (1, -1), (-1, 1), (-1, -1))): # Concatenate the inside and outside subpaths together, changing their # order as needed vertices = np.concatenate((outside_vertices[::outside], inside_vertices[::inside])) # Shift the path vertices[:, 0] += i * 2.5 # The codes will be all "LINETO" commands, except for "MOVETO"s at the # beginning of each subpath all_codes = np.concatenate((codes, codes)) # Create the Path object path = mpath.Path(vertices, all_codes) # Add plot it patch = mpatches.PathPatch(path, facecolor='#885500', edgecolor='black') ax.add_patch(patch) ax.annotate("Outside %s,\nInside %s" % (wise(outside), wise(inside)), (i * 2.5, -1.5), va="top", ha="center") ax.set_xlim(-2,10) ax.set_ylim(-3,2) ax.set_title('Mmm, donuts!') ax.set_aspect(1.0) ipe-tools-7.2.24.1/matplotlib/tests/fill_demo.py000066400000000000000000000006631420423641200215000ustar00rootroot00000000000000 """ Demo of the fill function with a few features. In addition to the basic fill plot, this demo shows a few optional features: * Multiple curves with a single command. * Setting the fill color. * Setting the opacity (alpha value). """ import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 2 * np.pi, 100) y1 = np.sin(x) y2 = np.sin(3 * x) plt.fill(x, y1, 'b', x, y2, 'r', alpha=0.3) ipe-tools-7.2.24.1/matplotlib/tests/histogram_path_demo.py000066400000000000000000000016151420423641200235610ustar00rootroot00000000000000 import numpy as np import matplotlib.pyplot as plt import matplotlib.patches as patches import matplotlib.path as path fig, ax = plt.subplots() # histogram our data with numpy data = np.random.randn(1000) n, bins = np.histogram(data, 50) # get the corners of the rectangles for the histogram left = np.array(bins[:-1]) right = np.array(bins[1:]) bottom = np.zeros(len(left)) top = bottom + n # we need a (numrects x numsides x 2) numpy array for the path helper # function to build a compound path XY = np.array([[left,left,right,right], [bottom,top,top,bottom]]).T # get the Path object barpath = path.Path.make_compound_path_from_polys(XY) # make a patch out of it patch = patches.PathPatch(barpath, facecolor='blue', edgecolor='gray', alpha=0.8) ax.add_patch(patch) # update the view limits ax.set_xlim(left[0], right[-1]) ax.set_ylim(bottom.min(), top.max()) ipe-tools-7.2.24.1/matplotlib/tests/image_demo.py000066400000000000000000000004141420423641200216260ustar00rootroot00000000000000""" Simple demo of the imshow function. """ import matplotlib.pyplot as plt import matplotlib.cbook as cbook datafile = cbook.get_sample_data('ada.png', asfileobj=False) image = plt.imread(datafile) plt.imshow(image) plt.axis('off') # clear x- and y-axes ipe-tools-7.2.24.1/matplotlib/tests/image_demo_clip_path.py000066400000000000000000000006671420423641200236630ustar00rootroot00000000000000""" Demo of image that's been clipped by a circular patch. """ import matplotlib.pyplot as plt import matplotlib.patches as patches import matplotlib.cbook as cbook datafile = cbook.get_sample_data('grace_hopper.jpg', asfileobj=False) image = plt.imread(datafile) fig, ax = plt.subplots() im = ax.imshow(image) patch = patches.Circle((260, 200), radius=200, transform=ax.transData) im.set_clip_path(patch) plt.axis('off') ipe-tools-7.2.24.1/matplotlib/tests/joinstyle.py000066400000000000000000000014311420423641200215600ustar00rootroot00000000000000""" Illustrate the three different join styles """ import numpy as np import matplotlib.pyplot as plt def plot_angle(ax, x, y, angle, style): phi = angle/180*np.pi xx = [x+.5,x,x+.5*np.cos(phi)] yy = [y,y,y+.5*np.sin(phi)] ax.plot(xx, yy, lw=8, color='blue', solid_joinstyle=style) ax.plot(xx[1:], yy[1:], lw=1, color='black') ax.plot(xx[1::-1], yy[1::-1], lw=1, color='black') ax.plot(xx[1:2], yy[1:2], 'o', color='red', markersize=3) ax.text(x,y+.2,'%.0f degrees' % angle) fig, ax = plt.subplots() ax.set_title('Join style') for x,style in enumerate((('miter', 'round', 'bevel'))): ax.text(x, 5, style) for i in range(5): plot_angle(ax, x, i, pow(2.0,3+i), style) ax.set_xlim(-0.5,2.75) ax.set_ylim(-0.5,5.5) ipe-tools-7.2.24.1/matplotlib/tests/legend_demo.py000066400000000000000000000010231420423641200217770ustar00rootroot00000000000000 import numpy as np import matplotlib.pyplot as plt mpl.rcParams['ipe.textsize'] = True # Make some fake data. a = b = np.arange(0,3, .02) c = np.exp(a) d = c[::-1] # Create plots with pre-defined labels. plt.plot(a, c, 'k--', label='Model length') plt.plot(a, d, 'k:', label='Data length') plt.plot(a, c+d, 'k', label='Total message length') legend = plt.legend(loc='upper center', shadow=True, fontsize='x-large') # Put a nicer background color on the legend. legend.get_frame().set_facecolor('#00FFCC') ipe-tools-7.2.24.1/matplotlib/tests/line_demo_dash_control.py000066400000000000000000000003441420423641200242340ustar00rootroot00000000000000 import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 10) line, = plt.plot(x, np.sin(x), '--', linewidth=2) dashes = [10, 5, 100, 5] # 10 points on, 5 off, 100 on, 5 off line.set_dashes(dashes) ipe-tools-7.2.24.1/matplotlib/tests/line_styles_reference.py000066400000000000000000000042131420423641200241110ustar00rootroot00000000000000 import numpy as np import matplotlib.pyplot as plt cmaps = [('Sequential', ['Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']), ('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool', 'copper', 'gist_heat', 'gray', 'hot', 'pink', 'spring', 'summer', 'winter']), ('Diverging', ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral', 'seismic']), ('Qualitative', ['Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2', 'Set1', 'Set2', 'Set3']), ('Miscellaneous', ['gist_earth', 'terrain', 'ocean', 'gist_stern', 'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar', # 'nipy_spectral', 'jet', 'rainbow', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism'])] nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps) gradient = np.linspace(0, 1, 256) gradient = np.vstack((gradient, gradient)) def plot_color_gradients(cmap_category, cmap_list): fig, axes = plt.subplots(nrows=nrows) fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99) axes[0].set_title(cmap_category + ' colormaps', fontsize=14) for ax, name in zip(axes, cmap_list): ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name)) pos = list(ax.get_position().bounds) x_text = pos[0] - 0.01 y_text = pos[1] + pos[3]/2. fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10) # Turn off *all* ticks & spines, not just the ones with colormaps. for ax in axes: ax.set_axis_off() for cmap_category, cmap_list in cmaps: plot_color_gradients(cmap_category, cmap_list) ipe-tools-7.2.24.1/matplotlib/tests/power_norm_demo.py000066400000000000000000000014661420423641200227430ustar00rootroot00000000000000from matplotlib import pyplot as plt import matplotlib.colors as mcolors import numpy as np from numpy.random import multivariate_normal data = np.vstack([multivariate_normal([10, 10], [[3, 5],[4, 2]], size=100000), multivariate_normal([30, 20], [[2, 3],[1, 3]], size=1000) ]) gammas = [0.8, 0.5, 0.3] xgrid = np.floor((len(gammas) + 1.) / 2) ygrid = np.ceil((len(gammas) + 1.) / 2) plt.subplot(xgrid, ygrid, 1) plt.title('Linear normalization') plt.hist2d(data[:,0], data[:,1], bins=100) for i, gamma in enumerate(gammas): plt.subplot(xgrid, ygrid, i + 2) plt.title('Power law normalization\n$(\gamma=%1.1f)$' % gamma) plt.hist2d(data[:, 0], data[:, 1], bins=100, norm=mcolors.PowerNorm(gamma)) plt.subplots_adjust(hspace=0.39) ipe-tools-7.2.24.1/matplotlib/tests/two_scales.py000066400000000000000000000007711420423641200217110ustar00rootroot00000000000000 import numpy as np import matplotlib.pyplot as plt fig, ax1 = plt.subplots() t = np.arange(0.01, 10.0, 0.01) s1 = np.exp(t) ax1.plot(t, s1, 'b-') ax1.set_xlabel('time (s)') # Make the y-axis label and tick labels match the line color. ax1.set_ylabel('exp', color='b') for tl in ax1.get_yticklabels(): tl.set_color('b') ax2 = ax1.twinx() s2 = np.sin(2*np.pi*t) ax2.plot(t, s2, 'r.') ax2.set_ylabel('sin', color='r') for tl in ax2.get_yticklabels(): tl.set_color('r') ipe-tools-7.2.24.1/matplotlib/tests/watermark_image.py000066400000000000000000000006351420423641200227040ustar00rootroot00000000000000""" Use a Text as a watermark """ import numpy as np import matplotlib.pyplot as plt mpl.rcParams['ipe.preamble'] = r"\usepackage{times}" fig, ax = plt.subplots() ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=0.7, mfc='orange') ax.grid() # position bottom right fig.text(0.95, 0.05, 'Property of MPL', fontsize=50, color='gray', ha='right', va='bottom', alpha=0.5) ipe-tools-7.2.24.1/matplotlib/tests/watermark_image2.py000066400000000000000000000006501420423641200227630ustar00rootroot00000000000000 """ Use a PNG file as a watermark """ import numpy as np import matplotlib.cbook as cbook import matplotlib.image as image import matplotlib.pyplot as plt datafile = cbook.get_sample_data('logo2.png', asfileobj=False) im = image.imread(datafile) im[:,:,-1] = 0.5 # set the alpha channel fig, ax = plt.subplots() ax.plot(np.random.rand(20), '-o', ms=20, lw=2, alpha=0.7, mfc='orange') ax.grid() fig.figimage(im, 10, 10) ipe-tools-7.2.24.1/obs/000077500000000000000000000000001420423641200144415ustar00rootroot00000000000000ipe-tools-7.2.24.1/obs/debian/000077500000000000000000000000001420423641200156635ustar00rootroot00000000000000ipe-tools-7.2.24.1/obs/debian/changelog000066400000000000000000000052771420423641200175500ustar00rootroot00000000000000ipe (7.2.24-1) unstable; urgency=medium * New version. -- Otfried Cheong Wed, 07 Apr 2021 11:50:00 +0200 ipe (7.2.23-3) unstable; urgency=medium * Build a single deb file, including shared libraries. -- Otfried Cheong Sat, 26 Dec 2020 19:40:00 +0100 ipe (7.2.23-2) unstable; urgency=medium * Run doxygen as part of the build process. -- Otfried Cheong Wed, 23 Dec 2020 12:30:00 +0100 ipe (7.2.23-1) unstable; urgency=medium * New version. -- Otfried Cheong Mon, 21 Dec 2020 11:40:00 +0100 ipe (7.2.22-1) unstable; urgency=medium * New version. -- Otfried Cheong Sun, 20 Dec 2020 19:17:00 +0100 ipe (7.2.21-1) unstable; urgency=medium * New version. -- Otfried Cheong Mon, 26 Oct 2020 17:00:00 +0100 ipe (7.2.20-1) unstable; urgency=medium * New version. -- Otfried Cheong Thu, 25 Jun 2020 14:00:00 +0200 ipe (7.2.19-1) unstable; urgency=medium * New version. -- Otfried Cheong Sun, 14 Jun 2020 15:00:00 +0200 ipe (7.2.18-1) unstable; urgency=medium * New version. -- Otfried Cheong Wed, 20 May 2020 22:40:00 +0200 ipe (7.2.17-1) unstable; urgency=medium * New version. -- Otfried Cheong Sat, 09 May 2020 14:44:00 +0200 ipe (7.2.16-1) unstable; urgency=medium * New version. -- Otfried Cheong Fri, 01 May 2020 10:00:00 +0200 ipe (7.2.15-1) unstable; urgency=medium * New version. -- Otfried Cheong Sun, 26 Apr 2020 10:02:00 +0200 ipe (7.2.14-1) unstable; urgency=medium * New version. -- Otfried Cheong Sat, 18 Apr 2020 23:02:00 +0200 ipe (7.2.13-1) unstable; urgency=medium * New version. -- Otfried Cheong Mon, 07 Oct 2019 11:56:00 +0200 ipe (7.2.12-1) unstable; urgency=medium * New version. -- Otfried Cheong Mon, 06 May 2019 10:40:00 +0100 ipe (7.2.11-2) unstable; urgency=medium * Do not insist on having tex package (it might be installed differently). -- Otfried Cheong Sun, 17 Mar 2019 20:30:00 +0100 ipe (7.2.11-1) unstable; urgency=medium * New version. -- Otfried Cheong Sat, 09 Mar 2019 13:40:00 +0100 ipe (7.2.10-1) unstable; urgency=medium * New version. -- Otfried Cheong Mon, 04 Feb 2019 14:03:00 +0100 ipe (7.2.9-1) unstable; urgency=medium * New version. -- Otfried Cheong Wed, 16 Jan 2019 14:20:00 +0100 ipe (7.2.8-1) unstable; urgency=medium * First build on opensuse build service -- Otfried Cheong Thu, 10 Jan 2019 16:25:00 +0100 ipe-tools-7.2.24.1/obs/debian/compat000066400000000000000000000000031420423641200170620ustar00rootroot0000000000000010 ipe-tools-7.2.24.1/obs/debian/control000066400000000000000000000030271420423641200172700ustar00rootroot00000000000000Source: ipe Maintainer: Otfried Cheong Section: graphics Priority: optional Build-Depends: sharutils, doxygen, debhelper (>= 9~), zlib1g-dev, qtbase5-dev, qtbase5-dev-tools, libfreetype6-dev, libcairo2-dev, libfontconfig1-dev, libjpeg-dev, libpng-dev, liblua5.3-dev, libgsl-dev, libspiro-dev, libcurl4-openssl-dev Homepage: http://ipe.otfried.org/ Package: ipe Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends} Description: The Ipe extensible drawing editor Ipe is a drawing editor for creating figures in PDF format. It supports making small figures for inclusion into LaTeX-documents as well as making multi-page PDF presentations. Ipe's main features are: * Entry of text as LaTeX source code. This makes it easy to enter mathematical expressions, and to reuse the LaTeX-macros of the main document. In the display text is displayed as it will appear in the figure. * Produces pure PDF, including the text. Ipe converts the LaTeX-source to PDF when the file is saved. * It is easy to align objects with respect to each other (for instance, to place a point on the intersection of two lines, or to draw a circle through three given points) using various snapping modes. * Users can provide ipelets (Ipe plug-ins) to add functionality to Ipe. This way, Ipe can be extended for each task at hand. ipe-tools-7.2.24.1/obs/debian/copyright000066400000000000000000000017711420423641200176240ustar00rootroot00000000000000Ipe 7 extensible graphics editor Copyright (c) 1993-2019 Otfried Cheong Ipe 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 3 of the License, or (at your option) any later version. As a special exception, you have permission to link Ipe with the CGAL library and distribute executables, as long as you follow the requirements of the Gnu General Public License in regard to all of the software in the executable aside from CGAL. Ipe 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 Ipe; if not, you can find it at "http://www.gnu.org/copyleft/gpl.html", or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ipe-tools-7.2.24.1/obs/debian/ipe.desktop000066400000000000000000000003611420423641200200330ustar00rootroot00000000000000[Desktop Entry] Version=1.0 Type=Application Name=Ipe drawing editor GenericName=PDF Figure Editor Comment=Create your drawings in PDF format Exec=ipe Icon=ipe Terminal=false Categories=Graphics;2DGraphics;RasterGraphics; StartupNotify=true ipe-tools-7.2.24.1/obs/debian/ipe.install000066400000000000000000000001121420423641200200220ustar00rootroot00000000000000usr/bin usr/lib/lib*.so usr/share/ipe usr/share/man usr/lib/ipe/*/ipelets ipe-tools-7.2.24.1/obs/debian/ipe.lintian-overrides000066400000000000000000000001621420423641200220170ustar00rootroot00000000000000# Several scripts use the non-standard interpreter ipescript, shipped in package ipe. # ipe: unusual-interpreter ipe-tools-7.2.24.1/obs/debian/ipe.menu000066400000000000000000000001401420423641200173210ustar00rootroot00000000000000?package(ipe):needs="X11" section="Applications/Graphics"\ title="Ipe" command="/usr/bin/ipe" ipe-tools-7.2.24.1/obs/debian/rules000066400000000000000000000007611420423641200167440ustar00rootroot00000000000000#!/usr/bin/make -f export IPEPREFIX=/usr export INSTALL_ROOT=$(shell pwd)/debian/tmp #export DH_VERBOSE=1 export DEB_BUILD_MAINT_OPTIONS=hardening=+all # Tell qtchooser which version we want. export QT_SELECT=qt5 # libspiro before 201907 apparently had no pkg-config export SPIRO_LIBS=-lspiro export SPIRO_CFLAGS= %: dh $@ --sourcedirectory=src override_dh_auto_build: dh_auto_build -- all documentation override_dh_install: dh_install dh_install debian/tmp/usr/lib/lib*.so.* usr/lib ipe-tools-7.2.24.1/obs/ipe.spec000066400000000000000000000123071420423641200160750ustar00rootroot00000000000000%global majorversion 7.2 Name: ipe Version: 7.2.24 Release: 1 Summary: Extensible drawing editor Group: Productivity/Publishing/Presentation License: GPL-3.0-or-later #License: GNU General Public License v3.0 or later URL: http://ipe.otfried.org/ Source0: %{name}-%{version}-src.tar.gz BuildRequires: desktop-file-utils BuildRequires: pkgconfig BuildRequires: doxygen BuildRequires: zlib-devel BuildRequires: libjpeg-devel BuildRequires: gsl-devel BuildRequires: libspiro-devel BuildRequires: pkgconfig(Qt5Core) BuildRequires: pkgconfig(Qt5Gui) BuildRequires: pkgconfig(Qt5Widgets) BuildRequires: pkgconfig(cairo) >= 1.10.0 BuildRequires: pkgconfig(cairo-ft) >= 1.10.0 BuildRequires: pkgconfig(cairo-pdf) BuildRequires: pkgconfig(cairo-ps) BuildRequires: pkgconfig(cairo-svg) BuildRequires: pkgconfig(freetype2) BuildRequires: pkgconfig(libpng) BuildRequires: pkgconfig(lua) >= 5.3 BuildRequires: pkgconfig(libcurl) #Requires: tex(latex) #Requires: xdg-utils %description A drawing editor for creating figures in PDF format. It supports making small figures for inclusion into LaTeX-documents as well as making multi-page PDF presentations. %package devel Summary: Header files for writing Ipelets Group: Development/Libraries/C and C++ Requires: %{name} = %{version}-%{release} %description devel The header files necessary to link against ipelib. %if 0%{?suse_version} # Suse specific %else # Other distro %endif %prep %setup -n %{name}-%{version} -q sed -i 's#/usr/bin/env ipescript#/usr/bin/ipescript#' scripts/update-styles.lua sed -i 's#/usr/bin/env ipescript#/usr/bin/ipescript#' scripts/update-master.lua sed -i 's#/usr/bin/env ipescript#/usr/bin/ipescript#' scripts/add-style.lua sed -i 's#/usr/bin/env ipescript#/usr/bin/ipescript#' scripts/onepage.lua sed -i 's#/usr/bin/env ipescript#/usr/bin/ipescript#' scripts/page-labels.lua %build export IPEPREFIX="%{_usr}" export IPELIBDIR="%{_libdir}" export IPELETDIR="%{_libdir}/ipe/%{version}/ipelets" export IPECURL=1 export IPE_NO_IPELIB_VERSION_CHECK=1 export QT_SELECT=qt5 export MOC=moc-qt5 export LUA_PACKAGE=lua pushd src make %{_smp_mflags} make documentation popd %install export IPEPREFIX="%{_usr}" export IPELIBDIR="%{_libdir}" export IPELETDIR="%{_libdir}/ipe/%{version}/ipelets" pushd src make INSTALL_ROOT=$RPM_BUILD_ROOT install \ INSTALL_PROGRAMS="install -m 0755" popd %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %license gpl.txt %doc readme.txt news.txt %{_bindir}/ipe %{_bindir}/ipepresenter %{_bindir}/ipe6upgrade %{_bindir}/ipeextract %{_bindir}/iperender %{_bindir}/iperender-par %{_bindir}/ipescript %{_bindir}/ipetoipe %{_bindir}/ipecurl %{_libdir}/libipe.so.%{version} %{_libdir}/libipeui.so.%{version} %{_libdir}/libipecairo.so.%{version} %{_libdir}/libipecanvas.so.%{version} %{_libdir}/libipelua.so.%{version} %dir %{_libdir}/ipe %dir %{_libdir}/ipe/%{version} %{_libdir}/ipe/%{version}/ipelets %dir %{_datadir}/ipe %dir %{_datadir}/ipe/%{version} %{_datadir}/ipe/%{version}/icons %{_datadir}/ipe/%{version}/lua %{_datadir}/ipe/%{version}/styles %{_datadir}/ipe/%{version}/scripts %{_datadir}/ipe/%{version}/doc %{_mandir}/man1/ipe.1.gz %{_mandir}/man1/ipe6upgrade.1.gz %{_mandir}/man1/ipeextract.1.gz %{_mandir}/man1/iperender.1.gz %{_mandir}/man1/ipescript.1.gz %{_mandir}/man1/ipetoipe.1.gz %files devel %{_includedir}/*.h %{_libdir}/libipe.so %{_libdir}/libipeui.so %{_libdir}/libipecairo.so %{_libdir}/libipecanvas.so %{_libdir}/libipelua.so %changelog * Wed Apr 07 2021 Otfried Cheong - 7.2.24-1 - New upstream version. * Wed Dec 23 2020 Otfried Cheong - 7.2.23-2 - Run doxygen as part of build process. * Mon Dec 21 2020 Otfried Cheong - 7.2.23-1 - New upstream version. * Sun Dec 20 2020 Otfried Cheong - 7.2.22-1 - New upstream version. * Mon Oct 26 2020 Otfried Cheong - 7.2.21-1 - New upstream version. * Thu Jun 25 2020 Otfried Cheong - 7.2.20-1 - New upstream version. * Sun Jun 14 2020 Otfried Cheong - 7.2.19-1 - New upstream version. * Wed May 20 2020 Otfried Cheong - 7.2.18-1 - New upstream version. * Sat May 09 2020 Otfried Cheong - 7.2.17-1 - New upstream version. * Fri May 01 2020 Otfried Cheong - 7.2.16-1 - New upstream version. * Sun Apr 26 2020 Otfried Cheong - 7.2.15-1 - New upstream version. * Sat Apr 18 2020 Otfried Cheong - 7.2.14-1 - New upstream version. * Mon Oct 07 2019 Otfried Cheong - 7.2.13-1 - New upstream version. * Mon May 06 2019 Otfried Cheong - 7.2.12-1 - New upstream version. * Sun Mar 17 2019 Otfried Cheong - 7.2.11-2 - Do not require tex in case user installed it differently. * Sat Mar 09 2019 Otfried Cheong - 7.2.11-1 - New upstream version. * Mon Feb 04 2019 Otfried Cheong - 7.2.10-1 - New upstream version. * Wed Jan 16 2019 Otfried Cheong - 7.2.9-1 - New upstream version. * Tue Jan 15 2019 Otfried Cheong - 7.2.8-1 - First try to build Ipe RPMs on openSuse build service. ipe-tools-7.2.24.1/obs/make-specs.sh000066400000000000000000000022641420423641200170310ustar00rootroot00000000000000# # Produce ipe.dsc and ipe_x.y.z-d.debian.tar.xz # # To create a new version, update IPEVERS below and add a new entry in debian/changelog # Then run bash make-specs.sh, and upload the three files to build.opensuse.org IPEVERS="7.2.24" DEBVERS="1" SOURCES=~/LastIpeRelease/ipe-$IPEVERS-src.tar.gz ORIGFILE=ipe_$IPEVERS.orig.tar.gz DEBFILE=ipe_$IPEVERS-${DEBVERS}.debian.tar.xz DSCFILE=ipe.dsc rm -f $ORIGFILE $DEBFILE $DSCFILE tar cJvf $DEBFILE debian cat < $DSCFILE Format: 3.0 (quilt) Source: ipe EOF echo "Version: $IPEVERS-$DEBVERS" >> $DSCFILE cat <> $DSCFILE Binary: ipe Maintainer: Otfried Cheong Architecture: any Homepage: http://ipe.otfried.org/ Build-Depends: sharutils, doxygen, debhelper (>= 9~), zlib1g-dev, qtbase5-dev, qtbase5-dev-tools, libfreetype6-dev, libcairo2-dev, libjpeg-dev, libpng-dev, liblua5.3-dev, libgsl-dev, libspiro-dev, libcurl4-openssl-dev, libfontconfig1-dev Files: EOF cp $SOURCES $ORIGFILE orig1=`md5sum $ORIGFILE | cut -d" " -f1` orig2=`stat --printf="%s" $ORIGFILE` deb1=`md5sum $DEBFILE | cut -d" " -f1` deb2=`stat --printf="%s" $DEBFILE` echo "" $orig1 $orig2 $ORIGFILE >> $DSCFILE echo "" $deb1 $deb2 $DEBFILE >> $DSCFILE ipe-tools-7.2.24.1/pdftoipe/000077500000000000000000000000001420423641200154705ustar00rootroot00000000000000ipe-tools-7.2.24.1/pdftoipe/Makefile000066400000000000000000000021771420423641200171370ustar00rootroot00000000000000# -------------------------------------------------------------------- # Makefile for pdftoipe # -------------------------------------------------------------------- ifdef COMSPEC # compiling on Windows? CPPFLAGS += -I/minglibs/include/poppler LIBS += -L/minglibs/lib -lpoppler LDFLAGS += -static TARGET = pdftoipe.exe else ifdef IPECROSS # cross-compiling for Windows? CXX=$(IPECROSS)-w64-mingw32-g++ CPPFLAGS += -I/sw/mingwlibs-$(IPECROSS)/include/poppler LIBS += -L/sw/mingwlibs-$(IPECROSS)/lib -lpoppler LDFLAGS += -static TARGET = pdftoipe.exe else POPPLER_CFLAGS ?= $(shell pkg-config --cflags poppler) POPPLER_LIBS ?= $(shell pkg-config --libs poppler) CPPFLAGS += $(POPPLER_CFLAGS) LIBS += $(POPPLER_LIBS) TARGET = pdftoipe endif CXXFLAGS += -Wno-write-strings -std=c++17 all: $(TARGET) objects = parseargs.o xmloutputdev.o pdftoipe.o $(TARGET): $(objects) $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) clean: @-rm -f $(objects) $(TARGET) xmloutputdev.o: xmloutputdev.h pdftoipe.o: xmloutputdev.h parseargs.h parseargs.o: parseargs.h # -------------------------------------------------------------------- ipe-tools-7.2.24.1/pdftoipe/compile_on_windows.pdf000066400000000000000000002427171420423641200220760ustar00rootroot00000000000000%PDF-1.5 % 4 0 obj << /S /GoTo /D [5 0 R /Fit] >> endobj 7 0 obj << /Length 1707 /Filter /FlateDecode >> stream xڝXm6 _}:=˖}@?tחv.CQàĸlIHQv쫂Jb(oI,g3M.xGzd,EŪ\|v~S0pK7WNMDl` rݫjZU]nQ O<|_A(؁4̳~KoZOI7EZGEܮo MvtR}m`QsJQ>ο\!]!O0g)NKXϭWJ endstream endobj 5 0 obj << /Type /Page /Contents 7 0 R /Resources 6 0 R /MediaBox [0 0 595.276 841.89] /Parent 18 0 R >> endobj 8 0 obj << /D [5 0 R /XYZ 27.346 825.923 null] >> endobj 9 0 obj << /D [5 0 R /XYZ 28.346 785.197 null] >> endobj 10 0 obj << /D [5 0 R /XYZ 28.346 800.223 null] >> endobj 13 0 obj << /D [5 0 R /XYZ 28.346 718.521 null] >> endobj 15 0 obj << /D [5 0 R /XYZ 28.346 591.428 null] >> endobj 16 0 obj << /D [5 0 R /XYZ 28.346 269.602 null] >> endobj 17 0 obj << /D [5 0 R /XYZ 28.346 182.913 null] >> endobj 6 0 obj << /ColorSpace 3 0 R /Pattern 2 0 R /ExtGState 1 0 R /Font << /F43 11 0 R /F16 12 0 R /F44 14 0 R >> /ProcSet [ /PDF /Text ] >> endobj 1 0 obj <<>> endobj 2 0 obj <<>> endobj 3 0 obj << /pgfprgb [/Pattern /DeviceRGB] >> endobj 21 0 obj << /Length 999 /Filter /FlateDecode >> stream xVͳ6#7I|rK3yI:Mgr; #wlCh v߿]AG7t~X1F$z)""<J}_߯ Ω_n3Oq|(HsmTtE?ȢDUAI*]'bâU+B.{fЛQiQڍƭ ;T;]O*TǮndeSМ{$†\luFnʍIPOHbX9i>XcϨn8ȦA4J (]=!cV/&sd n74V!2 ]Xbj~Ǻ,K MtB#V˱Q'm;|&wDK)5skpks9%^A7ԩ0[^*SD?eD f$Ï;ޙny3=O 2JwsuM+leoһ0AXV+LIh]wRRXY'aϺ,iC?XUC[.M5Vz#؟7Cf)'}ue x(F$;w*DY7&lp=άBioթGU8c eӾx^%_umw;\><c 1';LKSe(N@7tK> endobj 22 0 obj << /D [20 0 R /XYZ 27.346 825.923 null] >> endobj 23 0 obj << /D [20 0 R /XYZ 28.346 785.197 null] >> endobj 19 0 obj << /ColorSpace 3 0 R /Pattern 2 0 R /ExtGState 1 0 R /Font << /F16 12 0 R /F44 14 0 R /F23 24 0 R >> /ProcSet [ /PDF /Text ] >> endobj 25 0 obj [500] endobj 27 0 obj [514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5 514.5] endobj 28 0 obj [543.8 543.8 815.8 815.8 489.5 271.9 489.5 815.8 489.5 815.8 761.4 271.9 380.7 380.7 489.5 761.4 271.9 326.3 271.9 489.5 489.5 489.5 489.5 489.5 489.5 489.5 489.5 489.5 489.5 489.5 271.9 271.9 761.4 761.4 761.4 462.3 761.4 733.8 693.2 707 747.6 666 638.8 768.1 733.8 353.2 502.9 761 611.7 897 733.8 761.4 666 761.4 720.4 543.8 707 733.8 733.8 1005.8 733.8 733.8 598.2 271.9 489.5 271.9 598.2 761.4 271.9 489.5 543.8 435.1 543.8 435.1 299.1 489.5 543.8 271.9 299.1 516.7 271.9 815.8 543.8 489.5 543.8 516.7 380.7 386.1 380.7 543.8 516.7 707 516.7 516.7] endobj 29 0 obj [814.8 321.4 546.4 803.6 631.6 1007.7 814.8 856.6 739.3 856.6 749.7 642.8 792.3 793.1 771.4 1092.8 771.4 771.4 707.1 343.1 578.6 343.1 707.1 900 321.4 556.1 597.8 514.3 597.8 519.9 353.6 578.6 597.8 276.4 308.6 565.7 276.4 919.3 597.8 578.6 597.8 597.8 395.4 443.6 417.9 597.8 533.6 790.7] endobj 30 0 obj << /Length1 1403 /Length2 6029 /Length3 0 /Length 6987 /Filter /FlateDecode >> stream xڍx4ֶ Zhчu{NDc0D{ !: G'JM=5k={_{p (!mHZ$(i PD&0пD&Pw ;ؔh P<@"H\ Re'-xD@QDJHWw#S[7Pp `@v`*Bp!}#*%$%vA "dy^0#{B(t.?8F0_C= ` p@aB<vPw:PC E Ht%!~!+C8ap(@WUKv`8 {ap-{`>a sEQ0/B`ڬSB@hѯ)ܡL}3{eCa*dy@5`0&h(PRLLG_|\~HW=4f|P@  B`gǘ1X1>(TL PSQ H H .. guoz`؟GF =  L&GƆ :HP\@!,{Gpo?_v} 0z@cfCCM 6^ 43# @P0o qKK!Q_L_>A1~g]ikE`wwF(U;oH4&G:X(@#1vxc0^u( !ABÜ^(0x l୬vF'E9g9jgM)ؽ37W11|Qwnrz>Ko]P`qI0&NqDfckb:s.#rPr(9%gMg@)ub?1ge_E?"naakhimn_Qfo؋J:*ytIPXJilt.86? ےD<to>~QY>b1.Dr99ڑ&]t(ZߋK \֞Bka/4?snLK ||(gv7]auZ/yҌ%qmPO! dpYG& &*bZYd5OB^TA~^[Cyɹq#Y#mLBsp)rRJ/L/= iI>^?@^~KMD[C!a+·6:\a'gZS=~o#IAB]CxtjdwL3_vpm{7\RI +D[-Z'=O,ΤmZ}j9pQᦨ M5.)B;S8$PmxC BѾh.@Sk9BqQ/ 8DU⇧ȇBfbo}]_n[1(hE[)=h(4O~Whru%n-rEK9R=͏D=IG5A߆$9?0aa2VI=*jI> gQyEmzɬgX_$DPRMi? Rpc.G}yahPeYAVY;8Ϫq+ԫwPFOJgu9!}r\?o"epc o*ItBYϋ5:$JCT&ȺEּdTZa륕*7eN4PJ+Wv$#%pMgkV8׳®Ϧ,Tu憜zHd 32Ө-Aч1n/N(h1ܬš[ rWËIlƥr'ur)3a➤2z TY|NաHZf}kG$2E' (>5ANF\tl_㇓~YYki;3P\J>k5/^[B%Rjn\t[74.91$}/!U,n9c%'pñX`;h4b5y|dI!OKhBpu %Ydm cV}[ 0d+NvaeM z%(CXX2Z'xP;>qVNi)7"5?(?1FzuE .O} ):p@}|j]) ج2Yg[|'?ES2Œu<{K >L4X껞v'2wK=Lտ9,LCOӠ*M<8HqKYV-)ɱqCX?զ }bjjx3rwSWUf@K#[a,!>.ع./jJ> 7!汫brԋ߷j~89n71Ii+ϹADi.F@x$wvmX6XDf'TkFKjYǴOu韝{W Ǭ8ȁ W{.amXd.ȩ{7[_k@ Oڏ:wA@B礱*w3"!,*d:G>GbMty/#xxH"OKa)5dEI"8tgX$s*8xĒjO&~1~i_<>>*[G,4qr%-B}S;f~seBЗB%*[tS.T3oԝZ̊ {D>7qFY-b U>$յZ[r灻(Bqb^2aL[@{Ȳ=Hud2'8Iɏ I3[ɻlكh;!حiTެEGaeW%RO?4 ~Z6J]$l~8fM/8r_:6GT_*[k)s|f /B S(`xηiꆌ9F@Be -tAjk ؑT/tc˃Hd|MZzdH>.Ef쇒*4N2DO,yݬ&9+V0Uwb֧=۫nXV^/Oka,Jzg=a1a_zMgEIKݞ6jўtz_3 zTFaSu$+RS"sE=+  A\.{YƟ%]Y;Kmp̔%+ydYq,b&Wn^y?HF;;sIQ._XtҎg"u;"rt a#n9hBix:ì{̚Q}zʷ(csR\S6~M}̀o׏<#rSI9HH ^͔m{*BUEK8'f-zB m)t\"(IXŢclsqayY5W/L%4d=K_,Jh!Q"䑲Yw迦O%Tku6b%,b]Z EJ6O]lGI;<'ҕskr;co$׎^t;(<"h["WH] iEt:Z=K&Ij}7śuР<ɚ{81%]Wv*wO{*j,rk,ו/NYL.i ~D"d>{mJ=s6O(oi<AG6V^8UDo|I!Ҋqǎ7M]3w^r#_= _w_Ub}#rȾc魖bw±\' LN }plHlプ:0B*\WtEo#̫zf9$^[ڕM=dV0Y ?4C!RL2 1Zt+%!.T ߳b, F<˃(v Z1SJ%^O!{ZN?㡏5+#;|ݺsj\b^GbfȻ5u#s,KL{,vƂTf"S"XflIL{iԼ|1 _{s"g,y ZtͰ3Pس Kc*u!{T#wbzAB/𾏢x9;|y4GX=#[lg\_YeE~h{۟[ML3%פR;s!LnPSO.K~xZU[^l:DxBFIC%2`Hjx^xYv56KߴYշ{?Z!NJs˕ssc {;2Sd՟=WE iƤ ]Z%u)r:Uzj턜7:83-nN|UNѓg\hԗ`;Hr0q/h,ӇZ=w^G9XpG+fvPh5b-hk ~jɗa˂ifAgќyK"'krTUGO(νʨPꥪ޷GKI:$g̬WxҒe` Y%WDS8pHG1R&v#SYSSĘG&5 _+,/w1r^+/_=}b+Ք6_:Q8U9dS'8vd`'=b7eTo F?liG:Vt?V^.}|>V6L+Vi> endobj 32 0 obj << /Length1 721 /Length2 20029 /Length3 0 /Length 20610 /Filter /FlateDecode >> stream xl5:yǶm۶mm{=mm;77OjuzJ'UdD^fLtLqe9&fFF#= 7@b`ba!88z9[YX(MZY9lܭL,.nnlR13Z̭l" ZRJ y5-@ kebfbF0wpg0q7'w7sv@NLUH\A^ & *07JՅݿϲ5e?QgX0LLS+W= ÿt7wm?\GC*?h7[[y#;33@`gd_/ \Ar43UrGjs#$_?"gfjffύ:z#I2jKj7q0+YzK`k9#Wg+O#ʃ_-vcdб03Xq2蚸9;ٻ[r߅dfif`bV'V4W N37cFwFj ^l1}3QT Q k I_ֻR[C5:M ҜehᵱH]2lŠ@p66v[{w@QDB O5="?y#Ga{k;uTTgDW'aD_tpC !(sJDtC_qNqƪOUddF`I"qn3?wO=IL.I{<iG-J0 eZ;&$n|WyVsړsKLELpdb0Ѝn73g[W Ie=/3nؕF~N]FK+񦭚ԧu,\?>.iYQP; ~(TVT\&|6TVDI>y4=ù$6kE*<ܠ_@T_V<"Uطg <"z3&Cؚ&Ii d6wSL(Qi n ˄@ OW_Ju`pg[P/p; (\&;W ^S%m "Osh.Le0knJ6&65AI +-dUSMjBɸR9H gT (. }JI~Dۗ#1tno<8uyw3һu>/oY(/--AMaWG鸱$NsT/i|«R0~%! 3MepjI赩x1>WƟ\dUUsm]ڄ8ØbUNGQo9K/N~c>)W;3nV&]<эO;ob7o4ɠ'2"b&~^-(5jc.=}7n@7!㠈raQ`v6/kdDa(l賸r… [/I @u)]rx;w *ANE40|!X)ʕ{a|q`[T0e⻺i7wλ M.k-Wf85lzSRaCb6ݿ` d!u3%5^0Έs 0*\k6cq .)p4arƮPE hr odK)[V 'jo5yXpxr]ʼ|^OzRdDŽZbE) iK#*Ը3(&j_bx*hc a9ΑÀ2xRk{N T^; ]npi!LFKrvגO5~o˹0"Ar3yPv=Eƍi\Wg?ū:›4jYL)dˀC#e$${Mc{aW޲c\}CN{CzN@=vA,}ڻOGZhDLsGd@0qW,#ZFq^j#4@o.w:=+K(i&1He{ɮDaDOdY(< (CTc(rCp J2`Gӗ2͆2&VAWtqb ?Jn&'p%pQJ};ƥBR:f48>pvN8zOJ.1B?Ӓ A 9ոvr}WUF7KL,Q-` U%8qwG%^:SfL|(=:YU$U9T(pIb1s@@A8ݏW"㤀-n^ -vױT׺sqyE`Oj̑ړS`7A&ts+ÈEL¶.0F3gi}2_d@:YYp<$~X >47Z޵QvHC|gL,e8$7;M:Z U$$5S$P"; D:R5?hy W./It+sNEOWk뚡0.JpAV|>S(/tu|T*0O)v|45O&r[/+ dsT)A, gBLoU%RL= 6 _.%Wh>}ɪNEPypB2O*Gl9{)h\K>8A*O1hl)}%eR}clM>?NyqP B`JwgmA)y#gx3ć+n"Kۍ'ԯ 'B [ڪ_ ]zK?ћ U9w`}xbYMWdֿw,pjg1&QIBe h(0˒.ynK)bIuJS#o$@8t%a͛}r8uX[\avDAqp*fnI:jhXb$m+2ٔ &;`XիdsAElWV }Qjԛ}\vx#3?&UJFST,Edw{hEďqEFsȠ#T9yvVG_go>Ŀ#=̀y[k4KfhBZX]~YeNV͜H朂|a[ koOn9C^OyhD>We9XfEɥicS&+y~bP\6̴N>,m?)kӪ~h!4RZ=Oc/`}s]TѸvŽ}ˆ1B _qqDJ̀ӿ :z1 _5z^c=]3xQ7ŔmAn?>P "IԒR"ap=VL٠ר{X }Fj"Pݽ[|K  x1xǃGZN ֥G;=#Rx3,9cFʾ=IA8|zA8$hl&Ba`9jA΄B:\ #}ImZTj]f/k7\#h2>ͷU|8} Yi/ L |OgE2]dj pOlԦźQqBN),F{ `5 .N[uS˞ $ wRsY,MY'v9eBD#٬F")&!Wp8p>:5H ~ZKmVC 58| xM Jleiolq?ӥeƙ,SCf4~)S0,f9*V; 啱Tڑ r`Is(rMMfg S:K^a!|;fShJҊ|ϴ" qp?o9uOTZ V7"]iHYҗ2Jߒ _bL[J ÛLבɚ_h\T7(D,TȑFAcLiDvlz(cҚ.py)!TԐÝg"iݻ˙|Dn&i9"_ɼ (LI,j {S iFπhy<<~)k.;ap_kp'|T8$ O##Gg%`!{آ F[B"RwDX$ܡz#}˹X ]!㭍Fzo'JqȪ zĢz7;j ߵv5~CνDmqaA u`3K Iar{ёt*^HAZ0saR|nj9 ٿ0h&ecu `:p28Aw;=k&RKgA!ftL8HnI}y7V>8nl0 `_8$b5%i)(J4&4p+uK[SF/ϰk]roO><& Uzp~~' my<1.t$9 .DJ2@`pݬ絍/BȏD` n`y@ƪ;mFhh JkK`$o.yQBME'7Ԗ|0 $/gwјXXY6mo ijBu~x0 N-rjzoKĢ.41.?1öutaP2Ɖ+Ce![FZ(dbc}`g'8Q0j|,=yb%BW^MWBh5H"M9Wnabͤ2%;+]o(%r|͛1jPw o=wm pAn(ɗ;o"lD߬uے |8+v>$M Sl6 \ds/3^ғV'N%(!=|,)[T@q^PSJ:9H跗83WK@5]9"╏PԣRǪ Hί_;QvG:]!rVڇfk?2fĜS*s/͔*wlE",En:T,"Hkm0i8]d}Wʜ h2 ȔE(ݠc]ԇ7T\y6>ɇgPeB9ϴ7VS~fꔳƛҢNa&. U%-ʚ4o\G&1%ϡsu<'m ^eeNpkKZ˱K W1}7|1yzZ54G 5AdDa,)B0k3}\GnpaՀ&fi Nqn$[M0^vMjL⵽ &\0;L<,X؋!"U8B 'ZOkl9mĦ_Dq; &n".)vZǥ]e<)>N$+SY9t}KpʓelXPXaXVߜj☃+f%r}]G ;^\f w7 5>.c+^~{ x:X"24tGk}ї+fJvQ0 mpl;^0pь|zN`+TEkT7*^`E.)^"uۖwZѺEB[lV' uGaQv&=51=gx2~0ژ4bZD}ۆFVy:Y,.ո]-fA'I ѡ1K21 -LV9Y8խ]5DۋNuhX*ȡ(:&l:{M%ziw钲l%a MkTey{Mɥ@2꣧0^ql6R@,t:24RA| Eѓor|7g U&yA}P&>u@wHSfşj7]%_I>,T"MK <NkhMbI-)ǿqY 3 q,xtLÇ6rBÊpOjUe5~}ءy@Mg˺fFG0M c;N둵YI{g8y5 yg'4s:ğOIy&~-<Ëh6qpLD >:| M4k7k` Nc8KTk4|WBH0 itbz  O'/BҬ)ڭ3ăx;Pa@+FT>TSD4w6Gq<1E*t !e tV~iGI~18ΈG|j:JY7tz, ~Hf k;R.͵+n<^ N$e)/j8.%P]Pe̘Rסo\׵[7wM֣[:}='3|a!.BY}UUwJ%H7̽A4R:4fc_Un.H|0XdOoA%@uz-W7y& ye~:oI TM:I O8*ʌRk`M 9p!".ag_JR"GEF+r:1E LpM;gYNӽO1YcU,x|`qJ<8䄨or|ةO`JhnKH$t~B Y޲^|FjaDMڪ %P5$ } H,ZI\aMg=Cر ) {veNZ.D&jNNؿ ^ޜTSfKjR3$Pn.v<2b>e ]?<15vFd.pL!=I[PE{@6N]1YBU4\Zi.xO"g0ZƮquePbWPt#LmRR`pЦŘ5^].*y P+vVr$$'.fa<šXX?Q1AG`vQLEAs*ؤlK w%áTلlzA5̫M|ftvlp7r" Q!5 #C'b4v:B#:Rn0og I5yֹEމ pmrt,0ޔhYi\D{'4ca;pLp# e>LÞR7OQѬ}/q3iS} ٚ`n e" S'b@ϳpvun+Z$1yP*=r/ N(YFRۛ- {-qeaΓRcK~"`a `I N\kV{M98*>)cYBS`f]YVZH& _@Er\EݖFt.aJUe~XB {'L胼a 2uRt:㑥W:S'%c ˤ@a~ ^&Ne{\qR|~G;)bY}n)l[V : $ w;ix]5M쫋u}SJ]/lsPVEEf֙"#[n \۫,0TSkYĩmWU4oֺsn벨ǤLڭZ-zr"LBX(-6ڈ(A n: GAq}I?mTR%9o0p!)fyjQ^,ߒ+\&Q:ˤ̝̾V}?ItY3^f5NZp ]s/890Fֱ Fr鈌9;{$75)y@lb֟-N@'NDq=aj{6"5 4#A C%=NT#{"7#Ej 6gfϾ+˳@!6:ӻ{p{C[XWX\=`-*p /}*uI+?V]:)xjX:j#Xjp8VK"ULOLOd~%=TWT' {<\ ) 2DA̢Xw YlP%(%ZI ZŇClh5~}7GݿZZO^LDlrKĪ=P7.sHEs_N푅%Υf;2[雼*+;ʙr u-M2%"g.|LLJ!VXq0uO~;ްnYIڬb$&(O;I^ I "Gr뛢sbMϻXq^ϋP<$!+D;l+Dx?^DJ03đ }*y-zNZaF^~^_ p9Go+`=T)EZc˵lDyMJnM)`GtǒEI hb;!Mi\]X!rE\8 &v*.9>6ZCD;qv#2Y B/'k #q}F \49OXW??8qs%"21TW{(դe}!hiHMe(;~r!!"$7raٕ#XK+~$m$^.$;-5Ǔ%) McGw,W[U΁9:&m7D Й5^lCah߄I4L 4i7[H=?x"p*l)OvqW] e t "/W;l(sNf}wY.JNr*~H>_b%d_tTV~ZIQ}7 (gLH }[m>x+Y@K2:SGX\0(\<U:{W ^(҃v¢jeHd/}9vuK_LRj_'|OwGv~rU/T򂭽:=z~viͺ Is),L73@ u{(|`姫?NNgz:ZdTja6!_;>~lE&j5y䅢dqeZO=Pfa}Ź2(TwߪҴvmCb.ҟrD9WE;vh  PlL[ؽ?0]Y #j䏂sV.KrI F ByDahdĝSlZ>0`TbNAJ(,X5OvIzl|71~Bl6/w 9-w8޻G6~4 c1+Hs Hh󳿳zGm{Fn׃fXx_8Z .ĥ?|zӻU5ށ{RB)[x\.]U}jvWB^9Y  ࢟|?kvkiFG4rQ}YS_sϗ)> pZBVK}D@ i]+/C\~ g䧑²,˻-WJ-b4 35v3_X.(@߰`::Zha3eL]JrR7vR0W,4IypU_N>HD|Vq=2[TjMMz:2Xggd-CmH+FHS 9BUiZf%ҝxI:Wc P7J,건5$ h2o;˖uSH1RbsXd7'n=בsLWG-o5]`ZJNKrۡN]Lc:b~N5Wr\J[:+vnV@rs~540iI-0{yd aUSu1wlͿ QrԄV ӎNeS3oWEk3 Yg1>ǣZ=%8Zdz Qo&~:&6WSҎ"5_U7lH#@AD9s@20|{dxz?,lnA@t.y#.6a3"uKw$!TARMߖ7~5? h241,"KL]b :%VWƵEʒmʺ7鍧4jԦ3O85˱O/Mo߶Vhm2X:.K;? - $Tͅk,h \-АmwmB.UEh#Gp*4+4Qƭre:y‹)fW.lS\91&nE7W f >YD|+sKNΧB&zU tjGVP]yJ5ۻYVtdV7fwvM~lbmե{!o״ Xqsb !&*Qgޤk}zѭyV)6}11e>cTEwse Zʞ9g{ R# X)Bؽ#@NeF{rTPc7)|AKI~$сolHooJ<'7V^eФgoBmmN*0D_<{i[4Y+`\ԩ"]nˌ8NظugRRN9ifQm^DE܊ wϱ`W̎b=TkǸN. 5m6-72.oIBs$kfvXCd\/E=_h=3Ar촾W5&exHn ocۺpx Шjq-ܡhPP)ҽ~cC춟gvjtPKMDqKX`E^_b28n偒bEކڿ$;:d8HO9 c6U՞@T={LjJj v{ҏBzJ+&fM~ s :w;@R?7 1U=IS( 'YL:XQMⓜ',Ă1c-U|]@JmfCOAX:b$NtqMAc*y֯:mdbMs$F)4M3UEuUWN TH/GmS?Ķ'Q BT{)0ݵVz)7M3#^<<.^3vN|1Z": e5 @%kxvҼo{B7t V>QO{r3 B4 %l Ѯpl:vSb %fh}[yT;GGCNQL#K< #!8ݮZ/(.KUیW2KN95k19Xv:aFkT-'障ڭ$KYVŷ< &6LR/Tm憚ݫڄ3*,x6 mۏ=/&;ԬfeDGMC&<1#[CR X9W?ݺdB/*EI;\F\v_gڦV NU;>$ő) * Z腺sGn0D~y1n#Zsqk#ƶ=ǟ3D<5CN]LF Fڙҕף]4~]V{"Ys756ޠ?7Op߆{{ӣ ! "1J4QM$Gd\^5+2ZsT(mx S. l&r`ϖqP|xGX]3 (_h~ Ln[1 VE΍R> ®~`țs;}C qb*2vC[LT·S f,2{\&6DA++2}Pρ9 uQ)_%@ s~!|5ԋ7G`N.ȕw$He r^cI6Anl1Ⱦc0órSL:lBu$YZPyW'Ue _ ?ӷ o;LͩP!M>fOQskI\q}\C{Fϴ N\<\5J!"m_<ͺPxpS%m 6*aM~†YR<.Z4*Tc\v]eiWQ4KxúCaih*dR](?wKןFogǬlrp¶2cB8Gkޣ˥}֤ ؀6:F:#b1 Dzfd5ۯا.ՕޚIL#DVVfy8Q=5cpH5 ʘV+8A*$$# C-yx2C% m['4νJ‚*4ϳ7Nf+5yV\6b,Xi=K|NA`QO 2YVdXdx@-44j̻jkg'_ Ծ `Ŏg:;PU`'~H/x9J_ahqK_6X7UR?&^ a>ǿ7>|#_y@!d)s76t'!{e*W Z ҧ2+FA_ځIV;_?-[6U@Im1Lo8)`wōgX:%:6i S] g7i(ƈ~R>Ҧ cbKl լu|d/ѥ(Z72jcejB~e0$B&)x;Wl4B*O21E)ta?z#pi2iZJs _NnEN+I1[m$銒H$T9vh"C1yʫ _ʕÄa!B}]5/w(׭K.4k(k(Ւjx,5Ք= &rǺyj~V֨{/-2{0h/ՒTOoOلWDV}^[`}T;:`'q(: Ἅ Ҷ%#1)3%O0{`2|JBj.<، 0k6gͿhF:v)ȼHȃʠ4 t Qevt;K;&m2LlYm(bưˌFh&^ <'i/))5Y3R>hI ,YM|nP:MtZ6x2CX҅DtSAu )=bHla>I~|6z1ut-d~ {kMW O…&=%e'ZUS܄5nA O2@P]a^v<ے&Ufe}\Yy> endobj 34 0 obj << /Length1 738 /Length2 13221 /Length3 0 /Length 13802 /Filter /FlateDecode >> stream xmxcpݲĶĶ&m۶m;۶1m;7gSOFWwսz:+{؛021pDٙ~蘡IIM -l M8j&%{# dghaf 0G P60pY۹Z]]]]\]x)M&!9y Y1 @ bhma02u29}[ ?l]M3uȈ( *D蕅iNMU %J:[fd[9 M,lśjcr@CJ_okkY#@d`G S1@?6p6K_2pp71p˾_ѫUJ迩[llgk? O:z!EiA1 N?f[#;c [3_r ,o`q2Ύ- ÿH:%(hEefb033~r?p\MlZsgLLMז팸-S[B}E g+gUV~_k!Y ^b)c^H5ę& Y~)&0zS׽UŲ1uN__ d9t(Ac/C܎_]x!G5٧F5)2{[ څ&3/(? ,ڴ >s深irդ|Go8pt(/z-aۄ42cGf[/Dr҇fQYr~.j[*Gl6X[mI´2#RkU+tO+ͬ9Heɉ/rug\zXC~?ԅ|(DEHoCuOP2_i|@FU2l} 'ԒQcE2]s^dž D8β$4ʚF(M玌ubDlCȶB1C k$ J 9/SeQG*Aº^T::bv O7)Iq85!"D7MړyLࢦZx؝=H$}4ާ+,7tm ?\~0w/vwkĺ]̠O0绢)/2*uW`=v'\#?@cnk }N# d6^Dyl,R=k?dV߁B5QP]W3_Xԣe^`V덃wrP̐G4_j101e~I%p m3J֕eC8x'Ek^+b"(ݦ} 4cwuU9f (VLۡ r;أw A"`g’ļ[W4X(#ZzEτs3Vv*c=3+b3x#^(H|3,nAq0*atT P4,T_!>YB w*%^-TkUVY-D{ OT~x#BP7 m]ځAdL('4ʀmƈ t.aYS!Ns1?jYơ|@!S^J'w>Gɪn{QTlHl)rszu|8 v""p-p$@ [VQoO><02Zwh#6|P(,zb@SP!)~+'iS&4AWV>l7ծL6ڔʇCRgĿ\T)O`|.㹫ehځ˞3|x.Hj>ݨRk?dϒ)雉]ڒ?V8lA4,lq(ueA[x+x 5įFFe?X?ǏVy(S띬쀾W C/^sآM SDHA*u]0)ޓ>0\G&,l0HAkKv'.>0tUR-h7fzD+ᴶփ."2[. ̜g0POTcrjF#y屗B??E`re @ p%e=^[Yz2j%yigY` ʗ=3bzR7B(;@K~}"TP.!=ex(AmSit7~gl2F$36:t 7O@^IZPR_ud`k *$yD‘<T`zr&0ֹ"F8L `<▰XTnnU틊 7_&E(6AzSQEC{sxwqض>d0ubq8!>Q3"kݔ}vo ,fЯr3[3*@R d`܆ȒU{#~9)s12+|~rS`o)P1ܶ[Sg Ek|gMSN ͦԅU\׉wQK^=: wzp_̳yF՚2KHVt{A)M% x:tZi<~ő ɆAkkA[}9ι-ɭl7oXI~20ˋ!SHQNm5=wmU0=wn,C쨯L t{:<7³`~Bo  :i(VDV-!:|KFS8\wUTYo 5Z8>&N8>7 ='qʢ,P^,_8d.K~QtB*=zKGtҞ+`Xz<%O<3fUno8!6spdC壙j SCeJO~6q/T&D/-Sl5XA@>L -M02]>]r#3ޟ**lW"=ػ8B/M(9Owo|J:uF!ET /jҽF7^x@(pR9,|Xu(P{90 %կޅoC-9i C)(.D1>Y/K*~lx< '<lrݦc7lFVд!.e3~PNvp8^xUy9)uysUZW#Ӊ񰸟SǤ@ ")!&Vv;W T0y90JIio9DKz8U./H Ijt:{eRԋ<$2]17 *g$OAUUSjd4ilA#\Dv6>qK|ZB.c:m nfxM^8$1 ^u%IՖx"X ս|fUC~ʛ8H9n)\{HYT@V6lg42`*!b6;-"JsZS.*ܮ,`11ոtE96袐5e:#,kIca1L q>i.7 ڊ޺[H}Ù7cA>U1"MMzTͅFZ!~F-MH 0@t1嶹TWh]N 0tv.+@T+ExQSٲiRoP^h3<_䎌uDEu* RW `uEq&<)$h/Jj@ h==н%4.`?VD(}^=}A%^#\n QBM{{ o_ਖ&ᦱϲE߲{  ~؆7-9Kxj$$a xCoj<ߺ t}"!j6OyTTL8>NZoo7Dey._w*-ud;J1Q¶dl|x@fX3:~,U'sH6Ed`<>i t;ɩ[`&,Q\s:1cޑyjc*|C$V#5I\gY5_T^\ RdojX c=W9qP̎gՒE'!*Dw$VJ BسފXЃO!}曐}#rͯ`k1/}NhP7; =Z\>B6,H."n|ԓak+ފޭBDflG@p%|3˸T37 )~%wCj_22j|ZN=-+w hi\&f9Xs8״~D$DŽWwAD6M6@ԑ8nX]MN <+/s򾌜їEhLO/K䭙!D8}O!Q8:! ރ*mS. Ҕ֢ܿ( "]Ud ~<6W!}DKsA̧2y fӏ7䖢9$B D(vv;ϢE!l]8pDyJ.DF8g\޽1xTVP;!(0䱡ļ"GE6yvdu:|]Ӄ{%]eH]Ͳ/!ü#& 3uA^U^+8ɡpܲd5Kp-E U]K΋h;H]̅*=FguC*8@d nx9ۧfCLEÚ`ҳⓩߕ'w&.GYiD+(`F[O ;7Tj*KHZoE _s1CAa̭t *༌Y$N }'y\7\ETLb!>ymP&ZR$(^hS>*r&OvtHP| Hf3 5UbK78\Bdt$F+ճȌ[uB!` ;2";tpmOM㽩eYWX|. H:5VXtUCTlSǹ6Ӂ'icŅ"+!4/ѹ?iYl lӍ:$cD\fyL߮LA-W$l VPoS"<8| XÈMD{1bߠP#'`,:;mwLp^7E.ՙ\zʅ^&ٮ䌧zbkB]X=UYvnD>ZKOlJ'* D/~UuQ`;3 3E_-gn7T#3l*gvJeҜȱ`8Op !P\Gk-<=V7M-^~FΡ,BS3j׬pW7ˤЋrF>ijc6ijŮٹ RQm='ԉf݆W̔zٟ^V"- F+{ #k69ڟё!"06ihdRMƝƬ48LTuH; #[NncsOh9u212Yl̬.(5$+1/@yh+<UhWxseaTFxJؼ{!ϽU1d_sLݜ2P#iQE*RNS \nYw5Cx.%k(2'ֳ{Z<%=DyHHm|:LCzasXfJ^!%uL=䑴yBH'\?q|߮"v40j@pG2AXh( Stuc/5Y)G U;GM&v&'W381mm1;G=5:LMW ZuI2CmFG-`udn~H=+֝}[=ZQ-"։ ^-H3VM".r8e)i k\j_.۸O'vRou.ZY۾n$I:FFlm89^ ]in4+#+xc,1)7 !3L$i7fr`wX/;V>kp{[׽MyO{~|٫20k!Jx[3T:ل$9}H}>j狂QIJgtm,dhs0{ 삷d'-eGñ"Z4tTa!^#TSn-sS_n SĘ*?Ɏ/t#¦#_1QEy Oht8\Л9+O^IEzY)Ӵ6Ga/rDI~h )61Dt e{S܍ FaNY'':X,(5u?p$jAU@gLɡ}]G)f$PryԬ&Al NwJMI)&& YIubq/}uE@gp) -ȧ}A' yNwMnj 廿FQ WGPGm$^0o?&P4 ܎L_;bx&\GnihlI 'έR=g[JW]U^H*P])l4|Rw\4#~(ZO8r xXt9: .Uetij6k@h֔F[o0SI"M7RD?g>Ÿ#_RPB6T#-ȭQ2` %$b.Ƥ-a16'"DhWfw"23 Q@#T}E:+>6+R #1|fWQU\SKۄ$\W0bv[v?K vtcx e{k$F%l<X(/tLKdĒXo6{ܦAVj 0%tM9EV%6Mۅ֒QZU3U-I4Aq i4A-Ё66[=[H=^ sӅ˩8h "W[ 1?'W|-2a#;f/sHywݿ7I7{H;; '+l偌>v=MhBqX:kFcӈ $I;ZjxgQYM}3SMf R/V6n}#QSƎI)+؊:|Y"-;iSpԑulT|}%<[9Tm[Mحז8o 0lon쓊,'E*C]Kv͹ a4;Cs34;|fͧ:fyK]ަB[ ԚJҌ }p+woAڑ;/UtQmap8ʸjm.R0OÏZNz50.ϛzS]s3{5ڀ0ltn|rTO~os Mk 2Ú gK!*ބɮU} (i8N=u?bxe[#$ESp"b H4: 慳pCxҶw@Al=P͹wmUU7sPu^<9=.WG)hQ(DZ ˂T^u(uarp~gt1 V~'%KQ!u dؘ2 Ka&$_*kzn[*n) 7\U{Tᤲ+VRFƗ8MG$LwVO=NNluqvISli1RWJ mz<.nk #Ʊu9M4(j3ɓh*:`,1-+d,*|p:7 ob/%Yl`-Zˠ T]Ɨs6˃>ڛ>~,Roիp87笝6"h=%a}nE~qʝg޴Ŷ.WŠR@95j<E b  Ṯs6u5,P[bՁ^'ϹbH5Zx.v!cIVj$10qJ`CۏµAeo(5d&͈,e㿑G* ̆g 1c8&NrE5T(W 뻪kE^qK̕יar9JCfKSv cSJݓq87QVᦒzrrvG[29Ե\%M$YkSHdHF[Υv'S2"6=K|sy ޼$1q=]^/KTg烃\ f>imC!7uqX\%B tyquҼ\\6c[8lca^Od;=YT<Ó.R6ZPfb-RH.۱A- qOvxQ ]Ǚ3'0t]Eu{NuN_1 {+L-s֔3qqVy6"$tf v# sG=.MSUe#ӯX=+qYY,MKJ yT_x ifK\B%;}l 0^' D0E!>GF.MM~%45Js a >VUȀNEiۏ45z \d9"WѾBaT@WL79e;M]^X3NP] >&ւf<?ůBC(QAx!>t Yf ̉!V^qO^wZ`Cre?b ~((g~l,Sۖw@>)sIRu{~DHXO5Hz;7ޟuIxW PQ$yHgB#`h|JKԪ $!Dq5#AƺOW8W}omRH˾U(XƘxWrNer=3![yjSf/WMkc.tq QTLg@* ρߞ3+\HZXLA(ٶ{\n#^jog"ggJ5vʞ_u!YE61ǥ 4tx XjmP)>!zXAG2-_x|h_cƅ\vCo3ͨoQne-5dPʬ!LuPݠBsut4Q5iD)wУ~) -B/[^8]q^;~[j wI[ / MO6QBo8j:BID|߽fZ2;sQ`*N4x5mzɂKFuΡ7^jo o5Xl0NnI\ts94ѵƗ`%,BC #o^W0HvbW/fy}H0;RnVA?&/VA|G^ן<ثrKH6Y97KʯD>$mwB M4e,dk4c ϭk߷ .I2ݷIG{rSzQ \淪?01~(Q$tdUƠY)pitބ Z-ưŲҲ+0sN[$ū_hVF3IJ\rlj7/ZeO"{vDY݆@Aj/-e\k7eD3}<#RgE.mVs-huD@AS"%Ȑu;݈iS'tQ?6&zz0S5~h]Gm1rj[KythZ>gZ8|nF(uH"Lh(HQ K҅.6X渠+3Xi?^WAdr/VvstB!Y`k#9>T +z䷙X\gѧqcz+je<iL~F45aJ%.]p\lnwA ƘCT퉬4tXY >z4tOPp&GK,ܕTχm$n&@3 N|?\ D dE5gIVF`•\YH)dCrK6D[R\WY]ʼ41 mUs<:4ƭР#m,oNv^?\p0٫h [ ޾G-F'Emh (T=AU73ZPyMGZi*2ލ̝|{ T}9Q?IF3F&W'Wvx_B/br˯^p4 YScmjzۼѰV~ S:k"M(B"Pm'-40;klw1.ڊñ9[k&@NoI_KV+Kz||މyjKAlM`EB X Cۂ+2Q2)/p,ɓtp6sNJ>P)JSxghUA#~PQ็w_֩OrJXW.[3)keޠ 5D8t4+6G> -]1eO)8gnRR6ZyrE!ErV- &8E+W2P_&3}s^A-ue_dZ;#;>f_4ɶH@# s"KSDُg匨恖Oɥ\iCPNO]<-Qʀ5@CXcq&1ƒ'DRu 6J 0E1 ޏl;wrfxx_z[`m >Z%/O*LjOQ6j?F2kOH# ^|t1@}9k>2І{ۊΫOF/ R %,f`ndKRRۗB+HG{YɔR84 l/s.AO|t1GP6oIZ\yҼ) vgSIr;_^b%93"DUfR,'d endstream endobj 35 0 obj << /Type /FontDescriptor /FontName /CRLBGY+SFSX1728 /Flags 4 /FontBBox [-234 -322 1732 898] /Ascent 707 /CapHeight 689 /Descent -193 /ItalicAngle 0 /StemV 50 /XHeight 450 /CharSet (/H/c/d/e/f/i/l/m/n/o/p/s/t/w) /FontFile 34 0 R >> endobj 36 0 obj << /Length1 725 /Length2 31333 /Length3 0 /Length 31843 /Filter /FlateDecode >> stream xlcfͶ5ZU]l]m۶l۶m[]m{s7֟1#GVFd.RBQ;[ge{FZF:.2#TVل fb P2020B=-̝FT -l,vvFWWW~W':G>LL&S kBLV fbkh` w10H[:PLqFvDo&3uȈ( *D蕅imA9;kv4 2?FC3 [h&akj`OSr )&]e lLBv6.&;cG[ws'P k_ $ Cpp71pGogGL62&.6)7GVFg*^ZZXPRi[#;c [3?z8_iytrߎ0?_@aC ڹ{210h,&Nkhbon&w#e;# Ԗr_ 0}eF-$!sc+{e<7X yw/w$ bM`'t_+|ktAUldAo䩦X2sk$Jy:Vm(uȑ34aMg̓ ސb? 4@:Bezsi"K,/mv_L:iDPF̺WTuFD/%6U۬f=4lk'JVѳD EQ:qeνqgT7ؘ Tz )#eZ l$ӝ'tI<>WJG|-=uG+md|r1e:udzMi_mU fl'|']ne`JH"oϫIZ.i3]u^t=Q:YC*:* ܥ- Q>YK Ghj4#Ry4eɱ^5l:OA_Cb/4t{u80eK0 !LϫT6JZ_tWڝ^z^;kvLz[~{Eh;#8-:}bo<钁QlK$X-%A}Kr:1T U]a%&#3gm'iAHA P)=)I{\*T'ˌl=TEZ$tO⸓FBLbY!aTc h"A:.NMd겗_ :xY3 &WmQo]nց27ak}RPM᭢<ÖFT1^`.o=z]Gd{|^hc bjmDT *k! {cHi_ {{;Msk8ᔤؐd%& NUx.7֥~d-櫳\ֵ1Ϙ=q}5袑e`Fojs7P Ni1H2@[5 q Y 6K~gumXm 5n* lrKRL`6>Zj==HHNk XF68UPrB/(ec/k. ]:ҵTd,Cf^ hjL)Yу;EwִaQ@kxeaSZx`&PG^Ϥh'KUڙ N&zҳ@K؛N/Om˷oթ+E$|!@p$Ks妗A9aj8[Ih]m%gle85c?8QNO0!?]cghaFנ:o5Dp$ZղK\fP&8}Nw0eUOg`^e_H RJ[ShA?}1_7ԝ,%P+Xy;c0Q_ej@K@\_&cc̓hK{ -@DŽŬFfFDd/8cM+}2*%0LO<*K&YS&J9&ɹgdB|x\YLP_= Qd a wJΐ99i,)~^'A2}_@ךBNMqgz,81p_F0nӂJGKζ8`9`SpP~>晪7҈doFW3Џx6S˳w5 U瓦֪mDq^Gd>Fug˻U"/r%Vv 7XOa}FӛMKޖ$a(- v-ͲKCaN#Ms6UyUOْ5Oc811*ՙ_P2=1I|G*WZj.vZ7JGfFA+Hߺ8eo3)4Eo64MTbR%^>jB{h-pʁ݀hݦ@r( ZT!OiB觷, Um@1jBx*ߙO@7V7Ѯk|k/K*|BoMq՞2{v0w)m,EX~AEppcP i+vW۶e^+ #D ZM|?nȡY3䁟JRgMjF+8RvNbńplYܴ蹧x2!tRĬn] mTga-ݩb =S2c\񝩣+`M9)f7举L-s6*'٫Uvվ3e_ LG hUuVL(Xު?3_TXdkAW h WI9'&g:aP,RZcJs-ޛ-7W''@W⺟md;ڄ8.Z}=MM5)٧l2QIoن m𤑵. ovׄDQU! d` k-)FBBu< Pΐ8FgQ:Qh>W1=CA3sA5]RVꀔ2rdM)j_5T>YCb;B!٠Et_r_R:D5 vHO\aU 3L3`Xu*=(Zzh&7.6K?wc(ggj;xK"gfWͼ[͋!{m43jf+63um*/:_ED;.7*=Ǎ@R㱌nlQ6(.*M?"İTUr()ͬn"c[DpE-tr?qФ5$ٵ_Lu^szTF #9:\R-BK]Pj[;3E 2Ss*&̷J\Pf!S7MtPVu^& 0({QE ֆ [I PѮI4AR /a4o(.fťD8 odπc=Z~L&_lyJx +4н"KtxHH4OŸַUqTu5W6OOTnݛ bRS-rQLj{Dlbם%`7 i8Xb2y'̑?3q0h:>U {'EӒP*ޖ>v#2"!!{^Sbr|yFqi?[BZVk*R©;bRwX{RcZy #=L;nd{1BԎMVH],)t$,$'~0> ڬ2 ^(("OB nOREVM@ ҹn9̉iO?G<"b͚!TXJuYF0wAh%lh}I qTLTeq؀z0-9iѻ a ҿLWc>&"{]W%:Ћ99Tvt8)W*Y2rj=NɅ耥mJ܆22(L"Pb dӠ:d㥷2k&G\rO@P"M1BڼcfaEl.`:9<_WԴte(1+컰 S݌ժ+k.5kKq@s4'-|N;Y} a]YpSWEHFDƻzf2v?jCM[p߷<$K}g'"<.Z+>5RjNuFD'd~z+>=˒Qt $7"P](B1Z#Jvʇr Ԡw~|oͪcITP=1U!5dX9;e/bBMw 0 +Ylj[tTW3xx-r`(F%Ó{HݴMԚ͓l+6&[MNe+aHh׿ʇ;ȥ4 PSpy$Xe :c ^W74V)8t:OҎ@"[i s;1K*+iRblZ(Ϝ\gͅp0Z wՏ+yi>|몹/z@+nv?w XnW)GeyFw YT=Y֘;̵!~sa+NfnmXj Q/C9ҭ]bt*F<+wD"9s<+iDtyIof(}7;ͦHI(HJpӑ$H拧w*<x v8c,@X\r 6*,J$)7> 7DDpIk*G ѕ~vì6/qehmNjS ]nuکxD6Q\JI_=l6\ 0&jo "cEij@-Nƿ?'o,{i[_eOa) #}Eg!Z2T$Kqv`\F :cЏDg*=FFZZp1oڗy@k#_ק nNCE{pԚ;V+[0Ce2;Q۪Ez~;5B?t<UB !" CudMy_|=A(jGX̷n|GZ=D0[4H1k$$yrq<@2BX7 ;Dw ' Y{l/s0hiߟn5R|*hn-{;%@i1T*8xgKCY}wNݴfwځFR Ixi{kuhtӗ{ɝ|L[MHku:zZh+=$5 %7 V[LQ́ &ra˖iT@@T2Ռ{qf"e 5N]h_p\V&riJ7K؃ ;r~%s͞3 8=Ӽۨ^W6# AU1#nDqsކ%d4KX~:wx'jSS,)k)GGŏ8C3u*K zC:W:Nq8JGQan^_{hssH+tȣ 8sB>i/yY|L>^PMc#`։/׽6㇋7<뾮wBQL;uiLF qx"U(a2J\|/si{B+;kiA>;ciwlY Ѭ߁At.2q!!G1-.ߊB=N %_IRj_~Zo+uw7KI"V(`fHZ̍B&7>"ZX-Чiꏼs{WؐxF;]>أt!†T,p\;Ɣ"JF`lW]wQ3#qш.Aۋ9Jpryk|#bPZzŋmeؕHhi^`q9<Gի}uKxKb,NaOi uz.\r5Jr30M"ݍ JJ )=O4]ڇ,=WY\"io׆O⋺M12}E ѧţA陠(ZT?ij ljtWF0s YE$@%c$#_K4y7?/V rNw ]<3Cy @яcR#Z=qdV'G?WH,}xr~.Qv=z={uIUh-P,lM 08VY$}|az6:BKw FG`♿b.f976ys9ɭr& =ɟF#+&Ye#̺P;tWn7zT݀$$eD9 rz׻C$o i6150r4'[|L^Tf^&EŲ LBs=p!]@oJE}%onXCz,NV,-ۏs[0@} D^mpŻQ_m4nH#,R?oTj H8ɟ@R |(ODǷD]?_Aߗ{Jyq4q^x$kz8ut#S;FǛ#ޮ?}uPJXs,Ë6dS:\itX<ǾjqxN㛑xGGrm-B1O0flV#' fR^:JCmlwyhPP9c=g f`6͢`jIduhfNF.PGT5NAyӫ|OLpˌhkJ̒8R*;ݥlYL|*0!7\؋vC~)ӷ0HsA4$K61 W{xѳ@|<+ww#HqCI?~.DK8zIv^Lm¥y%[?g}!r#FNݎlpS1zj>pY'y=F d {ة7| NCS2"ݘ,XNWl]ߎaG!%6'MWG3y /UﺤDbRbWȋCBW03\/餜)C+o! a9PsE RWQ-mvЛbI^AjOO芁pthcPTSbYe-,u2ԕQwJ*-6q60%dy.P8Srè|! $ I4IԟQjX7QĐcV]N3 Cْ<ÑK̽/.7. 1`eoOduH܀N+6DKpe/^dlsED\B<-.‘AW;-lomM$ltrR*蜯)O!BY\d id;;5ucBCa=tt+lUhG} p:9fv,O VbB׊+\\v_N\pFPC7o8mC2;%,I;@O*gC~E"\ 2o%1*v8R4zƳ %ЋD_p}"MNHQQ%Nj^w&w-~dݰ,J 7PDzس{t "Iz 2 y~7Q4 w=8%`r\qƭF,8]ndt"`DʳwXm)Π*K?ttDb|Y!ey4sZcT 06=?X ޯ*Q18?nd2P8e=@2J%.cu)>Gt v6;%Cprұȯm"b4TvA< zuh (h$ECBydtYۇ{ 9ku L+`6oxFJОwPUT=m┝G-P X` _$[ &䨉 ;U(Uvh^ ~Xu;xR#䐀!AIwV+rHq:pcfY`wRU b|z+W< FL95[/<cҐPNMVŮ2!9D_H_bPx tT[<3u'Q"L9;!ACo0/ET41` RmK1SOtfT>ҍ]la5;'oap\ Od{HEF0xXaQi8ӚV;jS&`%2u#H\<#]H&b,_AMPoU{EkH%k^[C=K}Ь!q%Aڪ{Xu=_Uס3>䝑Zx8)J٨D9hmoJ||871-X D)[nMvg ʑx~YS͟ @6_|$X K9p*[8㐗b%M0m2 #?TQaU7BNKG"c8}5ᡕ nb;%6Ez-pO*R֣ðbٟ oH߆G1B䲯P"x$,aP+<@;Kgp\/@UzD]7$@zЙ͕sKCÁ:3IP'悢fA58"3mSDY'GWz/s "9bKԤYT .rwsl 2ʪ8w!_(F{I\~[^(Rڏmz׌ rxN*4z}Kwئ݁U2̈́Ep G֖Z: n(c 3/qP^<;Y(WH+tDcQpY0Xp%m/T J+TȀ[nylR.<0&,C!S(0/nvylfzLy%4:wb5X[.E])RѰRD[eiHXTN:qALL1'v o*bd+X*p˵ٟ(=zh1k;5VΧֹ1-OY>W/"FE J.m6gӅ{`=kVzE&uLALP;&1E_Բz[l%6f:ocPAx\.϶0Q:A.ή< ?Z mxu[#6Y  @E yCu CH=JhF<؞9 z|_WhqM5CܽR2Y]۸K!ͪ)* 9Sj3 :*Н4n|W*A,={¡o<{ZSsb`D%/< a,V40`C?ʺUb2Yv4*};^ƊœDguT&ƏKjVo0`V5&P(cQڦۣN&N:~nJq' =18aɉՐl`qT?09T 7sjeWu̻-D4 W*EY)Wu!W8g@j:ڌnM~KW ްڙpitWm3yeqHdnB鮭"o  m04+*Nlk ErGa\a]%?Xn寸5I!/#3Via*\sύ N@ 5NЕd9(| 3Pw2ǰ6:WUfzOy9C\>Ƅ' |p0վR*5=֐}Ơljx"Ysm5چk`cТT {8^զN%qYj Ŋΰ/ 6Lv &7Z"\L[B 5-Q@7;Ufc_~Խ?Mw0&ʪ6=F:WYKdKui.^6%: 84dn4mAϷT?J"PȪ3 Xmh$(1lǣ~ PHt}Ʀ-9&A*.Bĺpۜd3Fj52؁+(H~'sm!DPI8$BpaGE 6_Jճw(mX,u6q/QBVqI>Df\hi%:e8dˋT϶K.jgmcȞ#i?sZx-f\QHHM a*#~CÛ0AQvsYFSz_x`/Qs'w2K/nz$Fxuvo]IYfϏmANeNֲ .dJJ5 o a$؊^V혹ɷSa]H2& '5j.K%"=/Fp"HTwmOZoOYQ'~+wV>i}NV 2uQe~y|>;6S%/IBą4uC%ȼO'@n:CZy*: Hh 68~SW[7t uRgi:D$MM_H [5M"=ڟeḞlpTTQ])]%i!b2\H7h\Z..5O7=M}<1yٷ%iC&h"FI Eʌ;<^+9f:˘~cpUntU-U6O!+L]%R<pod.&pu>qij,=ui(9X껃Lȯ\f5Y{)g5@. Q8ڴ-ԃXх-YL 6k1|~,l4(|M[~smR}l?.?1mKµoLQGgcLpޯ/CItxٗ rVwod>{n4}UOF3`]xmAs:m*O#_o+&*XYhҨ\B>zWcs 3=pRzb?2^O- y;ژ.}YODW8PsG}ipa Osa;5x`U#ce6J 4KKPGcY? n 47_dſ_]xJ8nQ. uch\,6h2rEiφ(̯Ͳ"^ dDw=#t'~iTpa{{7IJ D4s?U5ƪNAt)2{`4#MO`*f2Ѧ{Dii,| -'Ϸ\km w5#{1q@9E&᎞eVXlY 9C ʃi@9{g69żӖݰ@Z7\P;q-iϜD=iI]:mzh0X-n(܁okeYєa{ -{+izYcu[5Vӏή| E(| fw6 U RSiA(NI3\m J%rɦQTAl)]F (UxT]Bu؃@e(o 8`Ldh,7YZoYل)ٲY#k2xm[AwQG{/1G6,Eqa#TQ ;{s]i gyBԺAⳊ2{J R~V>yO̺m G=aYbdcjc^8 /;^E, cktϲ[ vIa>evM ȁ,bje(}˹d5ehk]_5Xg_92S(E(TCj0Æy9'V a+K3%h !~|w%%P*`\v }Q{%=QN 9҈˴) 0fi6wQɽ0BnjI+/o'=P(qzWt)qB[IۆWkxxY1h2F>hVXwP$U(ʂ"Of.Ay p+eaʫ Mj46iiK!iu2/ .&?k 7t!.31Yz(OÆO5V)B|0f:C/xkf{`q5S Q07 g0-+T FY,;uˆy!~5p ۪URwwB5٬ƽgfЄxx=xWǷ^"Գ3jL5fL#6.9,o | JGFKC2_/2LH11M%٤Ce.V@D:<3֗W&P ?f^}W-rq:Ra.:,SvV*J+" o) &Z䅾T]:Z1  ^3VSqlLC~<`NU̧?+v}<ڏ\''ٌNԉc$ cZߕb@Mm \ߛ*Ԗ12lyW`6@z6wh,_b+*Ic̲XK @fVP#{J䬎/dj,'< 4hD*k6?i)NAE`#+hm[?;?{B\PmO%qUόK$&2Ҿ?i>㌇>. XB=ibq͟頍diԉ:L bqoB8gdǺn]!I<.$'2ڹ$ xD2yceon~\_}<רC&ØV?m bL¯1D3DQCUkDr;"}= 8_i7`APT6YT)Z]Y*{ZJtzoaS&O Px"١VͿEHԫ˥ -3ۘ:Q=٩4 6KȊuZ#,K9"#z ,* -%wcIXs7;kDy[$RGDEov!HUMczx m% S!dhbQ3AzdcxrLR1" i:}É=4H'ڱYSO( to& nTXÅ(t!٢ WЅ&ʫ=NPE}ʕ<:h/DQΠdDw/ B˝dL$CLkmbij+^c=b8ۓ8I.f~Ks[5/@^m<6S|hlF䇜3Y{msf!P$Ҵ_+f'5ذ(|䜘GY*̄52xnS53nPKKPgb/rخW=Cp~ֳpY~pNUՍr>IcL6b lpn8zpd\.{ 1]H6eTLmIeYѱx7R O4Ji&}w5uWGR J?ܔql`ү :ZBWBUU]Q e1ҭ1R g VDJ{YS=s8aԣ ;C!-I|oeBoMnTWErk$4!s cG.S; f BZ7A6/9dn.5&slrWLVz%=ʍ; FTa˳?k-:|W^ ˑn2py]w"trCB7wfo2[-:\_N=8[ݘӺk~%Yi窝t~"Kxɢ:(r]70WAl"8A mڔvA[|;fwmLG崭,Q:Hb[]6q 9"A֊NL h`N3A͒U)gY^2%B)}p~<.@wi8#U/Wz^;_"]4Gg`'oo;j"~_`gi4\}RM` jc(]Nl/hn@,rq\:~Ta=ZƣAwLg2.~NJTOV]NOΖH/Q[hZ^CFW~H/+H| 'eIXOCM15$|a.9e~`"OBX6Hd6|G ;?˟͔NGFCbJk.QrZG$:.\le"~ynRgPӅKkԸVzC16-'63)3**EovH呋f;bVKnXhϊ["?rLa[ 4(V P#id\Eap)pU`gu=h+˥ 2=ܔ2f40O*ˆȸsUd򪤨N8O[1s!Q;ޝaމt鍭Я.1qɛS#HYZ?f$&A%&6B,n2%zڙd8=u]jPE-h94m^-Ǽe&o]Z2i$VK8!Qk|dAAwPX:\20a2 ͑ց%5wciDq:=s|&%lPB/.,G89NmmG}[ _qR&>pm W:}isVfGf(үpR\oe.}:n`x8H c 8кJoUmͱ_W8)eXM̰n#}w u1Wk^RguD"{ 5G¯-5?< G@m;Ebct2%̞DNc;+{rq?Ihe]ñ]aeLD ׷1XœWo򓖪v45цC~$uCbf.K9 DȪD"}e'G$rfbW}.' r; ڰM{._}dGuKfQpЬ LH+v) M)~\BRVehs"# -+GTC.My)ʥtY~3!ԞҜwzu+]ʊ_ǭ` r ꂽ Xduf%PO<Ǐswۨ4=w O.g^"V4|NxH (*Vy?0/jkcK?c> Y:lj7pu?! A18?V>~(l>X&juE;gK!+h}тnS3nĄ|`T!.5 SK)򠉄ÄUdg"::y */fRgN[ui̽`iRlGUxڦ2^nZ?T.]=)u?mz~5"`^K !"ty@~vhf,Z<o0_u"q NmU`F{j'ɶ4%h<@\!8zb#Bw݇Q^ PC nr t+_9h)rzm@]Y ͵P".u8H< {#ʂkCYG=ㅌjԝCts7x̂B: L*NPE7.hΜ5O1tϜ%_Хk)ҹpy]'}ňNRd3߳8[._ʦE~ՙt'UCa 7Ick[vݑoÚU^99sUN.|/W9R6[K< 'Q'1͔S-24hA ȝOIZv.Y#ބjx5IQYo!0Z7J;[W(lD|0ejZ[H |O{X b9Pɳ`ζeʵj9uU?`6 H [AX?83].9a+&u+ CgfMA8TĄ(g@ s7cz;P$%%r4wZ AD 6)l*ݣ?R_3 Gr2CRY嘌Ƥ& 33tx jN~>?-ST-DT,T?IռlAάP8gHM|. U6xH~4(^#{[)DWC}w^~0U+zi^:q#`JFמW1kv)} ~j E #zc"vMS A,<(Td!P}SЫA0 ә#VHB dԙE#A Y<~ }۠} 3$Ub &yeq6q/:.+Ǹqf**QtA: r;5s Fh`E)UFl]!`9b Pjk^2\mTr"x4Q[l1RKؔh}2Q(!qKv)\E3BM޶Fqi,f͢$L ؔ}vg:"h,` HPғ_IMr+'%. nfĀKjFX}Xv6l~RrOIOX)=6h:o h(8-t$!{&ǿpuq\"m6ղl_d~ԲFW- "!s b|t{A-FJP] JVr m 2Zci (2)R9G_J5J'Lrj6̠W7bI։(y1ÀޘY ^Eұs>&GlȢOf,B3d1aa!߬iʡx}Sj6U2Z0w3[;nh*Sl!\{  mPPo׽n5psz#DcpC)+Wld%ƥTurNG1u{*vwMtT0Jɰ"Y)]1;5L>z svL`Ÿ55q0dTXo@fF/YF娕s 7( f x錴񨭟`[N}@yjca$ Uyk /x we Ns 2O0iSd5m-\C9]bt%iY;Qɀa QUy7IϽ_pA-B/1hhSGlʎ tZbQV=AdM{0GF˧iZ U0|zIIcx!)7) 9/#b.jrɉk2"Q 0Gznҷ] xp횘+Aׄ#rB:Y:gU"I^6[Y&2%S`y2RKfً^Am%sM6wOOѣV*ů6R t.q"t~e@QM:5y2u~ʵJͣ]omO{REmOIDu^;#gSH9Qw}n4tUFxVM\@9̱5Ҷﴕl$Xr== ]b7*Hj_wDT c$T1<(㑩P>XwwޯV bĜ a0qׂ tNe*d( sg^C`wRȫ6z,yc*k+ 6TV%J2--mu(/7WҝoM۵uacЩ2$E.|n-FugG~ Q& 7ԣA{X3Vq_ S)/Vtx'|1ho 'ވWy]=mFo]S}.4.{ @i/,5l& gT'&qv|tuN)O2OMVd/Ӻd'5׃-oNԅ0JxBptEAwH ьbH nVL=$DkqUZR~P Yprgq=`_g]jrjRS,z2`8(2_JT,`|cBm%#п]ۯ'=mMTI(I~SEϥK`:7OZJf ZOI(oPTw(z!\m4ay1TFdvm+X^&~SP287WTvu4 dzn1wH) "*HM1/vىL1}K!zsFS5 iMO@gl}3 u &yqM^vwzc rc%]G)n ]1T:GwghOT2pyr#0l)bM-*Ak2t2ZiٯCYjN ]2X "#C>G ^"$I?m++!G1qz/`a4[MU( ŨnQu7j,ʂp;vr`M?mm#=  +l%nCM*ꗯ{&fTv44+${s+sJF]@pe 6c|/H$'J`cQ9Zg XY.sdo?:p&\͛bL?Fjpf )plW`*6αC4ѭ=FIa>ħkI{L)jc{QqHǰ2Y1|ɂ3 L( roO#ús5UjIT)Pb@8<%YJvݴ56-f jQ{< kɸ a9m2g 1z!fp?&]1v{Q񮭍-2,$b g*8[zIƼ!{hԼ/N ˾4{!nz]c8Q65isUTDpу7YR޲lşwf~ڦF8n!&桜_รo3Gŷ=:K0 =oز)s)D۠D`rFx!;e8drgM:r<$M(;!,evG3oÒBaUi}͚#6E>3{ 5Gr2)w//+3'qZG'hʕ}?ᤛ`.vt>E^h~!ɞ=: 80h_e('WҋoV3R g.`YYNo=bfи.30~#Kb=h/Zn!3qFKs0htfynQyȵN\xyʬR8Rirب0 TeD qlf nhO^/Z1(G?IŝM&Olm6ܥPRՌԝ65׌FBP*ieCN^val۳itAz- b}8W&O|Ŕ{ Mhf'|]I"ֆ؋Fbdt!SҵagG;*I gAd&O.ѐ >V)_*oT53&ْ6u(`^>7K'Rp.8Y40'FYqg5t\Wm:{A .m&f9!7G6|qȅa&SQ-wy7'{;*T}_rõ4eѝ[5۶8¯V|r؁Iʨq[PDZ{(Iq餽]#ⴾ6oj\Flbc@bDrh OKPj dBUsi*؉EEv%^s5]0kT2mw,N(gYHD+ԞDSC3pphn}4a%!rb .Q TҪѢh(7e룑-S= l.bKyJuv[dézD0Z0Nի0OMX\A0v8,j9S {i4KD:nɶ 2:mfKzdh7%ĩ HTtCzJӠ}NKM yOIds\Ia)9 LWS~LqRbt>+DjPD3[,_*+>V0dT>.# 8Nn8kNiC`Tc@ǭ1,K M@SS T G^&|:pծU>Qqai2@` uJ)e;ܺ~?M!4HkmH Gb'Q.FͷjQ眮8CB]lKǢ]ѼFG/ׂJ%$aܐ.rUUCRwU.ޢAӉ;D'\ޖ"ў)L MS{4lz!>M$*V\,*r.KQ.@xEz3FD,=,Qx_c#b \YOY9!Fv*p~1PE<:-w_*e / Ol"!Bް el}0 `UcֱF_`%0Uɇ{f#&G[CVf_zo4u\{hmYhŘn=/`kEFُր/̎lhڨiӃ³- eY Iu@&#[ 邞te"gBmMD)z4gjfXX, Գ*ăþ,P ĽYӔ+9-/C>Y W۟*_ӭ\:SW3Uz@kljzu2|ch<@yİ.G+ yTufWqT N|+#ƣI1 9̥Bښ\^uL9gf+#QogŚ5|c<_ j0glƅ8>N`%& 9+OiU;\T{A:zίBfH棯,fgzƋ@l8ve6joIcmْ!-pW-"#9~D\ Rd"eVX^"23]35ԃ0nR[7BW߿"jZGw*3Ye_77.RTaD}aGCB\G9|6!xb^ (L4  xWۆ1Qn <4]㙎*S&COTl8{E%lN"x2|4|p<Ũ #&xVGHPn%b$!w"R|dF<*EAXl\XԽu}8" b;,kOkpO[x}Lixbz9}EU͖ŀ9s к#Û<YVBn,Oc,7~Gy6D+r=G^쑏^X={@664oE/N]"B)qOrh!k\| 3ق+5js0Ys_7 ?VՃ D0Yrs%^Xqt^̐ΞbWt|4K@bbx_J.Rkw8H_:!WSRã 2ejuw' %a#f\I7-ÃsE皶ϋNXHgcn3hUݯV կPXfq3q%˔]B{>s}eYf &l\AwPF~ZYR4!E+%dWqR&x<7u:U-JnJ?sX- /.<幙>97M9K5+.ybٍ!HN m`1]|):ӂZjdmH̭ք{VȜjML?0ݪ Ax%6uIL}v+*Gг;j"!r`J'KDpA 3}sݩH(p w.|u[DXRìA0QKdrws;=:9l(3LL^MbPؕ1rXML?6) Χ ')G^E&z찻ȴ$eUPE}rUHC0؜,%JwwAl1{ whRܴqՠ#R*NעmN- kcnwiO=|AD24 `vJN P+.1';Gu^iѭÇEZoCGQ#1SvͰ# +~  &KfH n[\#9z }v!ks2혈ǝ.l7ϳoڂe,q Mx!V􍹧yS3[ SA=ymHGJ2J\ǕvD@ײe3]hjMao'ׯXNByN' Ԡ]}AjJnE˱ ڀ}F$oс'tu9$9n4VLj:hV] %|sd/؜k]wGi.Zvਭ},qW߱|z\c)MLV'VCmr#[ԓFSl˧J!rfL#vIBNgE:4ӷ;9{VvUj͵87PrKQ[l[:djGMYc-Y@ҕgh Ŀ6x+Ûx"]y000jvyHM@NN]<$kQJ1j@ޫֹvgCU1G+ɄӶQ'Gvyqb~rׄqmڅqyUX} liItP\eǂ-%@|n Th^uH$SN9Z ZA3L~rS25>-H%^: Ѕe0S伹;U6]RAPMOQCР{ ]>8iV-NջELLXgޜQ"Nv{3=BBFإUiW5op9itjaZ~"$'s JM(W @EWt]ɔ6US/ SO /DPe敏y20}ڮ/u2ϸ?nb`]].*,.#x^9= `cwE'-uOc<:筻_dC\]D%=Duy`#L6?N81(R+@/b=஡O*SF@>[L*8+yeWY-!¸c iimHon}cm} zƯUebLjNuz!2㳫ƻ@DUu) #(khk.ܛta'\l {=M_9~pʬ$UlaINQXdYyXpC9D3d lj;6XaTx6V kίza,)NdžT|pZ g9a;^sQSv<(\\qOI39ӄ' T i&FOh:07on\dQEx`d:V1&y"INF2ɠYIPXAsE{-PxZڽoei#rC.sT_u Xv5A#ЃkwuK\^ %q2xl\a;R/Twh5JV۫kj/XZU|⋌^r%}&= ^:79iu% jy 'W6Dgt8Q[Hg]kh.g!;@E?ݴR pG4W!jy0. n'58ܣUz[j]sf'-hG 8(/a*ML-fGx kۏV5dBX@ȫ1;i_J.c1A@q "2蔤Ї~*nc{bۤУi~{s-AD`z=mTj$nmaf?hVKM\1r{P5sihOe{_f|%,PWP#JB\AsG-K޴>sOrM'}="sse P9ug Dѩ I(!^EV.X%iiAH w\y*EmkkA{C}dO4vbb'.iK" z&ؐlm{V|| endstream endobj 37 0 obj << /Type /FontDescriptor /FontName /LLDBJW+SFTT1200 /Flags 4 /FontBBox [-202 -360 1341 829] /Ascent 611 /CapHeight 611 /Descent -222 /ItalicAngle 0 /StemV 50 /XHeight 430 /CharSet (/A/C/F/G/I/L/M/O/S/W/a/b/backslash/c/colon/d/e/equal/f/g/h/hyphen/i/k/l/m/n/o/one/p/period/plus/r/s/six/slash/t/three/two/u/underscore/uni2423/v/w/x/y/zero) /FontFile 36 0 R >> endobj 26 0 obj << /Type /Encoding /Differences [28/fi 32/uni2423/exclam 39/quoteright/parenleft/parenright 43/plus/comma/hyphen/period/slash/zero/one/two/three/four/five/six/seven/eight/nine/colon 61/equal 65/A 67/C/D/E/F/G/H/I 76/L/M/N/O/P 83/S/T/U 87/W 89/Y 92/backslash 95/underscore 97/a/b/c/d/e/f/g/h/i 107/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y] >> endobj 24 0 obj << /Type /Font /Subtype /Type1 /BaseFont /GFAWRG+CMSY10 /FontDescriptor 31 0 R /FirstChar 15 /LastChar 15 /Widths 25 0 R >> endobj 12 0 obj << /Type /Font /Subtype /Type1 /BaseFont /TZVFHZ+SFRM1200 /FontDescriptor 33 0 R /FirstChar 28 /LastChar 121 /Widths 28 0 R /Encoding 26 0 R >> endobj 11 0 obj << /Type /Font /Subtype /Type1 /BaseFont /CRLBGY+SFSX1728 /FontDescriptor 35 0 R /FirstChar 72 /LastChar 119 /Widths 29 0 R /Encoding 26 0 R >> endobj 14 0 obj << /Type /Font /Subtype /Type1 /BaseFont /LLDBJW+SFTT1200 /FontDescriptor 37 0 R /FirstChar 32 /LastChar 121 /Widths 27 0 R /Encoding 26 0 R >> endobj 18 0 obj << /Type /Pages /Count 2 /Kids [5 0 R 20 0 R] >> endobj 38 0 obj << /Names [(Doc-Start) 9 0 R (Item.1) 13 0 R (Item.2) 15 0 R (Item.3) 16 0 R (Item.4) 17 0 R (Item.5) 23 0 R] /Limits [(Doc-Start) (Item.5)] >> endobj 39 0 obj << /Names [(page.1) 8 0 R (page.2) 22 0 R (section*.1) 10 0 R] /Limits [(page.1) (section*.1)] >> endobj 40 0 obj << /Kids [38 0 R 39 0 R] /Limits [(Doc-Start) (section*.1)] >> endobj 41 0 obj << /Dests 40 0 R >> endobj 42 0 obj << /Type /Catalog /Pages 18 0 R /Names 41 0 R /PageMode/UseOutlines/PageLabels<>1<>]>> /OpenAction 4 0 R >> endobj 43 0 obj << /Author(Daniel B. 2011)/Title(pdftoipe on windows)/Subject()/Creator(LaTeX2e and pdfLaTeX)/Producer()/Keywords() /CreationDate (D:20110513103435+02'00') /ModDate (D:20110513103435+02'00') /Trapped /False /PTEX.Fullbanner (This is MiKTeX-pdfTeX 2.9.4052 (1.40.11)) >> endobj xref 0 44 0000000000 65535 f 0000002508 00000 n 0000002528 00000 n 0000002548 00000 n 0000000015 00000 n 0000001847 00000 n 0000002363 00000 n 0000000061 00000 n 0000001959 00000 n 0000002016 00000 n 0000002073 00000 n 0000081163 00000 n 0000081003 00000 n 0000002131 00000 n 0000081323 00000 n 0000002189 00000 n 0000002247 00000 n 0000002305 00000 n 0000081483 00000 n 0000003913 00000 n 0000003680 00000 n 0000002601 00000 n 0000003795 00000 n 0000003854 00000 n 0000080863 00000 n 0000004059 00000 n 0000080516 00000 n 0000004081 00000 n 0000004639 00000 n 0000005208 00000 n 0000005514 00000 n 0000012620 00000 n 0000012845 00000 n 0000033574 00000 n 0000034012 00000 n 0000047933 00000 n 0000048180 00000 n 0000080142 00000 n 0000081548 00000 n 0000081708 00000 n 0000081822 00000 n 0000081901 00000 n 0000081937 00000 n 0000082084 00000 n trailer << /Size 44 /Root 42 0 R /Info 43 0 R /ID [<3BCB36B2CAAA940180E85685610187F7> <3BCB36B2CAAA940180E85685610187F7>] >> startxref 82370 %%EOF ipe-tools-7.2.24.1/pdftoipe/parseargs.cc000066400000000000000000000101251420423641200177650ustar00rootroot00000000000000/* * parseargs.h * * Command line argument parser. * * Copyright 1996-2003 Glyph & Cog, LLC */ //======================================================================== // // Modified under the Poppler project - http://poppler.freedesktop.org // // Poppler project changes to this file are under the GPLv2 or later license // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2008, 2009 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== #include #include #include #include #include #include "parseargs.h" /* #include "goo/gstrtod.h" */ static const ArgDesc *findArg(const ArgDesc *args, char *arg); static bool grabArg(const ArgDesc *arg, int i, int *argc, char *argv[]); bool parseArgs(const ArgDesc *args, int *argc, char *argv[]) { const ArgDesc *arg; int i, j; bool ok; ok = true; i = 1; while (i < *argc) { if (!strcmp(argv[i], "--")) { --*argc; for (j = i; j < *argc; ++j) argv[j] = argv[j+1]; break; } else if ((arg = findArg(args, argv[i]))) { if (!grabArg(arg, i, argc, argv)) ok = false; } else { ++i; } } return ok; } void printUsage(char *program, char *otherArgs, const ArgDesc *args) { const ArgDesc *arg; char *typ; int w, w1; w = 0; for (arg = args; arg->arg; ++arg) { if ((w1 = strlen(arg->arg)) > w) w = w1; } fprintf(stderr, "Usage: %s [options]", program); if (otherArgs) fprintf(stderr, " %s", otherArgs); fprintf(stderr, "\n"); for (arg = args; arg->arg; ++arg) { fprintf(stderr, " %s", arg->arg); w1 = 9 + w - strlen(arg->arg); switch (arg->kind) { case argInt: case argIntDummy: typ = " "; break; case argFP: case argFPDummy: typ = " "; break; case argString: case argStringDummy: typ = " "; break; case argFlag: case argFlagDummy: default: typ = ""; break; } fprintf(stderr, "%-*s", w1, typ); if (arg->usage) fprintf(stderr, ": %s", arg->usage); fprintf(stderr, "\n"); } } static const ArgDesc *findArg(const ArgDesc *args, char *arg) { const ArgDesc *p; for (p = args; p->arg; ++p) { if (p->kind < argFlagDummy && !strcmp(p->arg, arg)) return p; } return NULL; } static bool grabArg(const ArgDesc *arg, int i, int *argc, char *argv[]) { int n; int j; bool ok; ok = true; n = 0; switch (arg->kind) { case argFlag: *(bool *)arg->val = true; n = 1; break; case argInt: if (i + 1 < *argc && isInt(argv[i+1])) { *(int *)arg->val = atoi(argv[i+1]); n = 2; } else { ok = false; n = 1; } break; case argFP: if (i + 1 < *argc && isFP(argv[i+1])) { *(double *)arg->val = atof(argv[i+1]); n = 2; } else { ok = false; n = 1; } break; case argString: if (i + 1 < *argc) { strncpy((char *)arg->val, argv[i+1], arg->size - 1); ((char *)arg->val)[arg->size - 1] = '\0'; n = 2; } else { ok = false; n = 1; } break; default: fprintf(stderr, "Internal error in arg table\n"); n = 1; break; } if (n > 0) { *argc -= n; for (j = i; j < *argc; ++j) argv[j] = argv[j+n]; } return ok; } bool isInt(char *s) { if (*s == '-' || *s == '+') ++s; while (isdigit(*s)) ++s; if (*s) return false; return true; } bool isFP(char *s) { int n; if (*s == '-' || *s == '+') ++s; n = 0; while (isdigit(*s)) { ++s; ++n; } if (*s == '.') ++s; while (isdigit(*s)) { ++s; ++n; } if (n > 0 && (*s == 'e' || *s == 'E')) { ++s; if (*s == '-' || *s == '+') ++s; n = 0; if (!isdigit(*s)) return false; do { ++s; } while (isdigit(*s)); } if (*s) return false; return true; } ipe-tools-7.2.24.1/pdftoipe/parseargs.h000066400000000000000000000040501420423641200176270ustar00rootroot00000000000000/* * parseargs.h * * Command line argument parser. * * Copyright 1996-2003 Glyph & Cog, LLC */ //======================================================================== // // Modified under the Poppler project - http://poppler.freedesktop.org // // All changes made under the Poppler project to this file are licensed // under GPL version 2 or later // // Copyright (C) 2008 Albert Astals Cid // // To see a description of the changes please see the Changelog file that // came with your tarball or type make ChangeLog if you are building from git // //======================================================================== #ifndef PARSEARGS_H #define PARSEARGS_H #ifdef __cplusplus extern "C" { #endif /* * Argument kinds. */ typedef enum { argFlag, /* flag (present / not-present) */ /* [val: bool *] */ argInt, /* integer arg */ /* [val: int *] */ argFP, /* floating point arg */ /* [val: double *] */ argString, /* string arg */ /* [val: char *] */ /* dummy entries -- these show up in the usage listing only; */ /* useful for X args, for example */ argFlagDummy, argIntDummy, argFPDummy, argStringDummy } ArgKind; /* * Argument descriptor. */ typedef struct { char *arg; /* the command line switch */ ArgKind kind; /* kind of arg */ void *val; /* place to store value */ int size; /* for argString: size of string */ char *usage; /* usage string */ } ArgDesc; /* * Parse command line. Removes all args which are found in the arg * descriptor list . Stops parsing if "--" is found (and removes * it). Returns gFalse if there was an error. */ extern bool parseArgs(const ArgDesc *args, int *argc, char *argv[]); /* * Print usage message, based on arg descriptor list. */ extern void printUsage(char *program, char *otherArgs, const ArgDesc *args); /* * Check if a string is a valid integer or floating point number. */ extern bool isInt(char *s); extern bool isFP(char *s); #ifdef __cplusplus } #endif #endif ipe-tools-7.2.24.1/pdftoipe/pdftoipe.1000066400000000000000000000070421420423641200173670ustar00rootroot00000000000000.\" 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) .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. .TH PDFTOIPE 1 "January 3, 2017" .\" 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 pdftoipe \- Convert PDF files into editable Ipe format .SH SYNOPSIS .B pdftoipe { \fIoptions\fP } \fIPDF file\fP [ \fIXML file\fP ] .SH DESCRIPTION \fBpdftoipe\fP converts arbitrary PDF files to Ipe's XML format. Note that \fBpdftoipe\fP is not related to Ipe's use of the PDF file format. PDF files generated by Ipe contain an extra stream with Ipe markup information, which is necessary for Ipe to read the file again. If you wish to convert an Ipe-generated PDF-file to XML format, you should use \fIipetoipe -xml\fP! \fBpdftoipe\fP is meant to allow you to take arbitrary PDF files and make them editable in Ipe. \fBpdftoipe\fP does a pretty good job on drawings, but doesn't handle text very well. Ipe's text model is based on LaTeX, which is just too different from the text found in most PDF files. .TP \fB-notext\fR Ignore all text in the PDF file, convert graphics only .TP \fB-literal\fR Allow Latex markup in text objects. The default is to escape all characters special in Latex. .TP \fB-math\fR Use LaTeX math mode for all text in the PDF file .TP \fB-merge\fR \fIint\fP Set the text merge level, an integer between 0 (the default) and 2. It determines how eagerly \fBpdftoipe\fP tries to combine consecutive text in the PDF document into a single Ipe text object. At level 0, only characters consecutively rendered in PDF are combined. At level 1, more text is combined. At level 2, all text is combined until a path or image is drawn. .TP \fB-unicode\fR \fIint\fP Determine what should be done with non-ASCII characters in text. At level 0, all non-ASCII characters are represented as \fB[U+XXX]\fR. At level 1 (the default), some often used characters (such as bullets) are replaced by Latex equivalents, others are represented as \fB[U+XXX]\fR. At level 2, characters that are not replaced by Latex equivalents are included in UTF-8. At level 3, all characters are included as UTF-8. At level 2 and 3, UTF-8 is set as the input encoding in the Latex preamble of the generated Ipe document. Note that this only concerns characters for which the PDF file provides a mapping to Unicode. Characters from embedded fonts without Unicode mapping (such as symbol fonts) are always represented as \fB[S+XX]\fR. .TP \fB-f\fR \fIint\fP First page to convert .TP \fB-l\fR \fIint\fP Last page to convert .TP \fB-opw\fR \fIstring\fP Owner password for encrypted PDF files .TP \fB-upw\fP \fIstring\fP User password for encrypted PDF files .TP \fB-q\fP Quiet mode (don't print any messages or errors) .SH AUTHOR Otfried Cheong .SH REPORTING BUGS .ad l Please report bugs at .I "https://github.com/otfried/ipe-tools/issues" .SH SEE ALSO .ad l More information about Ipe can be found in .IR "The Ipe Manual" , available online at .I "http://ipe.otfried.org/manual/manual.html" ipe-tools-7.2.24.1/pdftoipe/pdftoipe.cpp000066400000000000000000000112621420423641200200100ustar00rootroot00000000000000// -------------------------------------------------------------------- // Pdftoipe: convert PDF file to editable Ipe XML file // -------------------------------------------------------------------- #include #include #include #include #include #include "goo/GooString.h" #include "goo/gmem.h" #include "Object.h" #include "Stream.h" #include "Array.h" #include "Dict.h" #include "XRef.h" #include "Catalog.h" #include "Page.h" #include "PDFDoc.h" #include "Error.h" #include "GlobalParams.h" #include "parseargs.h" #include "xmloutputdev.h" static int firstPage = 1; static int lastPage = 0; static int mergeLevel = 0; static int unicodeLevel = 1; static char ownerPassword[33] = ""; static char userPassword[33] = ""; static bool quiet = false; static bool printHelp = false; static bool math = false; static bool literal = false; static bool notext = false; static bool noTextSize = false; static ArgDesc argDesc[] = { {"-f", argInt, &firstPage, 0, "first page to convert"}, {"-l", argInt, &lastPage, 0, "last page to convert"}, {"-opw", argString, ownerPassword, sizeof(ownerPassword), "owner password (for encrypted files)"}, {"-upw", argString, userPassword, sizeof(userPassword), "user password (for encrypted files)"}, {"-q", argFlag, &quiet, 0, "don't print any messages or errors"}, {"-math", argFlag, &math, 0, "turn all text objects into math formulas"}, {"-literal", argFlag, &literal, 0, "allow math mode in input text objects"}, {"-notext", argFlag, ¬ext, 0, "discard all text objects"}, {"-notextsize", argFlag, &noTextSize, 0, "ignore size of text objects"}, {"-merge", argInt, &mergeLevel, 0, "how eagerly should consecutive text be merged: 0, 1, or 2 (default 0)"}, {"-unicode", argInt, &unicodeLevel, 0, "how much Unicode should be used: 1, 2, or 3 (default 1)"}, {"-h", argFlag, &printHelp, 0, "print usage information"}, {"-help", argFlag, &printHelp, 0, "print usage information"}, {"--help", argFlag, &printHelp, 0, "print usage information"}, {"-?", argFlag, &printHelp, 0, "print usage information"}, {NULL, argFlag, 0, 0, 0} }; int main(int argc, char *argv[]) { // parse args bool ok = parseArgs(argDesc, &argc, argv); if (!ok || argc < 2 || argc > 3 || printHelp) { fprintf(stderr, "pdftoipe version %s\n", PDFTOIPE_VERSION); printUsage("pdftoipe", " []", argDesc); return 1; } GooString *fileName = new GooString(argv[1]); globalParams = std::make_unique(); if (quiet) globalParams->setErrQuiet(quiet); GooString *ownerPW, *userPW; if (ownerPassword[0]) { ownerPW = new GooString(ownerPassword); } else { ownerPW = 0; } if (userPassword[0]) { userPW = new GooString(userPassword); } else { userPW = 0; } // open PDF file PDFDoc *doc = new PDFDoc(fileName, ownerPW, userPW); delete userPW; delete ownerPW; if (!doc->isOk()) return 1; // construct XML file name std::string xmlFileName; if (argc == 3) { xmlFileName = argv[2]; } else { const char *p = fileName->c_str() + fileName->getLength() - 4; if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) { xmlFileName = std::string(fileName->c_str(), fileName->getLength() - 4); } else { xmlFileName = fileName->c_str(); } xmlFileName += ".ipe"; } // get page range if (firstPage < 1) firstPage = 1; if (lastPage < 1 || lastPage > doc->getNumPages()) lastPage = doc->getNumPages(); // write XML file XmlOutputDev *xmlOut = new XmlOutputDev(xmlFileName, doc->getXRef(), doc->getCatalog(), firstPage, lastPage); // tell output device about text handling xmlOut->setTextHandling(math, notext, literal, mergeLevel, noTextSize, unicodeLevel); int exitCode = 2; if (xmlOut->isOk()) { doc->displayPages(xmlOut, firstPage, lastPage, // double hDPI, double vDPI, int rotate, // bool useMediaBox, bool crop, bool printing, 72.0, 72.0, 0, false, false, false); exitCode = 0; } if (xmlOut->hasUnicode()) { fprintf(stderr, "The document contains Unicode (non-ASCII) text.\n"); if (unicodeLevel <= 1) fprintf(stderr, "Unknown Unicode characters were replaced by [U+XXX].\n"); else fprintf(stderr, "UTF-8 was set as document encoding in the preamble.\n"); } // clean up delete xmlOut; delete doc; return exitCode; } // -------------------------------------------------------------------- ipe-tools-7.2.24.1/pdftoipe/readme.txt000066400000000000000000000124441420423641200174730ustar00rootroot00000000000000 Pdftoipe ======== This is Pdftoipe, a program that tries to read arbitrary PDF files and to generate an XML file readable by Ipe. You can report bugs on the issue tracking system at "https://github.com/otfried/ipe-tools/issues". Before reporting a bug, check that you have the latest version of Pdftoipe, and check the existing reports to see whether your bug has already been reported. Please do not send bug reports directly to me (the first thing I would do with the report is to enter it into the tracking system). Suggestions for features, or random comments on Pdftoipe can be sent to the Ipe discussion mailing list at . If you have problems installing or using Pdftoipe, the Ipe discussion mailing list would also be the best place to ask. You can send suggestions or comments directly to me by Email, but you should then not expect a reply. I cannot dedicate much time to Ipe, and the little time I have I prefer to put into development. I'm much more likely to get involved in a discussion of desirable features on the mailing list, where anyone interested can participate than by direct Email. Otfried Cheong School of Computing KAIST Daejeon, South Korea Email: ipe@otfried.org Ipe webpage: http://ipe.otfried.org -------------------------------------------------------------------- Pdftoipe 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. -------------------------------------------------------------------- Compiling ========= You need the Poppler library (http://poppler.freedesktop.org) v0.86.0 or greater. On Debian/Ubuntu, install the packages 'libpoppler-dev' and 'libpoppler-private-dev'. In source directory, say make This will create the single executable "pdftoipe". Copy it to wherever you like. You may also install the man page "pdftoipe.1". If there are compilation errors, you most likely have a different poppler version. Poppler has changed dramatically during the last releases, as the developers are updating the code to use modern C++. If your poppler version is <= v0.68, then you can check out branch "old-poppler" from the "ipe-tools" repository. If you have a version between v0.69 and v0.85 and cannot upgrade poppler easily, you need to look in the release history below and checkout the right release of the "ipe-tools" repository. -------------------------------------------------------------------- Changes ======= * 2021/09/08 (v7.2.24.1) Add -notextsize flag. * 2020/09/09 (v7.2.20.1) Compatibility with poppler 0.86.0 (#47). * 2019/12/10 (v7.2.13.1) Compilation with poppler 0.83.0 (#42). * 2019/01/08 More changes to compile with poppler 0.73.0. * 2018/12/07 Changes to compile with poppler 0.72.0. GString is now based on std::string and may be getting deprecated soon so get rid of some uses. * 2018/11/01 Poppler keeps changing: gBool -> bool (issue #31). * 2018/10/23 Const changes to compile with poppler 0.70 (issue #30). * 2018/09/24 Removed Object::memCheck call to compile with poppler 0.69 (bug #29). * 2017/09/19 Fixed formatting of color values from %g to %f. * 2017/09/05 Added -std=c++11 flag to Makefile for compiling with new poppler version. * 2014/03/03 Applied patch from bug #138 to fix compilation on newer poppler versions, as well as fix missing dollar signs for some greek letters. * 2013/01/24 Applied patches from bugs #88 and #112 to fix compilation on newer poppler versions. * 2011/05/17 Built Windows binary and included instructions by Daniel Beckmann for compiling on Windows in the source download. * 2011/01/16 Re-released to clarify that pdftoipe uses GPL V2, compatible with the poppler license. * 2009/10/14 Changed to use libpoppler instead of using Xpdf's code directly. Generate Ipe 7 format. * 2007/05/09 Applied patches provided by Philip Johnson (bug #160) to support latex markup in text objects, and to handle text transformations. Improved text transformations, and added -merge option to better control separation/merging of text. * 2005/11/14 Generating header correct for Ipe 6.0 preview 25. * 2005/09/17 Fixed handling of transformation matrix for text objects. (Text was incorrectly positioned if pages had the /Rotate flag on.) Added -cyberbit option to automatically insert style sheet for using the Cyberbit font (but of course it has to be installed properly to be used from Pdflatex). Removed silly dependency on Qt. Added conversion of some Unicode characters to Latex macros. * 2003/06/30 Added recognition of Unicode text (results in a message to the user) and escaping of the special Latex characters. Fixed generation of incorrect XML files (unterminated objects). * 2003/06/18 Added -notext option to completely ignore all text in PDF file. Added man page. * 2003/06/13 Packaged pdftoipe separately from Ipe. * 2003/06/04 Fixed handling of transformation matrix in Pdftoipe. Added option -math to pdftoipe. With this option, all text objects are turned into math formulas. -------------------------------------------------------------------- ipe-tools-7.2.24.1/pdftoipe/xmloutputdev.cpp000066400000000000000000000367551420423641200207740ustar00rootroot00000000000000// -------------------------------------------------------------------- // Output device writing XML stream // -------------------------------------------------------------------- #include #include #include #include #include "Object.h" #include "Error.h" #include "Gfx.h" #include "GfxState.h" #include "GfxFont.h" #include "Catalog.h" #include "Page.h" #include "Stream.h" #include "xmloutputdev.h" #include #include //------------------------------------------------------------------------ // XmlOutputDev //------------------------------------------------------------------------ XmlOutputDev::XmlOutputDev(const std::string& fileName, XRef *xrefA, Catalog *catalog, int firstPage, int lastPage) { FILE *f; if (!(f = fopen(fileName.c_str(), "wb"))) { fprintf(stderr, "Couldn't open output file '%s'\n", fileName.c_str()); ok = false; return; } outputStream = f; // initialize ok = true; xref = xrefA; inText = false; iUnicode = false; // set defaults iIsMath = false; iNoText = false; iIsLiteral = false; iMergeLevel = 0; iUnicodeLevel = 1; Page *page = catalog->getPage(firstPage); double wid = page->getMediaWidth(); double ht = page->getMediaHeight(); /* int rot = page->getRotate(); fprintf(stderr, "Page rotation: %d\n", rot); if (rot == 90 || rot == 270) { double t = wid; wid = ht; ht = t; } */ const PDFRectangle *media = page->getMediaBox(); const PDFRectangle *crop = page->getCropBox(); fprintf(stderr, "MediaBox: %g %g %g %g (%g x %g)\n", media->x1, media->x2, media->y1, media->y2, wid, ht); fprintf(stderr, "CropBox: %g %g %g %g\n", crop->x1, crop->x2, crop->y1, crop->y2); writePS("\n"); writePS("\n"); writePSFmt("\n", PDFTOIPE_VERSION); writePS("\n"); writePSFmt("\n", wid, ht, crop->x2 - crop->x1, crop->y2 - crop->y1, crop->x1 - media->x1, crop->y1 - media->y1); writePS("\n"); writePS("18 0 0 18 0 0 e\n"); writePS("\n"); // initialize sequential page number seqPage = 1; } XmlOutputDev::~XmlOutputDev() { if (ok) { finishText(); writePS("\n"); } fclose(outputStream); } // ---------------------------------------------------------- void XmlOutputDev::setTextHandling(bool math, bool notext, bool literal, int mergeLevel, bool noTextSize, int unicodeLevel) { iIsMath = math; iNoText = notext; iIsLiteral = literal; iMergeLevel = mergeLevel; iNoTextSize = noTextSize; iUnicodeLevel = unicodeLevel; if (iUnicodeLevel >= 2) { writePS("\n"); writePS("\\usepackage[utf8]{inputenc}\n"); writePS("\n"); } } // ---------------------------------------------------------- void XmlOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) { writePSFmt("\n", pageNum, seqPage); fprintf(stderr, "Converting page %d (numbered %d)\n", seqPage, pageNum); writePS("\n"); ++seqPage; } void XmlOutputDev::endPage() { finishText(); writePS("\n"); } // -------------------------------------------------------------------- void XmlOutputDev::startDrawingPath() { finishText(); } void XmlOutputDev::stroke(GfxState *state) { startDrawingPath(); GfxRGB rgb; state->getStrokeRGB(&rgb); writeColor("getTransformedLineWidth()); double *dash; double start; int length, i; state->getLineDash(&dash, &length, &start); if (length) { writePS(" dash=\"["); for (i = 0; i < length; ++i) writePSFmt("%g%s", state->transformWidth(dash[i]), (i == length-1) ? "" : " "); writePSFmt("] %g\"", state->transformWidth(start)); } if (state->getLineJoin() > 0) writePSFmt(" join=\"%d\"", state->getLineJoin()); if (state->getLineCap()) writePSFmt(" cap=\"%d\"", state->getLineCap()); writePS(">\n"); doPath(state); writePS("\n"); } void XmlOutputDev::fill(GfxState *state) { startDrawingPath(); GfxRGB rgb; state->getFillRGB(&rgb); writeColor("\n"); doPath(state); writePS("\n"); } void XmlOutputDev::eoFill(GfxState *state) { startDrawingPath(); GfxRGB rgb; state->getFillRGB(&rgb); writeColor("\n"); doPath(state); writePS("\n"); } void XmlOutputDev::doPath(GfxState *state) { const GfxPath *path = state->getPath(); const GfxSubpath *subpath; int n, m, i, j; n = path->getNumSubpaths(); double x, y, x1, y1, x2, y2; for (i = 0; i < n; ++i) { subpath = path->getSubpath(i); m = subpath->getNumPoints(); state->transform(subpath->getX(0), subpath->getY(0), &x, &y); writePSFmt("%g %g m\n", x, y); j = 1; while (j < m) { if (subpath->getCurve(j)) { state->transform(subpath->getX(j), subpath->getY(j), &x, &y); state->transform(subpath->getX(j+1), subpath->getY(j+1), &x1, &y1); state->transform(subpath->getX(j+2), subpath->getY(j+2), &x2, &y2); writePSFmt("%g %g %g %g %g %g c\n", x, y, x1, y1, x2, y2); j += 3; } else { state->transform(subpath->getX(j), subpath->getY(j), &x, &y); writePSFmt("%g %g l\n", x, y); ++j; } } if (subpath->isClosed()) { writePS("h\n"); } } } // -------------------------------------------------------------------- void XmlOutputDev::updateTextPos(GfxState *) { if (iMergeLevel < 2) finishText(); } void XmlOutputDev::updateTextShift(GfxState *, double /*shift*/) { if (iMergeLevel < 1) finishText(); } void XmlOutputDev::drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) { // check for invisible text -- this is used by Acrobat Capture if ((state->getRender() & 3) == 3) return; // get the font if (!state->getFont()) return; if (iNoText) // discard text objects return; startText(state, x - originX, y - originY); if (uLen == 0) { if (code == 0x62) { // this is a hack to handle bullets created by pstricks and should // probably be an option writePS("\\ipesymbol{bullet}{}{}{}"); } else writePSFmt("[S+%02x]", code); } else { for (int i = 0; i < uLen; ++i) writePSUnicode(u[i]); } } void XmlOutputDev::startText(GfxState *state, double x, double y) { if (inText) return; double xt, yt; state->transform(x, y, &xt, &yt); const double *T = state->getTextMat(); const double *C = state->getCTM(); /* fprintf(stderr, "TextMatrix = %g %g %g %g %g %g\n", T[0], T[1], T[2], T[3], T[4], T[5]); fprintf(stderr, "CTM = %g %g %g %g %g %g\n", C[0], C[1], C[2], C[3], C[4], C[5]); */ double M[4]; M[0] = C[0] * T[0] + C[2] * T[1]; M[1] = C[1] * T[0] + C[3] * T[1]; M[2] = C[0] * T[2] + C[2] * T[3]; M[3] = C[1] * T[2] + C[3] * T[3]; GfxRGB rgb; state->getFillRGB(&rgb); writeColor("getFontSize()); writePS("valign=\"baseline\" "); writePSFmt("matrix=\"%g %g %g %g %g %g\">", M[0], M[1], M[2], M[3], xt, yt); if (iIsMath) writePS("$"); inText = true; } void XmlOutputDev::finishText() { if (inText) { if (iIsMath) writePS("$"); writePS("\n"); } inText = false; } // -------------------------------------------------------------------- void XmlOutputDev::drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) { finishText(); ImageStream *imgStr; unsigned char *p; GfxRGB rgb; int x, y; int c; writePSFmt("getCTM(); double tx = mat[0] + mat[2] + mat[4]; double ty = mat[1] + mat[3] + mat[5]; writePSFmt(" rect=\"%g %g %g %g\"", mat[4], mat[5], tx, ty); if (str->getKind() == strDCT && !inlineImg && 3 <= colorMap->getNumPixelComps() && colorMap->getNumPixelComps() <= 4) { // dump JPEG stream std::vector buffer; // initialize stream str = str->getNextStream(); str->reset(); // copy the stream while ((c = str->getChar()) != EOF) buffer.push_back(char(c)); str->close(); if (colorMap->getNumPixelComps() == 3) writePS(" ColorSpace=\"DeviceRGB\""); else writePS(" ColorSpace=\"DeviceCMYK\""); writePS(" BitsPerComponent=\"8\""); writePS(" Filter=\"DCTDecode\""); writePSFmt(" length=\"%d\"", buffer.size()); writePS(">\n"); for (unsigned int i = 0; i < buffer.size(); ++i) writePSFmt("%02x", buffer[i] & 0xff); #if 0 } else if (colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1) { // 1 bit depth -- not implemented in Ipe // initialize stream str->reset(); // copy the stream size = height * ((width + 7) / 8); for (i = 0; i < size; ++i) { writePSFmt("%02x", (str->getChar() ^ 0xff)); } str->close(); #endif } else if (colorMap->getNumPixelComps() == 1) { // write as gray level image writePS(" ColorSpace=\"DeviceGray\""); writePS(" BitsPerComponent=\"8\""); writePS(">\n"); // initialize stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // for each line... for (y = 0; y < height; ++y) { // write the line p = imgStr->getLine(); for (x = 0; x < width; ++x) { GfxGray gray; colorMap->getGray(p, &gray); writePSFmt("%02x", colToByte(gray)); p += colorMap->getNumPixelComps(); } } delete imgStr; } else { // write as RGB image writePS(" ColorSpace=\"DeviceRGB\""); writePS(" BitsPerComponent=\"8\""); writePS(">\n"); // initialize stream imgStr = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); imgStr->reset(); // for each line... for (y = 0; y < height; ++y) { // write the line p = imgStr->getLine(); for (x = 0; x < width; ++x) { colorMap->getRGB(p, &rgb); writePSFmt("%02x%02x%02x", colToByte(rgb.r), colToByte(rgb.g), colToByte(rgb.b)); p += colorMap->getNumPixelComps(); } } delete imgStr; } writePS("\n\n"); } // -------------------------------------------------------------------- struct UnicodeToLatex { int iUnicode; const char *iLatex; }; static const UnicodeToLatex unicode2latex[] = { // { 0xed, "{\\'\\i}" }, // -------------------------------------------------------------------- { 0xb1, "$\\pm$" }, { 0x391, "$\\Alpha$" }, { 0x392, "$\\Beta$" }, { 0x393, "$\\Gamma$" }, { 0x394, "$\\Delta$" }, { 0x395, "$\\Epsilon$" }, { 0x396, "$\\Zeta$" }, { 0x397, "$\\Eta$" }, { 0x398, "$\\Theta$" }, { 0x399, "$\\Iota$" }, { 0x39a, "$\\Kappa$" }, { 0x39b, "$\\Lambda$" }, { 0x39c, "$\\Mu$" }, { 0x39e, "$\\Nu$" }, { 0x39e, "$\\Xi$" }, { 0x39f, "$\\Omicron$" }, { 0x3a0, "$\\Pi$" }, { 0x3a1, "$\\Rho$" }, { 0x3a3, "$\\Sigma$" }, // sometimes \\sum would be better { 0x3a4, "$\\Tau$" }, { 0x3a5, "$\\Upsilon$" }, { 0x3a6, "$\\Phi$" }, { 0x3a7, "$\\Chi$" }, { 0x3a8, "$\\Psi$" }, { 0x3a9, "$\\Omega$" }, // -------------------------------------------------------------------- { 0x3b1, "$\\alpha$" }, { 0x3b2, "$\\beta$" }, { 0x3b3, "$\\gamma$" }, { 0x3b4, "$\\delta$" }, { 0x3b5, "$\\varepsilon$" }, { 0x3b6, "$\\zeta$" }, { 0x3b7, "$\\eta$" }, { 0x3b8, "$\\theta$" }, { 0x3b9, "$\\iota$" }, { 0x3ba, "$\\kappa$" }, { 0x3bb, "$\\lambda$" }, { 0x3bc, "$\\mu$" }, { 0x3be, "$\\nu$" }, { 0x3be, "$\\xi$" }, { 0x3bf, "$\\omicron$" }, { 0x3c0, "$\\pi$" }, { 0x3c1, "$\\rho$" }, { 0x3c3, "$\\sigma$" }, { 0x3c4, "$\\tau$" }, { 0x3c5, "$\\upsilon$" }, { 0x3c6, "$\\phi$" }, { 0x3c7, "$\\chi$" }, { 0x3c8, "$\\psi$" }, { 0x3c9, "$\\omega$" }, // -------------------------------------------------------------------- { 0x2013, "-" }, { 0x2019, "'" }, { 0x2022, "$\\bullet$" }, { 0x2026, "$\\cdots$" }, { 0x2190, "$\\leftarrow$" }, { 0x21d2, "$\\Rightarrow$" }, { 0x2208, "$\\in$" }, { 0x2209, "$\\not\\in$" }, { 0x2211, "$\\sum$" }, { 0x2212, "-" }, { 0x221e, "$\\infty$" }, { 0x222a, "$\\cup$" }, { 0x2260, "$\\neq$" }, { 0x2264, "$\\leq$" }, { 0x2265, "$\\geq$" }, { 0x22c5, "$\\cdot$" }, { 0x2286, "$\\subseteq$" }, { 0x25aa, "$\\diamondsuit$" }, // -------------------------------------------------------------------- // ligatures { 0xfb00, "ff" }, { 0xfb01, "fi" }, { 0xfb02, "fl" }, { 0xfb03, "ffi" }, { 0xfb04, "ffl" }, { 0xfb06, "st" }, // -------------------------------------------------------------------- }; #define UNICODE2LATEX_LEN (sizeof(unicode2latex) / sizeof(UnicodeToLatex)) void XmlOutputDev::writePSUnicode(int ch) { if (iIsLiteral && ch == '\\') { writePSChar(ch); return; } if (!iIsLiteral) { if (ch == '&' || ch == '$' || ch == '#' || ch == '%' || ch == '_' || ch == '{' || ch == '}') { writePS("\\"); writePSChar(ch); return; } if (ch == '<') { writePS("$<$"); return; } if (ch == '>') { writePS("$>$"); return; } if (ch == '^') { writePS("\\^{}"); return; } if (ch == '~') { writePS("\\~{}"); return; } if (ch == '\\') { writePS("$\\setminus$"); return; } } // replace some common Unicode characters if (1 <= iUnicodeLevel && iUnicodeLevel <= 2) { for (int i = 0; i < UNICODE2LATEX_LEN; ++i) { if (ch == unicode2latex[i].iUnicode) { writePS(unicode2latex[i].iLatex); return; } } } writePSChar(ch); } void XmlOutputDev::writePSChar(int code) { if (code == '<') writePS("<"); else if (code == '>') writePS(">"); else if (code == '&') writePS("&"); else if (code < 0x80) writePSFmt("%c", code); else { iUnicode = true; if (iUnicodeLevel < 2) { writePSFmt("[U+%x]", code); fprintf(stderr, "Unknown Unicode character U+%x on page %d\n", code, seqPage); } else { if (code < 0x800) { writePSFmt("%c%c", (((code & 0x7c0) >> 6) | 0xc0), ((code & 0x03f) | 0x80)); } else { // Do we never need to write UCS larger than 0x10000? writePSFmt("%c%c%c", (((code & 0x0f000) >> 12) | 0xe0), (((code & 0xfc0) >> 6) | 0x80), ((code & 0x03f) | 0x80)); } } } } void XmlOutputDev::writeColor(const char *prefix, const GfxRGB &rgb, const char *suffix) { if (prefix) writePS(prefix); writePSFmt("\"%f %f %f\"", colToDbl(rgb.r), colToDbl(rgb.g), colToDbl(rgb.b)); if (suffix) writePS(suffix); } void XmlOutputDev::writePS(const char *s) { fwrite(s, 1, strlen(s), outputStream); } void XmlOutputDev::writePSFmt(const char *fmt, ...) { va_list args; char buf[512]; va_start(args, fmt); vsprintf(buf, fmt, args); va_end(args); fwrite(buf, 1, strlen(buf), outputStream); } // -------------------------------------------------------------------- ipe-tools-7.2.24.1/pdftoipe/xmloutputdev.h000066400000000000000000000067031420423641200204270ustar00rootroot00000000000000// -*- C++ -*- // -------------------------------------------------------------------- // XmlOutputDev.h // -------------------------------------------------------------------- #ifndef XMLOUTPUTDEV_H #define XMLOUTPUTDEV_H #include #include "Object.h" #include "OutputDev.h" #include "GfxState.h" class GfxPath; class GfxFont; #define PDFTOIPE_VERSION "2021/09/08" class XmlOutputDev : public OutputDev { public: // Open an XML output file, and write the prolog. XmlOutputDev(const std::string& fileName, XRef *xrefA, Catalog *catalog, int firstPage, int lastPage); // Destructor -- writes the trailer and closes the file. virtual ~XmlOutputDev(); // Check if file was successfully created. bool isOk() { return ok; } bool hasUnicode() const { return iUnicode; } void setTextHandling(bool math, bool notext, bool literal, int mergeLevel, bool noTextSize, int unicodeLevel); //---- get info about output device // Does this device use upside-down coordinates? // (Upside-down means (0,0) is the top left corner of the page.) virtual bool upsideDown() override { return false; } // Does this device use drawChar() or drawString()? virtual bool useDrawChar() override { return true; } // Does this device use beginType3Char/endType3Char? Otherwise, // text in Type 3 fonts will be drawn with drawChar/drawString. virtual bool interpretType3Chars() override { return false; } //----- initialization and control // Start a page. virtual void startPage(int pageNum, GfxState *state, XRef *xrefA) override; // End a page. virtual void endPage() override; //----- update graphics state virtual void updateTextPos(GfxState *state) override; virtual void updateTextShift(GfxState *state, double shift) override; //----- path painting virtual void stroke(GfxState *state) override; virtual void fill(GfxState *state) override; virtual void eoFill(GfxState *state) override; //----- text drawing virtual void drawChar(GfxState *state, double x, double y, double dx, double dy, double originX, double originY, CharCode code, int nBytes, const Unicode *u, int uLen) override; //----- image drawing virtual void drawImage(GfxState *state, Object *ref, Stream *str, int width, int height, GfxImageColorMap *colorMap, bool interpolate, const int *maskColors, bool inlineImg) override; protected: void startDrawingPath(); void startText(GfxState *state, double x, double y); void finishText(); void writePSUnicode(int ch); void doPath(GfxState *state); void writePSChar(int code); void writePS(const char *s); void writePSFmt(const char *fmt, ...); void writeColor(const char *prefix, const GfxRGB &rgb, const char *suffix); protected: FILE *outputStream; int seqPage; // current sequential page number XRef *xref; // the xref table for this PDF file bool ok; // set up ok? bool iUnicode; // has a Unicode character been used? bool iIsLiteral; // take latex in text literally bool iIsMath; // make text objects math formulas bool iNoText; // discard text objects bool inText; // inside a text object bool iNoTextSize; // all text objects at normal size int iMergeLevel; // text merge level int iUnicodeLevel; // unicode handling }; // -------------------------------------------------------------------- #endif ipe-tools-7.2.24.1/poweripe/000077500000000000000000000000001420423641200155105ustar00rootroot00000000000000ipe-tools-7.2.24.1/poweripe/demo-presentation.ipe000066400000000000000000000232051420423641200216460ustar00rootroot00000000000000 0 0 m -1 0.333 l -1 -0.333 l h 0 0 m -1 0.333 l -1 -0.333 l h 0.6 0 0 0.6 0 0 e 0.4 0 0 0.4 0 0 e 0.6 0 0 0.6 0 0 e 0.5 0 0 0.5 0 0 e 0.6 0 0 0.6 0 0 e 0.4 0 0 0.4 0 0 e -0.6 -0.6 m 0.6 -0.6 l 0.6 0.6 l -0.6 0.6 l h -0.4 -0.4 m 0.4 -0.4 l 0.4 0.4 l -0.4 0.4 l h -0.6 -0.6 m 0.6 -0.6 l 0.6 0.6 l -0.6 0.6 l h -0.5 -0.5 m 0.5 -0.5 l 0.5 0.5 l -0.5 0.5 l h -0.6 -0.6 m 0.6 -0.6 l 0.6 0.6 l -0.6 0.6 l h -0.4 -0.4 m 0.4 -0.4 l 0.4 0.4 l -0.4 0.4 l h -0.43 -0.57 m 0.57 0.43 l 0.43 0.57 l -0.57 -0.43 l h -0.43 0.57 m 0.57 -0.43 l 0.43 -0.57 l -0.57 0.43 l h 0 0 m -1 0.333 l -1 -0.333 l h 0 0 m -1 0.333 l -0.8 0 l -1 -0.333 l h 0 0 m -1 0.333 l -0.8 0 l -1 -0.333 l h -1 0.333 m 0 0 l -1 -0.333 l 0 0 m -1 0.333 l -1 -0.333 l h -1 0 m -2 0.333 l -2 -0.333 l h 0 0 m -1 0.333 l -1 -0.333 l h -1 0 m -2 0.333 l -2 -0.333 l h \renewcommand\rmdefault{cmss} \makeatletter \leftmargini 1.5em \leftmargin \leftmargini \leftmarginii 1.2em \leftmarginiii 1em \def\@listI{\leftmargin\leftmargini \parsep \z@ \partopsep 3pt \topsep \z@ \itemsep \z@} \@listI \def\@listii {\leftmargin\leftmarginii \labelwidth\leftmarginii \advance\labelwidth-\labelsep \topsep \z@ \parsep \z@ \itemsep \parsep} \def\@listiii{\leftmargin\leftmarginiii \labelwidth\leftmarginiii \advance\labelwidth-\labelsep \topsep \z@ \parsep \z@ \partopsep \z@ \itemsep \topsep} \makeatother 0 310.652 m 0 414.874 l 736 414.874 l 736 310.652 l h This is the first text with a bullet point. This is the second text which is centered and red. This is the third text which is Huge and \textcolor{lightgreen}{dark} green. 0 498.244 m 0 528 l 736 528 l 736 498.244 l h 0 446.874 m 0 466.244 l 736 466.244 l 736 446.874 l h 544 128 m 513.879 190.547 l 446.198 205.994 l 391.922 162.711 l 391.922 93.2893 l 446.198 50.0058 l 513.879 65.4535 l h This is the \textcolor{red}{first} line. \textcolor{blue}{This} is the second paragraph. \par This is the third \textcolor{purple}{paragraph.} \item A \item B \item C ipe-tools-7.2.24.1/poweripe/poweripe.py000066400000000000000000000223401420423641200177150ustar00rootroot00000000000000# # poweripe.py # # Translate an Ipe presentation to Powerpoint # import sys, os, re, datetime, argparse import pptx from pptx.util import Pt from pptx.enum.shapes import MSO_SHAPE from pptx.enum.text import PP_ALIGN, MSO_AUTO_SIZE, MSO_ANCHOR # TODO: maybe use a better Latex parser # from pylatexenc.latexwalker import LatexWalker, LatexEnvironmentNode from PIL import Image from lxml import etree import ipe assert sys.hexversion >= 0x3060000 # -------------------------------------------------------------------- # these values are from 'official' Ipe presentation stylesheet # TODO: compute based on actual stylesheet sizeMap = { "huge": 2.8 * 17, "large": 2.8 * 12, "normal": 2.8 * 10, "small" : 2.4 * 10, } # Latex definitions are: latex_sizes = { "\tiny", 5, "\footnotesize", 8, "\small", 9, "\normalsize", 10, "\large", 12, "\Large", 14, "\LARGE", 17, "\huge", 20, "\Huge", 25, } def addBulletToParagraph(p): size = p.font.size or Pt(18) el = p._pPr el.set("marL", "%d" % size) el.set("indent", "-%d" % size) nsmap = el.nsmap el.insert(0, etree.Element("{" + nsmap['a'] + "}buChar", char="\u2022")) # -------------------------------------------------------------------- class PowerIpe: def __init__(self, args): self.args = args def findColors(self): s = self.sheets.allNames("color") self.colors = {} for k in s: sym = s[k] abs = self.sheets.find("color", sym) h = "%02x%02x%02x" % (int(abs.r * 255), int(abs.g * 255), int(abs.b * 255)) rgb = pptx.dml.color.RGBColor.from_string(h) self.colors[sym] = rgb def convertTime(self, s): return datetime.datetime(int(s[2:6]), int(s[6:8]), int(s[8:10]), int(s[10:12]), int(s[12:14]), int(s[14:])) def addSvg(self, slide, pageNo, svgobjs): doc1 = ipe.Document() doc1.replaceSheets(self.sheets.clone()) doc1.setProperties(self.iprops) p1 = doc1[1] p = self.doc[pageNo] for i in svgobjs: obj = p[i] p1.insert(None, obj.clone(), None, "alpha") tmpname = "tmpipe%d.ipe" % pageNo svgname = "tmpipe%d.svg" % pageNo pngname = "tmpipe%d.png" % pageNo doc1.save(tmpname, "xml", None) os.system("iperender -svg -nocrop %s %s" % (tmpname, svgname)) os.system("iperender -png -nocrop -transparent %s %s" % (tmpname, pngname)) pic = slide.shapes.add_svg_picture(svgname, pngname, 0, 0) def convertText(self, slide, obj): s = obj.text().strip() textStyle = obj.get("textstyle") textSize = obj.get("textsize") color = obj.get("stroke") italic = False bold = False if s.startswith(r"\bfseries"): s = s[9:].lstrip() bold = True if s.startswith(r"\itshape"): s = s[8:].lstrip() italic = True parSplitRe = "\n[ \t]*\n|\\\\par\\W*" if textStyle == "itemize" and s.startswith(r"\item"): s = s[5:].lstrip() parSplitRe += "|\\\\item" textStyle = "item" pars = re.split(parSplitRe, s) pars = [ s.replace("\n", " ") for s in pars ] if len(pars) == 0: return # huh? box = ipe.Rect() obj.addToBBox(box, ipe.Matrix()) pos = box.topLeft() size = sizeMap.get(textSize, sizeMap["normal"]) #helper = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, Pt(self.x0 + pos.x), Pt(self.y0 - pos.y), # Pt(box.width()), Pt(box.height())) txBox = slide.shapes.add_textbox(Pt(self.x0 + pos.x), Pt(self.y0 - pos.y - 0.2 * size), Pt(box.width()), Pt(box.height())) tf = txBox.text_frame tf.word_wrap = True tf.auto_size = MSO_AUTO_SIZE.NONE tf.margin_top = 0 tf.margin_left = 0 tf.margin_right = 0 tf.margin_bottom = 0 tf.vertical_anchor = MSO_ANCHOR.TOP for i in range(len(pars)): s = pars[i].strip() s = re.sub(r"\\\\[ \t\n\r\f\v]*", "\v", s) if i == 0: p = tf.paragraphs[0] else: p = tf.add_paragraph() p.text = s p.font.size = Pt(size) p.line_spacing = Pt(1.2 * size) if textStyle == "center": p.alignment = PP_ALIGN.CENTER elif textStyle == "item": addBulletToParagraph(p) if color in self.colors: p.font.color.rgb = self.colors[color] p.font.bold = bold p.font.italic = italic if "\\textcolor{" in s: pos = 0 r = p pat = re.compile("\v|\\\\textcolor{([a-zA-Z0-9]+)}{([^}]+)}") while True: m = pat.search(s, pos) if m is None: r.text = s[pos:] break r.text = s[pos:m.start()] if m.lastindex is None: p.add_line_break() else: r = p.add_run() r.text = m.group(2) runColor = m.group(1) if runColor in self.colors: r.font.color.rgb = self.colors[runColor] r = p.add_run() pos = m.end() # determine if we want to convert this text object def wantConvertText(self, obj): if self.args.no_text: return False mp = obj.get("minipage") if not self.args.labels and not mp: return False if self.args.latex: return True # allow some simple latex s = obj.text().strip() if s.startswith("\\itshape"): s = s[8:] if s.startswith("\\bfseries"): s = s[9:] s = s.replace(r"\\", "") s = s.replace(r"\textcolor", "") s = s.replace(r"\item", "") s = s.replace(r"\par", "") return not ("\\" in s or "$" in s) def embedImage(self, slide, obj): info = obj.info() s = str(obj) # make a unique name imgname = "tmpipe" + s[s.index("@"):] ext = ".jpg" if info.format == "jpg" else ".ppm" obj.savePixels(imgname + ext) if ext == ".ppm": im = Image.open(imgname + ext) ext = ".png" im.save(imgname + ext) box = ipe.Rect() obj.addToBBox(box, ipe.Matrix()) pos = box.topLeft() pic = slide.shapes.add_picture(imgname + ext, Pt(self.x0 + pos.x), Pt(self.y0 - pos.y), Pt(box.width()), Pt(box.height())) def convertPage(self, pageNo, viewNo): sys.stderr.write("Converting page %d\n" % pageNo) p = self.doc[pageNo] t = p.titles() if t.title != "": slide = self.prs.slides.add_slide(self.title_slide_layout) title_placeholder = slide.shapes.title title_placeholder.text = t.title else: slide = self.prs.slides.add_slide(self.blank_slide_layout) # First determine which objects we'll convert to svg svgobjs = set() for i in range(1, len(p) + 1): if not p.visible(viewNo, i): continue obj = p[i] ty = obj.type() if ty == "text" and self.wantConvertText(obj): pass elif ty == "image": self.embedImage(slide, obj) else: svgobjs.add(i) self.addSvg(slide, pageNo, svgobjs) # Now add remaining objects for i in range(1, len(p) + 1): if i in svgobjs or not p.visible(viewNo, i): continue obj = p[i] ty = obj.type() if ty == "text": self.convertText(slide, obj) def convert(self): self.doc = ipe.Document(self.args.ipefile) self.sheets = self.doc.sheets() self.iprops = self.doc.properties() self.findColors() self.prs = pptx.Presentation() self.title_slide_layout = self.prs.slide_layouts[5] self.blank_slide_layout = self.prs.slide_layouts[6] props = self.prs.core_properties props.author = self.iprops.author props.last_modified_by = "" # shouldn't be python-pptx author props.title = self.iprops.title props.created = self.convertTime(self.iprops.created) props.modified = self.convertTime(self.iprops.modified) props.keywords = self.iprops.keywords props.subject = self.iprops.subject layout = self.sheets.find("layout") self.x0 = layout.origin.x self.y0 = layout.origin.y + layout.papersize.y self.prs.slide_width = Pt(layout.papersize.x) self.prs.slide_height = Pt(layout.papersize.y) # set formatting of page title placeholder tph = self.title_slide_layout.slide_master.placeholders[0] # make sure the title width is not excessive tph.width = min(tph.width, Pt(0.8 * layout.papersize.x)) for pageNo in range(1, len(self.doc) + 1): p = self.doc[pageNo] viewNo = p.countViews() self.convertPage(pageNo, viewNo) pptname = self.args.output if pptname is None: pptname = self.args.ipefile[:-4] + '.pptx' self.prs.save(pptname) # -------------------------------------------------------------------- if __name__ == "__main__": parser = argparse.ArgumentParser(description="Convert an Ipe presentation into pptx format") parser.add_argument('ipefile', help="File name for Ipe input") parser.add_argument('--output', help="File name for pptx output") parser.add_argument('--no-text', help="Do not convert text objects", action='store_true') parser.add_argument('--latex', help="Try to convert Latex text", action='store_true') parser.add_argument('--labels', help="Also convert label objects, not just minipages", action='store_true') powerIpe = PowerIpe(parser.parse_args()) powerIpe.convert() # -------------------------------------------------------------------- ipe-tools-7.2.24.1/poweripe/readme.md000066400000000000000000000043441420423641200172740ustar00rootroot00000000000000# Poweripe Do you prefer to create your presentations in Ipe? But your bosses/colleagues/clients keep asking for Powerpoint files? Fear no more! Poweripe is a Python script that translates an Ipe presentation into a Powerpoint presentation. Poweripe requires Ipe 7.2.13 or later. ## Installation Poweripe requires some Python modules: 1. Install the [ipepython](https://github.com/otfried/ipe-tools/tree/master/ipepython) module. (This module is used to read Ipe files.) 2. Install the `python-pptx` module (a module for creating Powerpoint documents). Currently, you need to get my fork of this module, with added SVG support. You can find it at https://github.com/otfried/python-pptx. Make sure to get the **svg-pictures** branch! ## Usage You run Poweripe from the command line like this: ``` python3 poweripe.py --no-text presentation.pdf ``` The output will be stored in `presentation.pptx`. Alternatively, provide the `--output` option to select a different output file. In this simplest mode, Poweripe stores **all** the contents of the Ipe document, including all text, as *graphics* in the pptx output. There is both an SVG-version of this graphics, which gives high-quality vector output, and a lower-resolution bitmap as a fallback. Recent Powerpoint versions will display the SVG contents, but it seems that Libreoffice does not yet support SVG and shows the bitmap version instead. If you remove the `--no-text` option, Poweripe will convert all minipage text objects that contain only simple Latex markup into text objects that can be edited in the pptx file. With the `--latex` option, Poweripe will convert all minipage text objects. You will have to edit the resulting pptx file to make it useful. With the `--labels` option, Poweripe will also convert text in Label objects (not just minipage objects). Since the `python-pptx` library does not yet support making slides that build up incrementally, Poweripe currently converts only the **last view** of each page. ipe-tools-7.2.24.1/snap/000077500000000000000000000000001420423641200146175ustar00rootroot00000000000000ipe-tools-7.2.24.1/snap/README.md000066400000000000000000000066501420423641200161050ustar00rootroot00000000000000# Ipe in a snap Linux users suffer from the problem that Linux distributions provide packages for rather old versions of Ipe. For instance, Ubuntu 16.04 still provides Ipe 7.1.10 (from late 2015). Distributions tend to be conservative to ensure the stability of the system. [**Snappy**](http://snapcraft.io) is a new system that provides apps in *encapsulated blocks* called **snaps**. A bug in a snap does not affect the robustness of the remaining sytem, and so users can freely upgrade individual snaps at will. From now on, Ipe will provide an up-to-date snap for each Ipe release, such that Linux users, like Windows and OSX users, can upgrade easily and immediately. Snappy is available on recent Linux distributions such as Ubuntu 16.04, etc. You install the system by installing the **snapd** package. On Ubuntu or Debian, you would use ``` sudo apt-get install snapd ``` See http://snapcraft.io/docs/core/install for how to install Snapcraft on other Linux distributions such as Fedora, ## Installing Ipe in a snap Once you have the **snap** command on your system, you can install the most recent version of Ipe by saying ``` sudo snap install ipe --edge ``` You can now start Ipe by simply saying `ipe` (which calls `/snap/bin/ipe`). Note that the other Ipe commands are available as `ipe.ipetoipe`, `ipe.iperender`, etc. ## How Ipe uses Pdflatex Snappy isolates each snap to ensure the robustness of the system when a snap misbehaves. This implies some severe restrictions: Ipe does not have access to your system's files (with the exception of the files in your home directory that do not start with a dot). In particular, Ipe does not have access to the Latex installation on your system! To allow Ipe to run *pdflatex*, the Ipe snap contains a *small Texlive installation*. You will need to remember this when Ipe fails to find a Latex style or package. In this case, you will need to install the Latex package **inside** the snap. Note that the following commands are exposed by the snap to help you find Latex problems: `ipe.pdflatex`, `ipe.lualatex`, `ipe.kpsewhich`. ## Installing additional Latex packages You cannot actually add additional Latex packages to the Texlive installation inside a snap, as snaps reside entirely in read-only memory. However, Ipe's Latex installation can use a *user tree* where you can add additional styles and fonts. To install into this user tree, you run the *texlive manager* `tlmgr` contained in the snap. For technical reasons, it has to be run from outside the snap, like this: ``` /snap/ipe/current/bin/tlmgr install dante-logo ``` ## What doesn't work? * The Ipe icon is not shown on the desktop (apparently a bug in snappy). * Opening the manual from the Ipe Help menu currently doesn't work (apparently a bug in snappy). You can read the manual by opening the file `/snap/ipe/current/ipe/doc/manual.html`. * Declaring an external editor doesn't work. This is currently a fundamental limitation of snappy (Ipe has no access to running an editor program that lives outside the snap). ## Anything else? If you want to customize Ipe, you will need to know that some files are in locations different from a classic Linux setup. Use *Show configuration* in the Help menu to find the right place. For instance, Ipe runs Latex in `~/snap/ipe/current/latexrun` (rather than `~/.ipe/latexrun`), and the ipelet directory is `~/snap/ipe/common/ipelets` rather than `~/.ipe/ipelets`. ipe-tools-7.2.24.1/snap/opencage/000077500000000000000000000000001420423641200164005ustar00rootroot00000000000000ipe-tools-7.2.24.1/snap/opencage/opencage.sh000077500000000000000000000003131420423641200205150ustar00rootroot00000000000000#!/bin/sh SNAP="/snap/ipe/current" COMMAND=$1 shift export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$SNAP/lib:$SNAP/usr/lib:$SNAP/lib/x86_64-linux-gnu:$SNAP/usr/lib/x86_64-linux-gnu" exec $SNAP/bin/$COMMAND $@ ipe-tools-7.2.24.1/snap/setup/000077500000000000000000000000001420423641200157575ustar00rootroot00000000000000ipe-tools-7.2.24.1/snap/setup/gui/000077500000000000000000000000001420423641200165435ustar00rootroot00000000000000ipe-tools-7.2.24.1/snap/setup/gui/icon.png000066400000000000000000000453141420423641200202100ustar00rootroot00000000000000PNG  IHDR>aiCCPICC Profile(Kaǿ(A`f{CHLeD宿شmY컻"^:f=**9R,"t%1)fm-%*t̩vOvSA\\zN=/EN1I pHYs   IDATxwTwz6`M@AA4QJF)Qb4ňƞ&(*E Ҥ,eپ;{~ܙٙ&^zkw9̹ysvGl Gg\9F#@OEChUV`{R߾bvܩ/++3L&s^^!m!lTGP,($$I>2?O5& |`&; z^9餓Ro5L&FHn vB IR{%Iuѓ{IBڷE!dUUM:B~Zvd…׍7A#.$IJ6zh׿UG~&z-&L ,X f͚%d.e/B8p@Ch-}o0t^ҎjV]ݒ$a1HxC_F~Q DŽ3&19f<ސԙ{ع)⿯Y6o`浏1pxbP1XIv$ _8K%AA)};d5m;V=6.zEQլJ1[+*%CN1T:#F/Џ3tuRq#XЏ |Ktbw6sjVF $ʀ`I{_H}w߯Odw2nӦ駟F[aMUN9cV-y`0re3ȗ]vX`S'IFx0$*Чx1V yo-># kP{O@PRC'AB6]yRRч{1ccƌIoxg?%8'tDo~#=z*zN@YT!AT`"kTP_, OXJWb(k$=gq4"ty|+>"8p fqtøy==Őz9p۷oO;| =X[Yp-%;M ,fןb&VSK^(ov{!^8|q]"ÕÓ+wXtSQV=̌6l؀W^1!Æ>)`3I3 yä2O<n;-^+--ej($[=@ELGIK Uy%ܲ EQ2>O9W 2x%xDiPUF9͋1$l=/4;f&Lc=5y&9g;w#pر2FŨQ3f cǎ`\aG{dIz=*y\jJ&j|M^x5ݕT1/2u:Q dYN2$Xr%+Wπ! ;2uj2!V! HueZ|slذ}bZX\Jac8i=ѣG3xX"h T!NUɛJl2 >I-]ԿσE3?@^ֹeP#rUg"O{߽K/o]ؒ$ {ThMGW,7L_׌^lQwEQ/Qhi 1u}Ldܓ\0̬!&m2mCOB:>K +/ihh#@>V@8 On6 g tR~ۿh_r%|52 QmgV] 6cƏ R!7LqY-rR|Of#k(I4zաPϱG|M?O04wϞ=yꩧ8sӪrA<]"2:aѵq'WtSq)s9.|m/K;nѸ>^ye&3}p`Ѣlr9^$ Ym "s<6&OW_}y<+ fРA oaÕ_vYn&d ,2>b*.y ( M.hػw/o^WkZ= 5Yk΁v.0>DΝo& |vUl{ 22Q= .+_GU&,Oݻ7seҥ:hG~tup˜˧~< #m zdSpJf̘Q#~/x:F9i c=FAIED83d]"P(Hgx1{%Ibȑ̽%DVUU/RZ˖-[rcE՗CZdZ@M9E7§_U7 k}5U X+gh `l w.J/ Z Uٗ}.zJ\xzuͷ~ 2T,> 8l03-8ogI}V;` ^"RH%mK`N7zie0~!ܑ (XN93fz+ v*:l|7@n=-!ȷT731;AIސ@'fw}8v6"If*LhK8JS! O3=[1pJƇ$t~lN,(X L4)FQPz<,ah0MB7(A'|ZڽBmB{Ldy&zyߠ4WS^^W\ݻ(Qr.U+?OAƯ'fʤ3b!li7]- LfY RJ!0lF-.0>J7|!,t>ѣYvYĩbIb}7>*2SO;T.̈rCv^<3&pWruq5װdV+yzoE:Y.?%\[T(A;Mt^bZU! b*,EcؠѣRoIؒ,cviEvQbin 33k*: i=)b&(fryq9r$8y{=FE~ϛqun8syꩧ:vxP88b !A B2T7)XbX x֬*9jpqH3)wx<,jkt^Zw@gN"LMmzŠ8E*Q|m?b8,`Xb'KNÜZӌRڹԵk۶mPr.@ `Sթ} Mvl !0 7QjOm:ij] ZVfϞ~-(|1yg< -->A^jq"kj5/zDd䵶p5PRRud\ _A*++L:P($H64@@.yBxտ9Æ l6c00 |(sΙyMKKK q;v͠xeYfСL0=x4 &8r|L[cP.P$r4fK;FYqW Lm[{1cxᇣ(f8N@W9yL/b0ΦZ6eLPѐkvoH밟&wIYSHS9=AM؍"٤Cp*X28$$S'qIaL/z%,K/]q2%AXڑiώz&Ls=k%Tgr̾v!/`1J1;'оo9fpe:Mu;A:F'SՠEYQyj;fØ tVWW>`4g@9´Y me$Ib,^8vBn8g˕WiԹ<*U N<OAWpUa@=MQ" ]%[+fMfVlg3bA4S?G|"r咟z)va#ݖE+[@t"Փ.$I:*%jQ@4d/G^26DPѳtذ6wKU(HZPP@AAu-x$QbwT֌*{ZQ-[c=mQT^R @ KN;_mG}ԻUZܽN3D>FhblSz%v]ZOE% O-XX[ͱڌ]y.&ӡS 끔\4Y؃;tчܲ]|KNpp3SP@"}%W$dMY%%&ЫW/TWԔv< H:nm4553x-nHoŦe]Nbfwތ; .xUp…|@<BA 42liLjA(ӗYrE*G43N h44iң9|lfĀrFɓ5k^{x^/ ,`ɒ%DVR6(0K=Z5{:d#Gd_UwȔtڛ>tŊ,隣eSw/X_9GۍX,R>[e1;lӟJ~iI*ƍٱc#G.hK}VMСC2|p֯_Ư7s霳bgsnQc"UUq߯~a{F[]& !ʄ# nJ6m?>皤|ᇌ9QƓh6("A6Vh|Ab4E;G H֎_{)0==4z '@$ !DZJ4Ek;⟱'9w;4Љ ,ǜqikA‰HT|!gIƢ'f$>~Na_ ocִ A3d=ÌƄG_X1=r(ݜ?DS3d 2J0+FQ;v h?""l*F2X;X=*v"^g۱Sq!l_>~0ez;d;UFDիWs=d|88ЉWN|&K`Aե64Bf frD AQ{̛jjRvŧrΰ4~cM#I];rJVX~3tDO]hƍjjj7oLjuQQQr?$0nIu`׷bQA l5ٱ9ɲLAQ1e|!a&ٓä(Umc7zG;BTHb p> _>|xB<B0|oߞS]v7|s~"߼2b$9FuPl(ڬ%/5LL03AT1u i|N20_"|矮GK8^Ǻu(*}E/UɔӘ1c3gΌ gaU?`w}J,"牜ww _Hk\Q¢h ݃{3/[x/ǯd}{\xoi!(A;?srB:";$IL2g}={&$zrRc0у?zxR:`FM),G`)ԹUlvt1${06Œ`bbçppfAݘ.rЇ!hn8f46lp{0'(˰H ZpѣG3h,O7{átPjQV_ާ~ƌ爣FGӺ$1PHD8Zv؁cR b֝q sr IDATsΡ(lic@*W_Ӈ և9cG"}Ba:2p킊JM..zxaܗї{ ?TIG׋b1h qEW_}U466 !j$/md&?wV+VVV&8w/mpPu!DWu ؁Ђ;^z3gfӻwok׮x=9dsi5|z}\p˓/Wg$(?,Dz7oNsEY-趣a]=p`xgϞ-[y1pDqmUV:rĿ/a4S^/~'<>9s+WzKuOG &lb Ip1$f.o9RTUU%hO,בv !Z?<]WW'v!֭['֮]+l"jjjĦǥ'+W v5Ŀ5U~ `ժUB! 1dȐ.'1Ȳ,,(**f>c_?/٣a>$ ׸~hOkG ewvOp(&%JRZAoRx,̜9GyolWud̛7ЬJt<&N_;3߫*>ǪUϸ[4i{xue3z.) 7ے_,QӦпH(Qda3laU1ޟfb~ .Eܳ4e'o@2 bܹ~B޷vvj o(*ȺOØM)>XSnQu<$Za0o/W 26mieI%2ϻ z(J0r7%|*g>?ff RxQ2d|̜)é(#Iʘ"-A4np*B\h@9[rHWRZ  `eu@; Z \|7s~z=+x>2B0j(nV.R q%tԹGCC昳A!^@ZY H%t .VͮHO$ZU;_vE)LpPLc1J\^upݬ^u^y֯_EQRdfw0|<|L4))ϓ9FQ ?^UHS2qz yѤ%:SpsDj5L3&Xc}ٛüJ캔\:M&W\qW\q ,^O5 :?ƐO)rg2{l''VSpX;͒__{ڷ55455r4^%KqoVM8 **C!tC"ڞYfNhÌF6vcNZ,%dA.&¡vVe6 ϝA8(RV%5풒1AP];vf^/ |}e+ի椓NhluT# UU%v:Yr9~W%av'y$qOO;}TS׌+|쮮.KB^VVqaCYz\.7t˖-KYέ9s$(-Thaɓ}ʳ 4dT%Lmc+{~K/ěoM0u*ޕʦٴqtS;Co{ Q4|uO>du~Yg?!Æ of͚5@$nZꪫf1IAEKVAU՛?D JyԔ( .o>B63]{=BA s,%wQg 3'$8v6C(}ҤI"]Sf /Ҷ T3yKt8y)-@L:}r&a0h :Qc_ G(ޗ:ЪXML2TO#AK3ϤFpQA٠ 9?-سk' $^,"Q>6G!5G^H"t%BՀBidF{Hܿu7=M)0 87rx˓O>ɨQRk3jbOX%XjOV jkZ|*5m*-l^_3 ɚ^@QiO:SӯwVV0Wn%l޴իVصVM`L=Dt_[gGiΪ4u,g춈 Yy4=ܴ}]'()pE>W@p{h,!V0%3[l=y+~ gRx8l2iʙLr&w}7O>,? ջfD@@y!a9cf?2%,-X[6??*eI9"> 55\/COep %v9\I4od w?'?^F|dOhF^ŭ?v{(|C>|N"I0 5}H-fwX4"]ZY4l?]ˊ5kS!_[km؞bn>h:%Cfӳ ZU !q-,}O =Uz=X1B"B—ys.hKo e?wm5#!+_y/׉w,"ރWpBf3"9z+؏ |}݇ߟX4s]wQZZn{,REK _"k10UQX̿nMڃN_#J.Ӑ%,Oǡ }Z9^ĺky_o!@ =NΧT?B !#-argʶ) CCI#dI!9`n;CI,5nǴ)RSb#BTƫH6zmXGuvSWd&bUɜ-T*(((sO 8p 9N![`đ%rDs]l޺#EQHS/K~p]ݡ#R;"8CZM.3XXu4,oR$'LGQ*+++z@XMد_kB{_V5ĀCK3G.T8t:MF7]rt:UnC{!ɎAMMM,]4hdРAɓ͑hVxDKAG AFV֩b3j!zh+fnjf P8\}Mw (N?rGLz,EV#;0\.!rFtp~\j@Xp RLHNk2&ZN#1یRT}1s-}>c䐎$&NŒgrxFJ푽,Ofoz=r*6md7n\sȝ(:3\tRZۜlŚWD~Y%ŽK_i5s+r۩up.&Z߽)K"DƽԨӸ'&%}B?~)s=ip&M7]sdbӧJLq9eY /`a`6`L橊B8uڬej8mVaZ9z(J$Stl 4o=dsT : ݔTgݪ勵de{ 6#bYX&duDHILIEH,A位X,hNAg ~ܵn(rEFo=d /t9a*' yxќzݭyy2fL{. #lFY2z"@&hO5 NE0FqYz ?y&LHmQ-V/HO2 !ؽm8ޕ|ɱl[N4kHF|oP Ƣ6u!آZ|E?}w5)ࠝҺ-"H1W3xZJ#,^Lus;*GN`) }zkk/:RNWB' Ju-W jZBPT\€1ӹ=z4W_}5˗/Oj= C;r܎w=*m~[lc7I jGN9;dĒ GA.Of]RPQz,r%!AWY30zĈΘ16˥B`۩A y*[V R>āGwB W0qy9n )eT0cjGeN[`4W-'f? JJ{ܤHItzIr>,zOȪ"/j냂F v0ꃴzUF ĝzϖ'm u`f|ôUUe(ek}vd[$Z*W$Jt PE{B:m%h93i3jdA FOT騋ȉi,&:qZAE^WEgcQfG察r|b(7^O>Iڵk;V+]w# /RUUk9e(\LxA0b Xe$F󓹗ÁfEuHT6,^A^*Shpk$M [AW ."M2:gk^9 @nҦ:I^/K,I^O^Cah+Qs:X`2m  <*u.ŐmBhQKA ;_ͤiA%6~ERL+rs׬LPUU*?GNOM P$IJ hdac=ơC/ٜ)di!p#EJf|L}mM>Q@_x(9EdMӯjѿߍ"%dIi71E4d]~ |dTAEАP,'>LEA-Z+ڑry+ZH*8rJ?Ծ\~AkKB}q *`T*˓`CǺAU;ܪfX*3$ 1C0O$Ɇvv3+t dqBD9cxw[PU5,2f͢OGm&M5{G'(8ɫrEIXz92'BafR*K d{\@&(4{ B0qUUUIdIz BV_QV۵]wl_>jdQ䛬`^ׇcfe8$/  Դ)3$CV s:ʜvت ʔ].mMfC^B{*pl>,Gg(2HZl[7y!a!Ͳ-ζ5P=jr7<\;)5BUΪ 8r, zb<U{mtR#uQM[ѓe#<T:d;@DmwϲEq`"k^_cqq1~;>p?fvSvEj d,z6طo+Wl%6]$Uv4Oxꘜ9exA%fHԊ+bB^^4mֈFQ339ÎbM'~\:eID8рB^oϞ=3g/?~|>k2Eڍ9Z# ^S"WYv ..8'UfW]vG\Mc;BYթDABNAU;\N."6mD Hx b̞=??!2hɦ=YZ2. GO7 S9t^y)SR\VC8?#q~M \A%/2+ppYP3, IJ~vd>.m#TYhr>Iq~XzM3fK-ECd\ ߜ!Է7=`uN5˖_oNh6 Z7Y]`Z I¡}w?z/z0pݜ {>_ 򛉱s/3VOmCz>jy.IDAT8n&fϞ&`*acs gMgjfFw, J62qɹJǟؗ'֌QDc3P ^sES,1Eh~fTOK,=l-^}gSᐩ*ħ0ȸ'u$Cb菵)|VAéX Td(qD4@qiY#: ?̞~f</?mΘ PK03᭷͡#GSOϽiS0[d?h&b6B2r* xk'il>9p(:a"r0c'Lau9=kS޷ (zsϞ[2(hpg>M$^`;oB Ǘq]5pJyZg{ȘFю5-t  $Hyc Ą`w@V_zquL} u|s4Ifi>\Bm ^ٹc+}W],b0k9}޻|+0Bn'/(+Mt$T8djZjs7p$\BPh4aoGT axq7ɶSE %n5 (f3gu̩P0* ܋/䵷$ }ql%V@S(ߢM y7y7v6jjÇ<zbiDPᐩs 9z$Imp$"iRBmK K:)&"3uX/!vS0I[e٠,so49="f^{>t-'^Č?\G^ѮT.1 {ױ$RhM uuq)$N: SQFV(J-ޠ*o87O֣G}`0(F΄|F8fAN*g\,1f m*Gߞy?%#w$iȑL]cǝƙgI' ?~,}붆mسwz-aEI4Ɔw;˯2$I,X@BA2DTLj_Q= !8ű̑5a'3>xg[!-CVhq}K7}Ò$IT׶EGoa9RBd I1ݒA՘ܙr H`y&_P`IdX$-rRjkj:h}GI9"N@;Vd!L[5|sa6$)).0*SAI*h UCӪM>=$BPUQ`0v:6k ߷i bL11S؁?D2c`  3B]Zu@B*Tj&m\ٱc%@;p:={w߫WU9saYN£T lie$8'ψLG!,e`C7ՅAE2ji^Rꛈ|AKM}em۶wR{V+}7\6C[/׮Lzj67 {wtQ NatV(3r`zLՂTe4@{+ ,E/TkRk"RWJka4<_۽ nSTb 5 c\W&$>o?ʢvpi͸%pQXqIAk}̉,c}P̋iKyD)"m۶w0 ^$V{W͌0cLr9.$pN4M2{GXgpG_?hGCibt c6dY9WGIENDB`ipe-tools-7.2.24.1/snap/setup/gui/ipe.desktop000066400000000000000000000000631420423641200207120ustar00rootroot00000000000000[Desktop Entry] Name=Ipe Exec=ipe Type=Application ipe-tools-7.2.24.1/snap/snap/000077500000000000000000000000001420423641200155605ustar00rootroot00000000000000ipe-tools-7.2.24.1/snap/snap/plugins/000077500000000000000000000000001420423641200172415ustar00rootroot00000000000000ipe-tools-7.2.24.1/snap/snap/plugins/x-texlive.py000066400000000000000000000017741420423641200215510ustar00rootroot00000000000000# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4 -*- import os from subprocess import Popen, PIPE import snapcraft from snapcraft.plugins import dump extras = [ "xcolor", "tcolorbox", "pgf", "ms", "environ", "pgfplots", "metalogo", "trimspaces", "etoolbox" ] class TexLivePlugin(snapcraft.plugins.dump.DumpPlugin): def build(self): super(dump.DumpPlugin, self).build() # Install TexLive with the standard installer env = self._build_environment() p1 = Popen(['echo', '-n', 'I'], env=env, stdout=PIPE) p2 = Popen(['{}/install-tl'.format(self.builddir), '-portable', '-scheme', 'basic'], env=env, stdin=p1.stdout, stdout=PIPE) output = p2.communicate()[0] self.run(['{}/texlive/bin/x86_64-linux/tlmgr'.format(self.installdir), 'install'] + extras, env=env) def _build_environment(self): env = os.environ.copy() env['TEXLIVE_INSTALL_PREFIX'] = os.path.join(self.installdir, 'texlive') return env ipe-tools-7.2.24.1/snap/snapcraft.yaml000066400000000000000000000065121420423641200174700ustar00rootroot00000000000000name: ipe version: 7.2.7 summary: Ipe http://ipe.otfried.org/ description: Drawing editor for creating figures in PDF format. Ipe supports making small figures for inclusion into LaTeX documents as well as making multi-page PDF presentations. Ipe's main features are * Entry of text as LaTeX source code. This makes it easy to enter mathematical expressions, and to reuse the LaTeX-macros of the main document. In the display text is displayed as it will appear in the figure. * Produces pure Postscript/PDF, including the text. Ipe converts the LaTeX-source to PDF or Postscript when the file is saved. * It is easy to align objects with respect to each other (for instance, to place a point on the intersection of two lines, or to draw a circle through three given points) using various snapping modes. * Users can provide ipelets (Ipe plug-ins) to add functionality to Ipe. This way, Ipe can be extended for each task at hand. * The text model is based on Unicode, and has been tested with Korean, Chinese, and Japanese. grade: devel confinement: strict parts: ipe: source: https://dl.bintray.com/otfried/generic/ipe/7.2/ipe-7.2.7-src.tar.gz source-subdir: src plugin: make make-parameters: - IPESNAPCRAFT=1 - IPEQVORONOI=1 - QT_SELECT=5 - IPESTRICT=1 build-packages: - libcairo2-dev - libfreetype6-dev - liblua5.3-dev - libjpeg8-dev - libpng12-dev - make - qtbase5-dev - qtbase5-dev-tools - zlib1g-dev - libqhull-dev after: [desktop-qt5] wrapper: plugin: dump source: ./wrapper organize: wrapper.sh: bin/wrapper.sh tlmgr.sh: bin/tlmgr texlive: plugin: texlive source: http://mirror.ctan.org/systems/texlive/tlnet/install-tl-unx.tar.gz build-packages: - wget prime: - -texlive/texmf-dist/doc - -texlive/texmf-dist/source - -texlive/texmf-dist/tex/context - -texlive/texmf-dist/tex/plain - -texlive/texmf-var/web2c/luatex/dvi* - -texlive/texmf-var/web2c/luatex/luatex.* - -texlive/texmf-var/web2c/luajittex/ - -texlive/texmf-var/web2c/tex/ - -texlive/texmf-var/web2c/metafont - -texlive/texmf-var/web2c/pdftex/etex.* - -texlive/texmf-var/web2c/pdftex/latex.* - -texlive/texmf-var/web2c/pdftex/mptopdf.* - -texlive/texmf-var/web2c/pdftex/pdfetex.* - -texlive/texmf-var/web2c/pdftex/pdftex.* - -texlive/bin/x86_64-linux/xdvi* - -texlive/bin/x86_64-linux/luajittex - -texlive/bin/x86_64-linux/g* - -texlive/bin/x86_64-linux/dvi* apps: ipe: command: desktop-launch wrapper.sh ipe plugs: [x11, home] ipetoipe: command: wrapper.sh ipetoipe plugs: [home] ipeextract: command: wrapper.sh ipeextract plugs: [home] iperender: command: wrapper.sh iperender plugs: [home] ipescript: command: wrapper.sh ipescript plugs: [home] ipe6upgrade: command: wrapper.sh ipe6upgrade plugs: [home] pdflatex: command: wrapper.sh pdflatex plugs: [home] lualatex: command: wrapper.sh lualatex plugs: [home] bibtex: command: wrapper.sh bibtex plugs: [home] kpsewhich: command: wrapper.sh kpsewhich plugs: [home] sh: command: wrapper.sh sh plugs: [home] ipe-tools-7.2.24.1/snap/wrapper/000077500000000000000000000000001420423641200162775ustar00rootroot00000000000000ipe-tools-7.2.24.1/snap/wrapper/tlmgr.sh000077500000000000000000000014011420423641200177570ustar00rootroot00000000000000#!/bin/bash --posix # This should be run outside the snap, # as the Perl in the snap is incomplete and cannot execute tlmgr.pl findSnap() { local source="${BASH_SOURCE[0]}" while [ -h "$source" ] ; do local linked="$(readlink "$source")" local dir="$(cd -P $(dirname "$source") && cd -P $(dirname "$linked") && pwd)" source="$dir/$(basename "$linked")" done (cd -P "$(dirname "$source")/.." && pwd) } export SNAP="$(findSnap)" export TEXMFHOME=$HOME/snap/ipe/common/texmf TLMGR="$SNAP/texlive/bin/x86_64-linux/tlmgr" #echo "SNAP is $SNAP" #echo "TEXMFHOME is $TEXMFHOME" if [ ! -d "$TEXMFHOME" ]; then echo "Creating personal texlive tree..." mkdir -p "$TEXMFHOME" perl $TLMGR init-usertree --usermode fi perl $TLMGR $@ --usermode ipe-tools-7.2.24.1/snap/wrapper/wrapper.sh000077500000000000000000000003761420423641200203240ustar00rootroot00000000000000#!/bin/sh export PATH="$SNAP/texlive/bin/x86_64-linux:$PATH" export TEXMFHOME="$SNAP_USER_COMMON/texmf" export IPELATEXDIR="$SNAP_USER_DATA/latexrun" export IPELETPATH="$SNAP_USER_COMMON/ipelets:_" export IPESTYLES="$SNAP_USER_COMMON/styles:_" exec "$@" ipe-tools-7.2.24.1/svgtoipe/000077500000000000000000000000001420423641200155165ustar00rootroot00000000000000ipe-tools-7.2.24.1/svgtoipe/readme.txt000066400000000000000000000062441420423641200175220ustar00rootroot00000000000000 Svgtoipe ======== This is Svgtoipe, a Python script that reads SVG figures and generates an XML file readable by Ipe. You'll need Python3 installed on your system. To process embedded images in SVG figures will also require the Python Image Library (PIL). (On Ubuntu/Debian, install python3-image). For installation, just copy "svgtoipe" to a suitable location on your system. You can report bugs on the issue tracking system at "https://github.com/otfried/ipe-tools/issues". Before reporting a bug, check that you have the latest version of Svgtoipe, and check the existing reports to see whether your bug has already been reported. Please do not send bug reports directly to me (the first thing I would do with the report is to enter it into the bug tracking system). Suggestions for features, or random comments on Svgtoipe can be sent to the Ipe discussion mailing list at . If you have problems installing or using Svgtoipe, the Ipe discussion mailing list would also be the best place to ask. You can send suggestions or comments directly to me by Email, but you should then not expect a reply. I cannot dedicate much time to Ipe, and the little time I have I prefer to put into development. I'm much more likely to get involved in a discussion of desirable features on the mailing list, where anyone interested can participate than by direct Email. Otfried Cheong Dept. of Computer Science KAIST Daejeon, South Korea Email: ipe@otfried.org Ipe webpage: http://ipe.otfried.org -------------------------------------------------------------------- Copyright (C) 2009-2019 Otfried Cheong svgtoipe 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 3 of the License, or (at your option) any later version. svgtoipe 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 svgtoipe; if not, you can find it at "http://www.gnu.org/copyleft/gpl.html", or write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -------------------------------------------------------------------- Changes ======= * 2019/12/10 Add rudimentary support for and (thanks to Lukas Barth #40). * 2019/08/01 Handle missing attributes in various elements. More color keywords. A test script to go through SVG samples. Handle 'a' elements. Default for 'fill' is black. * 2019/06/02 Handle coordinates without a digit before the period. Migrate to Python 3. * 2016/12/09 Command line argument to generate Ipe selection format, ability to read/write stdin/stdout (thanks to Christian Kapeller). * 2013/11/07 Bugs in path parsing and Latex generation fixed by Will Evans. * 2010/06/08 Added copyright notice and GPL license to svgtoipe distribution. * 2009/10/18 First version of svgtoipe. -------------------------------------------------------------------- ipe-tools-7.2.24.1/svgtoipe/svgtoipe.1000066400000000000000000000025301420423641200174400ustar00rootroot00000000000000.TH SVGTOIPE "1" "April 2015" "Ipe" "User Commands" .SH NAME svgtoipe \- Convert a SVG file to Ipe 7 format .SH SYNOPSIS .B svgtoipe \fI[\-h] [\-c] figure.svg [figure.ipe] \fR .SH DESCRIPTION \fBsvgtoipe\fR converts a SVG file to an XML file understood by Ipe version 7. .PP If the output filename is not specified, it will be derived either by replacing \fI.ipe\fR at the end of the input filename with \fI.svg\fR, or by appending \fI.ipe\fR if the input filename does not end with with \fI.svg\fR. If either input or output filename is '\-\-', then stdin, resp. stdout is used to read or write data. .TP \-h display a short help text .TP \-c operate in clipboard mode. Data is output as ipe clipboard content. This allows pasting svg data from inkscape to ipe: (1) Copy elements Inkscape to clipboard, (2) run: 'xsel | ./svgtoipe.py -c -- | xsel -i', (3) paste clipboard content into ipe. .SH Supported SVG Features The following SVG elements are converted: path, image, rect, circle, ellipse, line, polygon, and polyline The following SVG elements are converted: group, clipPath, linearGradient, andradialGradient Image conversion is only supported if \fIpython-imaging\fR is available. Text is only converted, if it has coordinates specified as attribute of the svg text element. .SH AUTHOR Otfried Cheong Christian Kapeller .SH "SEE ALSO" \fBipe\fR(1) ipe-tools-7.2.24.1/svgtoipe/svgtoipe.py000077500000000000000000001076211420423641200177420ustar00rootroot00000000000000#!/usr/bin/env python3 # -------------------------------------------------------------------- # convert SVG to Ipe format # -------------------------------------------------------------------- # # Copyright (C) 2009-2019 Otfried Cheong # # svgtoipe 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 3 of the License, or # (at your option) any later version. # # svgtoipe 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 svgtoipe; if not, you can find it at # "http://www.gnu.org/copyleft/gpl.html", or write to the Free # Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # SVG Specification: https://www.w3.org/TR/SVG11 # # TODO: # - coordinates used in gradients (e.g. intertwingly.svg) # - sanitize paths (Ipe refuses to load illegal paths, but they are common in SVG) # - handle elements # - store gradients in the dictionary in a more useful format # - parse title and desc elements and store in Ipe header # - go through SVG test suite (see test_svg.py) # # -------------------------------------------------------------------- svgtoipe_version = "20191210" import sys import argparse import xml.dom.minidom as xml from xml.dom.minidom import Node from xml.parsers.expat import ExpatError import re import math import base64 import io try: from PIL import Image have_pil = True except: have_pil = False assert sys.hexversion >= 0x3000000, "Please run svgtoipe using Python 3" ignored_nodes = set(["defs", "audio", "video", "metadata", "sodipodi:namedview", "style", "script", "SVGTestCase", "animate", "animateTransform", "set" ]) # -------------------------------------------------------------------- # should extend this to cover list in https://www.w3.org/TR/SVG11/types.html#ColorKeywords color_keywords = { "aqua" :"rgb(0, 255, 255)", "black" : "rgb(0, 0, 0)", "blue" :"rgb(0, 0, 255)", "firebrick" : "rgb(178, 34, 34)", "fuchsia" :"rgb(255, 0, 255)", "gray" :"rgb(128, 128, 128)", "green" :"rgb(0, 128, 0)", "grey" :"rgb(128, 128, 128)", "lime" :"rgb(0, 255, 0)", "maroon" :"rgb(128, 0, 0)", "navy" :"rgb(0, 0, 128)", "olive" :"rgb(128, 128, 0)", "purple" :"rgb(128, 0, 128)", "red" :"rgb(255, 0, 0)", "silver" :"rgb(192, 192, 192)", "teal" :"rgb(0, 128, 128)", "white" :"rgb(255, 255, 255)", "yellow" :"rgb(255, 255, 0)", } attribute_names = [ "stroke", "fill", "stroke-opacity", "fill-opacity", "stroke-width", "fill-rule", "stroke-linecap", "stroke-linejoin", "stroke-dasharray", "stroke-dashoffset", "stroke-miterlimit", "opacity", "font-size" ] def printAttributes(n): a = n.attributes for i in range(a.length): name = a.item(i).name if name[:9] != "sodipodi:" and name[:9] != "inkscape:": sys.stderr.write(" %s %s\n" % (name, n.getAttribute(name))) def parse_float(txt): if not txt: return None if txt.endswith('px') or txt.endswith('pt'): return float(txt[:-2]) elif txt.endswith('pc'): return 12 * float(txt[:-2]) elif txt.endswith('mm'): return 72.0 * float(txt[:-2]) / 25.4 elif txt.endswith('cm'): return 72.0 * float(txt[:-2]) / 2.54 elif txt.endswith('in'): return 72.0 * float(txt[:-2]) else: return float(txt) def parse_opacity(txt): if not txt: return None m = int(10 * (float(txt) + 0.05)) if m == 0: m = 1 return 10 * m def parse_list(string): return re.findall("([A-Za-z]|-?[0-9]+\.?[0-9]*(?:e-?[0-9]*)?)", string) def parse_style(string): sdict = {} for item in string.split(';'): if ':' in item: key, value = item.split(':') sdict[key.strip()] = value.strip() return sdict def parse_color_component(txt): if txt.endswith("%"): return float(txt[:-1]) / 100.0 else: return int(txt) / 255.0 def parse_color(c): if not c or c == 'none': return None if c in color_keywords: c = color_keywords[c] m = re.match(r"rgb\(([0-9\.]+%?),\s*([0-9\.]+%?),\s*([0-9\.]+%?)\s*\)", c) if m: r = parse_color_component(m.group(1)) g = parse_color_component(m.group(2)) b = parse_color_component(m.group(3)) return (r, g, b) m = re.match(r"#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$", c) if m: r = int(m.group(1), 16) / 15.0 g = int(m.group(2), 16) / 15.0 b = int(m.group(3), 16) / 15.0 return (r, g, b) m = re.match(r"#([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])" + r"([0-9a-fA-F][0-9a-fA-F])$", c) if m: r = int(m.group(1), 16) / 255.0 g = int(m.group(2), 16) / 255.0 b = int(m.group(3), 16) / 255.0 return (r, g, b) sys.stderr.write("Unknown color: %s\n" % c) return None def pnext(d, n): l = [] while n > 0: l.append(float(d.pop(0))) n -= 1 return tuple(l) def parse_path(out, d): d = re.findall("([A-Za-z]|-?(?:\.[0-9]+|[0-9]+\.?[0-9]*)(?:e-?[0-9]+)?)", d) x, y = 0.0, 0.0 xs, ys = 0.0, 0.0 x0, y0 = 0.0, 0.0 while d: if not d[0][0] in "01234567890.-": opcode = d.pop(0) if opcode == 'M': x, y = pnext(d, 2) x0, y0 = x, y # If this moveto command is the last command, it starts a nonsensical empty # new subpath, with crashes ipe. Thus, we skip this command in this case. # See also https://github.com/otfried/ipe-issues/issues/274 if d: out.write("%g %g m\n" % (x, y)) opcode = 'L' elif opcode == 'm': x1, y1 = pnext(d, 2) x += x1 y += y1 x0, y0 = x, y # If this moveto command is the last command, it starts a nonsensical empty # new subpath, with crashes ipe. Thus, we skip this command in this case. # See also https://github.com/otfried/ipe-issues/issues/274 if d: out.write("%g %g m\n" % (x, y)) opcode = 'l' elif opcode == 'L': x, y = pnext(d, 2) out.write("%g %g l\n" % (x, y)) elif opcode == 'l': x1, y1 = pnext(d, 2) x += x1 y += y1 out.write("%g %g l\n" % (x, y)) elif opcode == 'H': x = pnext(d, 1)[0] out.write("%g %g l\n" % (x, y)) elif opcode == 'h': x += pnext(d, 1)[0] out.write("%g %g l\n" % (x, y)) elif opcode == 'V': y = pnext(d, 1)[0] out.write("%g %g l\n" % (x, y)) elif opcode == 'v': y += pnext(d, 1)[0] out.write("%g %g l\n" % (x, y)) elif opcode == 'C': x1, y1, xs, ys, x, y = pnext(d, 6) out.write("%g %g %g %g %g %g c\n" % (x1, y1, xs, ys, x, y)) elif opcode == 'c': x1, y1, xs, ys, xf, yf = pnext(d, 6) x1 += x; y1 += y xs += x; ys += y x += xf; y += yf out.write("%g %g %g %g %g %g c\n" % (x1, y1, xs, ys, x, y)) elif opcode == 'S' or opcode == 's': x2, y2, xf, yf = pnext(d, 4) if opcode == 's': x2 += x; y2 += y xf += x; yf += y x1 = x + (x - xs); y1 = y + (y - ys) out.write("%g %g %g %g %g %g c\n" % (x1, y1, x2, y2, xf, yf)) xs, ys = x2, y2 x, y = xf, yf elif opcode == 'Q': xs, ys, x, y = pnext(d, 4) out.write("%g %g %g %g q\n" % (xs, ys, x, y)) elif opcode == 'q': xs, ys, xf, yf = pnext(d, 4) xs += x; ys += y x += xf; y += yf out.write("%g %g %g %g q\n" % (xs, ys, x, y)) elif opcode == 'T' or opcode == 't': xf, yf = pnext(d, 2) if opcode == 't': xf += x; yf += y x1 = x + (x - xs); y1 = y + (y - ys) out.write("%g %g %g %g q\n" % (x1, y1, xf, yf)) xs, ys = x1, y1 x, y = xf, yf elif opcode == 'A' or opcode == 'a': rx, ry, phi, large_arc, sweep, x2, y2 = pnext(d, 7) if opcode == 'a': x2 += x; y2 += y draw_arc(out, x, y, rx, ry, phi, large_arc, sweep, x2, y2) x, y = x2, y2 elif opcode in 'zZ': x, y = x0, y0 out.write("h\n") else: sys.stderr.write("Unrecognised opcode: %s\n" % opcode) def parse_transformation(txt): d = re.findall("[a-zA-Z]+\([^)]*\)", txt) m = Matrix() while d: m1 = Matrix(d.pop(0)) m = m * m1 return m def get_gradientTransform(n): if n.hasAttribute("gradientTransform"): return parse_transformation(n.getAttribute("gradientTransform")) return Matrix() def parse_transform(n): if n.hasAttribute("transform"): return parse_transformation(n.getAttribute("transform")) return None # Convert from endpoint to center parameterization # www.w3.org/TR/2003/REC-SVG11-20030114/implnote.html#ArcImplementationNotes def draw_arc(out, x1, y1, rx, ry, phi, large_arc, sweep, x2, y2): phi = math.pi * phi / 180.0 cp = math.cos(phi); sp = math.sin(phi) dx = .5 * (x1 - x2); dy = .5 * (y1 - y2) x1p = cp * dx + sp * dy; y1p = -sp * dx + cp * dy r2 = (((rx * ry)**2 - (rx * y1p)**2 - (ry * x1p)**2)/ ((rx * y1p)**2 + (ry * x1p)**2)) if r2 < 0: r2 = 0 r = math.sqrt(r2) if large_arc == sweep: r = -r cxp = r * rx * y1p / ry; cyp = -r * ry * x1p / rx cx = cp * cxp - sp * cyp + .5 * (x1 + x2) cy = sp * cxp + cp * cyp + .5 * (y1 + y2) m = Matrix([rx, 0, 0, ry, 0, 0]) m = Matrix([cp, sp, -sp, cp, cx, cy]) * m if sweep == 0: m = m * Matrix([1, 0, 0, -1, 0, 0]) out.write("%s %g %g a\n" % (str(m), x2, y2)) # -------------------------------------------------------------------- class Matrix(): # Default is identity matrix def __init__(self, string = None): self.values = [1, 0, 0, 1, 0, 0] if not string or string == "": return if isinstance(string, list): self.values = string return mat = re.match(r"([a-zA-Z]+)\(([^)]*)\)$", string) if not mat: sys.stderr.write("Unknown transform: %s\n" % string) op = mat.group(1) d = [float(x) for x in parse_list(mat.group(2))] if op == "matrix": self.values = d elif op == "translate": if len(d) == 1: d.append(0.0) self.values = [1, 0, 0, 1, d[0], d[1]] elif op == "scale": if len(d) == 1: d.append(d[0]) sx, sy = d self.values = [sx, 0, 0, sy, 0, 0] elif op == "rotate": phi = math.pi * d[0] / 180.0 self.values = [math.cos(phi), math.sin(phi), -math.sin(phi), math.cos(phi), 0, 0] elif op == "skewX": tphi = math.tan(math.pi * d[0] / 180.0) self.values = [1, 0, tphi, 1, 0, 0] elif op == "skewY": tphi = math.tan(math.pi * d[0] / 180.0) self.values = [1, tphi, 0, 1, 0, 0] else: sys.stderr.write("Unknown transform: %s\n" % string) def __call__(self, other): return (self.values[0]*other[0] + self.values[2]*other[1] + self.values[4], self.values[1]*other[0] + self.values[3]*other[1] + self.values[5]) def inverse(self): d = float(self.values[0]*self.values[3] - self.values[1]*self.values[2]) return Matrix([self.values[3]/d, -self.values[1]/d, -self.values[2]/d, self.values[0]/d, (self.values[2]*self.values[5] - self.values[3]*self.values[4])/d, (self.values[1]*self.values[4] - self.values[0]*self.values[5])/d]) def __mul__(self, other): a, b, c, d, e, f = self.values u, v, w, x, y, z = other.values return Matrix([a*u + c*v, b*u + d*v, a*w + c*x, b*w + d*x, a*y + c*z + e, b*y + d*z + f]) def __str__(self): a, b, c, d, e, f = self.values return "%g %g %g %g %g %g" % (a, b, c, d, e, f) # -------------------------------------------------------------------- class Svg(): def __init__(self, fname): try: if fname == "--": self.dom = xml.parseString(sys.stdin.read()) else: self.dom = xml.parse(fname) except ExpatError as exc: sys.stderr.write('ERROR: Input is not valid xml data:\n') sys.stderr.write(exc.message) return attr = { } for a in attribute_names: attr[a] = None self.attributes = [ attr ] self.defs = { } self.symbols = { } for n in self.dom.childNodes: if n.nodeType == Node.ELEMENT_NODE and n.tagName == "svg": if n.hasAttribute("viewBox"): x, y, w, h = [float(x) for x in parse_list(n.getAttribute("viewBox"))] self.width = w self.height = h self.origin = (x, y) else: self.width = parse_float(n.getAttribute("width")) or 500 self.height = parse_float(n.getAttribute("height")) or 500 self.origin = (0, 0) self.root = n return # -------------------------------------------------------------------- def write_ipe_header(self): self.out.write('\n') self.out.write('\n') self.out.write('\n' % svgtoipe_version) self.out.write('\n') self.out.write(('\n') % (self.width, self.height, self.width, self.height)) for t in range(10, 100, 10): self.out.write('\n' % (t, t)) # set SVG defaults self.out.write('\n') self.out.write('\n') # collect definitions self.collect_definitions(self.root) # write definitions into stylesheet if len(self.defs) > 0: self.out.write('\n') for k in self.defs: if self.defs[k][0] == "linearGradient": self.write_linear_gradient(k) elif self.defs[k][0] == "radialGradient": self.write_radial_gradient(k) self.out.write('\n') def collect_definitions(self, node): # Collect Everything inside a tag for which we have a defs_() method defs_children = node.getElementsByTagName('defs') for c in defs_children: for my_attr in dir(self): if my_attr[:4] == 'def_': tag_name = my_attr[4:] # There is a parsing method for tags named parseable_tags = node.getElementsByTagName(tag_name) for tag in parseable_tags: getattr(self, my_attr)(tag) def parse_svg(self, outname, **kwargs): """ parses the svg, and writes it to file. outname, str, output filename. if '--' then parse_svg writes to stdout. Keywords: outmode, str, 'file' write in ipe file format. 'clipboard', write as ipe clipboard selection """ outmode = kwargs.get('outmode', 'file') if outname == '--': self.out = sys.stdout else: self.out = open(outname, "w") # write header if outmode == 'file': self.write_ipe_header() self.out.write('\n') elif outmode == "clipboard": self.out.write('\n') else: sys.stderr.write("bad output mode '{}'".format(outmode)) return self.write_data() if outmode == 'file': self.out.write('\n') self.out.write('\n') elif outmode == 'clipboard': self.out.write('\n') self.out.close() def write_data(self): # start real data m = Matrix([1, 0, 0, 1, 0, self.height / 2.0]) m = m * Matrix([1, 0, 0, -1, 0, 0]) m = m * Matrix([1, 0, 0, 1, -self.origin[0], -(self.origin[1] + self.height / 2.0)]) self.out.write('\n' % str(m)) self.parse_nodes(self.root) self.out.write('\n') def parse_nodes(self, root): """ parses recognized svg elements """ for n in root.childNodes: if n.nodeType != Node.ELEMENT_NODE: continue if n.tagName in ignored_nodes: continue nodeName = "node_" + n.tagName.replace(":","_") if hasattr(self, nodeName): getattr(self, nodeName)(n) else: sys.stderr.write("Unhandled node: %s\n" % n.tagName) # -------------------------------------------------------------------- def write_linear_gradient(self, k): typ, x1, x2, y1, y2, stops, matrix = self.defs[k] self.out.write('\n' % (x1, y1, x2, y2)) for s in stops: offset, color = s self.out.write(' \n' % (offset, color[0], color[1], color[2])) self.out.write('\n') def write_radial_gradient(self, k): typ, cx, cy, r, fx, fy, stops, matrix = self.defs[k] self.out.write('\n' % (fx, fy, 0, cx, cy, r)) for s in stops: offset, color = s self.out.write(' \n' % (offset, color[0], color[1], color[2])) self.out.write('\n') def get_stops(self, n): stops = [] for m in n.childNodes: if m.nodeType != Node.ELEMENT_NODE: continue if m.tagName != "stop": continue # should not happen offs = m.getAttribute("offset") or "0" if offs.endswith("%"): offs = float(offs[:-1]) / 100.0 else: offs = float(offs) if m.hasAttribute("stop-color"): color = parse_color(m.getAttribute("stop-color")) else: color = [ 0, 0, 0 ] if m.hasAttribute("style"): sdict = parse_style(m.getAttribute("style")) if "stop-color" in sdict: color = parse_color(sdict["stop-color"]) stops.append((offs, color)) if len(stops) == 0: if n.hasAttribute("xlink:href"): ref = n.getAttribute("xlink:href") if ref.startswith("#") and ref[1:] in self.defs: gradient = self.defs[ref[1:]] # TODO: use a class instead of a tuple! if gradient[0] == "linearGradient": stops = gradient[5] else: stops = gradient[6] return stops def node_inkscape_clipboard(self, node): self.parse_nodes(node) def def_symbol(self, node): # Symbols are parsed by pretending the symbol node is a group node and storing it in # self.symbols. Later, whenever a tag is encountered, # we call node_g() on the fake group node. self.symbols[node.getAttribute('id')] = node def def_linearGradient(self, n): #printAttributes(n) kid = n.getAttribute("id") x1 = 0; y1 = 0 x2 = self.width; y2 = self.height if n.hasAttribute("x1"): s = n.getAttribute("x1") if s.endswith("%"): x1 = self.width * float(s[:-1]) / 100.0 else: x1 = parse_float(s) if n.hasAttribute("x2"): s = n.getAttribute("x2") if s.endswith("%"): x2 = self.width * float(s[:-1]) / 100.0 else: x2 = parse_float(s) if n.hasAttribute("y1"): s = n.getAttribute("y1") if s.endswith("%"): y1 = self.width * float(s[:-1]) / 100.0 else: y1 = parse_float(s) if n.hasAttribute("y2"): s = n.getAttribute("y2") if s.endswith("%"): y2 = self.width * float(s[:-1]) / 100.0 else: y2 = parse_float(s) matrix = get_gradientTransform(n) stops = self.get_stops(n) self.defs[kid] = ("linearGradient", x1, x2, y1, y2, stops, matrix) def def_radialGradient(self, n): #printAttributes(n) kid = n.getAttribute("id") cx = "50%"; cy = "50%"; r = "50%" if n.hasAttribute("cx"): cx = n.getAttribute("cx") if cx.endswith("%"): cx = self.width * float(cx[:-1]) / 100.0 else: cx = parse_float(cx) if n.hasAttribute("cy"): cy = n.getAttribute("cy") if cy.endswith("%"): cy = self.width * float(cy[:-1]) / 100.0 else: cy = parse_float(cy) if n.hasAttribute("r"): r = n.getAttribute("r") if r.endswith("%"): r = self.width * float(r[:-1]) / 100.0 else: r = parse_float(r) if n.hasAttribute("fx"): s = n.getAttribute("fx") if s.endswith("%"): fx = self.width * float(s[:-1]) / 100.0 else: fx = parse_float(s) else: fx = cx if n.hasAttribute("fy"): s = n.getAttribute("fy") if s.endswith("%"): fy = self.width * float(s[:-1]) / 100.0 else: fy = parse_float(s) else: fy = cy matrix = get_gradientTransform(n) stops = self.get_stops(n) self.defs[kid] = ("radialGradient", cx, cy, r, fx, fy, stops, matrix) def def_clipPath(self, node): kid = node.getAttribute("id") # only a single path is implemented for n in node.childNodes: if n.nodeType != Node.ELEMENT_NODE or n.tagName != "path": continue m = parse_transform(n) d = n.getAttribute("d") output = io.StringIO() parse_path(output, d) path = output.getvalue() output.close() self.defs[kid] = ("clipPath", m, path) return def def_g(self, group): self.collect_definitions(group) def def_defs(self, node): self.collect_definitions(node) # -------------------------------------------------------------------- def parse_attributes(self, n): pattr = self.attributes[-1] attr = { } for a in attribute_names: if n.hasAttribute(a): attr[a] = n.getAttribute(a) else: attr[a] = pattr[a] if n.hasAttribute("style"): sdict = parse_style(n.getAttribute("style")) for a in attribute_names: if a in sdict: attr[a] = sdict[a] return attr def write_pathattributes(self, a): stroke = parse_color(a["stroke"]) if stroke: self.out.write(' stroke="%g %g %g"' % stroke) fill = a["fill"] if fill and fill.startswith("url("): mat = re.match("url\(#([^)]+)\).*", fill) if mat: grad = mat.group(1) if grad in self.defs and (self.defs[grad][0] == "linearGradient" or self.defs[grad][0] == "radialGradient"): self.out.write(' fill="1" gradient="g%s"' % grad) elif fill is None: self.out.write(' fill="0"') else: fill = parse_color(fill) if fill: self.out.write(' fill="%g %g %g"' % fill) opacity = parse_opacity(a["opacity"]) fill_opacity = parse_opacity(a["fill-opacity"]) stroke_opacity = parse_opacity(a["stroke-opacity"]) if fill and fill_opacity: opacity = fill_opacity if not fill and stroke and stroke_opacity: opacity = stroke_opacity if opacity and opacity != 100: self.out.write(' opacity="%d%%"' % opacity) stroke_width = parse_float(a["stroke-width"]) if a["stroke-width"]: self.out.write(' pen="%g"' % stroke_width) if a["fill-rule"] == "nonzero": self.out.write(' fillrule="wind"') k = {"butt" : 0, "round" : 1, "square" : 2 } if a["stroke-linecap"] in k: self.out.write(' cap="%d"' % k[a["stroke-linecap"]]) k = {"miter" : 0, "round" : 1, "bevel" : 2 } if a["stroke-linejoin"] in k: self.out.write(' join="%d"' % k[a["stroke-linejoin"]]) dasharray = a["stroke-dasharray"] dashoffset = a["stroke-dashoffset"] if dasharray and dashoffset and dasharray != "none": d = parse_list(dasharray) off = parse_float(dashoffset) self.out.write(' dash="[%s] %g"' % (" ".join(d), off)) # -------------------------------------------------------------------- def node_g(self, group): # printAttributes(group) attr = self.parse_attributes(group) self.attributes.append(attr) self.out.write('\n') self.parse_nodes(group) self.out.write('\n') self.attributes.pop() def node_use(self, n): if not n.hasAttribute('xlink:href'): print("Ignoring use node without xlink:href") return symbol_name = n.getAttribute('xlink:href') if symbol_name[0] != '#': print("Ignoring use node not referencing symbol ID") return symbol_name = symbol_name[1:] if symbol_name not in self.symbols: print("Ignoring use node for unknown symbol '{}'".format(symbol_name)) return translate_x = '0' if n.hasAttribute('x'): translate_x = n.getAttribute('x') translate_y = '0' if n.hasAttribute('y'): translate_y = n.getAttribute('y') scale_x = 1.0 if n.hasAttribute('width'): if n.getAttribute('width')[-1] != '%': print("Ignoring use with non-relative width scale.") return scale_x = float(n.getAttribute('width')[:-1]) / 100.0 scale_y = 1.0 if n.hasAttribute('height'): if n.getAttribute('height')[-1] != '%': print("Ignoring use with non-relative height scale.") return scale_y = float(n.getAttribute('height')[:-1]) / 100.0 scale_m = Matrix("scale({} {})".format(scale_x, scale_y)) translate_m = Matrix( "translate({} {})".format(translate_x, translate_y)) m = scale_m * translate_m # Use a group for the possible transform matrix from this # use node self.out.write('\n'.format(m)) self.node_g(self.symbols[symbol_name]) self.out.write('\n') def node_a(self, n): url = n.getAttribute("xlink:href") if url is not None: self.out.write('' % url) self.parse_nodes(n) if url is not None: self.out.write('\n') def collect_text(self, root): for n in root.childNodes: if n.nodeType == Node.TEXT_NODE: self.text += n.data if n.nodeType != Node.ELEMENT_NODE: continue if n.tagName == "tspan": # recurse self.collect_text(n) def parse_text_style(self, t): attrs = {} if not t.hasAttribute("style"): return attrs tokens = t.getAttribute("style").split(";") for token in tokens: if (len(token.split(":")) != 2): # Strange token sys.stderr.write("Ignored style token: %s\n" % token) continue key, value = token.split(":") value = value.strip().lower() key = key.strip().lower() if key == "font-weight": if value in ("bold", "bolder"): attrs["weight"] = "bold" elif key == "font-size": attrs["size"] = parse_float(value) elif key == "font-family": attrs["svg-families"] = [v.strip() for v in (value.split(","))] elif key == "text-anchor": attrs["anchor"] = value return attrs def parse_text_attrs(self, t): raw_attrs = self.parse_attributes(t) attrs = {} if raw_attrs["font-size"]: attrs["size"] = parse_float(raw_attrs["font-size"]) if raw_attrs["fill"]: attrs["color"] = parse_color(raw_attrs["fill"]) return attrs def map_svg_font_families(self, families): # return the first family for which we find a mapping for family in families: if family in ("helvetica", "sans-serif"): return "phv" elif family in ("times new roman", "times", "serif"): return "ptm" return None def node_text(self, t): if not t.hasAttribute("x"): x = 0.0 else: x = float(t.getAttribute("x")) if not t.hasAttribute("y"): y = 0.0 else: y = float(t.getAttribute("y")) attributes = self.parse_text_style(t) attributes.update(self.parse_text_attrs(t)) self.out.write('%s\n' % self.text) def node_image(self, node): if not have_pil: sys.stderr.write("No Python image library, ignored\n") return href = node.getAttribute("xlink:href") if not href.startswith("data:image/png;base64,"): sys.stderr.write("Image ignored, href = %s...\n" % href[:40]) return x = float(node.getAttribute("x")) y = float(node.getAttribute("y")) w = float(node.getAttribute("width")) h = float(node.getAttribute("height")) clipped = False if node.hasAttribute("clip-path"): mat = re.match("url\(#([^)]+)\).*", node.getAttribute("clip-path")) if mat: cp = mat.group(1) if cp in self.defs and self.defs[cp][0] == "clipPath": cp, m, path = self.defs[cp] clipped = True self.out.write('\n' % (str(m), path)) self.out.write('\n' % str(m.inverse())) self.out.write(' \n') if True: data = io.StringIO() for pixel in image.getdata(): data.write("%c%c%c" % pixel[:3]) self.out.write(base64.b64encode(data.getvalue())) data.close() else: count = 0 for pixel in image.getdata(): self.out.write("%02x%02x%02x" % pixel[:3]) count += 1 if count == 10: self.out.write("\n") count = 0 fin.close() self.out.write('\n') if clipped: self.out.write('\n\n') # handled in def pass def node_linearGradient(self, n): pass def node_radialGradient(self, n): pass def node_rect(self, n): attr = self.parse_attributes(n) self.out.write('\n') if not n.hasAttribute("x"): x = 0.0 else: x = parse_float(n.getAttribute("x")) if not n.hasAttribute("y"): y = 0.0 else: y = parse_float(n.getAttribute("y")) w = parse_float(n.getAttribute("width")) h = parse_float(n.getAttribute("height")) self.out.write("%g %g m %g %g l %g %g l %g %g l h\n" % (x, y, x + w, y, x + w, y + h, x, y + h)) self.out.write('\n') def node_circle(self, n): self.out.write('\n') cx = 0 cy = 0 if n.hasAttribute("cx"): cx = parse_float(n.getAttribute("cx")) if n.hasAttribute("cy"): cy = parse_float(n.getAttribute("cy")) r = parse_float(n.getAttribute("r")) self.out.write("%g 0 0 %g %g %g e\n" % (r, r, cx, cy)) self.out.write('\n') def node_ellipse(self, n): self.out.write('\n') cx = 0 cy = 0 if n.hasAttribute("cx"): cx = float(n.getAttribute("cx")) if n.hasAttribute("cy"): cy = float(n.getAttribute("cy")) rx = float(n.getAttribute("rx")) ry = float(n.getAttribute("ry")) self.out.write("%g 0 0 %g %g %g e\n" % (rx, ry, cx, cy)) self.out.write('\n') def node_line(self, n): self.out.write('\n') x1 = 0; y1 = 0; x2 = 0; y2 = 0 if n.hasAttribute("x1"): x1 = float(n.getAttribute("x1")) if n.hasAttribute("y1"): y1 = float(n.getAttribute("y1")) if n.hasAttribute("x2"): x2 = float(n.getAttribute("x2")) if n.hasAttribute("y2"): y2 = float(n.getAttribute("y2")) self.out.write("%g %g m %g %g l\n" % (x1, y1, x2, y2)) self.out.write('\n') def node_polyline(self, n): self.polygon(n, closed=False) def node_polygon(self, n): self.polygon(n, closed=True) def polygon(self, n, closed): self.out.write('\n') d = parse_list(n.getAttribute("points")) op = "m" while d: x = float(d.pop(0)) y = float(d.pop(0)) self.out.write("%g %g %s\n" % (x, y, op)) op = "l" if closed: self.out.write("h\n") self.out.write('\n') def node_path(self, n): d = n.getAttribute("d") if len(d) == 0: # Empty paths crash ipe, filter them out return self.out.write('\n') parse_path(self.out, d) self.out.write('\n') # -------------------------------------------------------------------- def parse_arguments(): """ parses command line arguments""" parser = argparse.ArgumentParser( description="convert SVG into Ipe files", epilog=""" Supported SVG elements: path,image,rect,circle,ellipse,line,polygon,polyline Supported SVG attributes: group,clipPath,linearGradient,radialGradient """) parser.add_argument('-c', '--clipboard', dest='clipboard', action='store_true', help=""" if -c is present, svg data is written as clipboard content. This allows pasting svg data from inkscape to ipe: (1) Copy Inkscape elements to clipboard, (2) run: 'xsel | ./svgtoipe.py -c -- | xsel -i', (3) paste clipboard content into Ipe. """) parser.add_argument('infile', metavar='svg-file.svg', help="input file in svg format. If infile = '--' svg data is read from stdin.") parser.add_argument('outfile', metavar='ipe-file.ipe', nargs='?', help=""" filename to write output. If no filename is given, the input filename together with '.ipe' extension is used. If outfile is '--', then data is written to stdout.""") parser.set_defaults( infile="--", verbosity=0, clipboard=False, ) #try: args = parser.parse_args() if args.outfile is None: if args.infile != "--": args.outfile = args.infile[:-4] + ".ipe" return args #except Exception as exc: #sys.stderr.write("parsing error:\n") #sys.exit(1) def main(): args = parse_arguments() svg = Svg(args.infile) if args.clipboard: svg.parse_svg(args.outfile, outmode="clipboard") else: svg.parse_svg(args.outfile) sys.exit(0) if __name__ == '__main__': main() # -------------------------------------------------------------------- ipe-tools-7.2.24.1/svgtoipe/test_svg.py000066400000000000000000000024461420423641200177340ustar00rootroot00000000000000#!/usr/bin/env python3 # # Test svgtoipe.py on a few examples # # More tests: # SVG Test suite with PNG reference: https://www.w3.org/Graphics/SVG/Test/20110816/ # import os, sys, re import urllib.request assert sys.hexversion >= 0x3000000, "Please run using Python 3" ignore = set(["photos.svg", "videos.svg"]) def getSamples(): url = "https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/" req = urllib.request.Request(url) f = urllib.request.urlopen(req) index = f.read() fpat = re.compile(b'href="([-_a-zA-Z0-9]+\\.svg)"') for m in fpat.finditer(index): fname = m.group(1).decode("UTF-8") if fname in ignore: continue if os.path.exists("test/" + fname): continue sys.stderr.write("Downloading '%s'\n" % fname) req1 = urllib.request.Request(url + fname) ex = urllib.request.urlopen(req1).read() out = open("test/" + fname, "wb") out.write(ex) out.close() def testSamples(): files = os.listdir("./test") for f in files: if not f.endswith(".svg"): continue status = os.system("python3 svgtoipe.py test/%s 2> test/results-%s.txt" % (f, f)) if status != 0: sys.stderr.write("Error converting '%s'\n" % f) else: sys.stderr.write("Converted '%s'\n" % f) if __name__ == "__main__": getSamples() testSamples()