├── .gitignore ├── composer.json ├── README.md ├── LICENSE └── bin └── unoconv /.gitignore: -------------------------------------------------------------------------------- 1 | # Application 2 | composer.lock 3 | composer.phar 4 | vendor/ 5 | 6 | # IDEs 7 | .idea/ 8 | .settings/ 9 | .project/ 10 | .buildpath/ 11 | 12 | # Miscellaneous 13 | .DS_Store -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "leodido/unoconv", 3 | "license": "GPL-2.0", 4 | "authors": [ 5 | { 6 | "name": "Leo Di Donato", 7 | "email": "leodidonato@gmail.com" 8 | }, 9 | { 10 | "name": "Lorenzo Fontana", 11 | "email": "fontanalorenzo@me.com" 12 | } 13 | ], 14 | "bin": [ 15 | "bin/unoconv" 16 | ], 17 | "require": { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Unoconv 2 | ======= 3 | 4 | This repository provides the [unoconv](https://github.com/dagwieers/unoconv) executable. 5 | 6 | Docs about **unconv** can be found [here](https://github.com/dagwieers/unoconv). 7 | 8 | Installation 9 | ------------ 10 | 11 | This package, named `leodido/unoconv`, can be found on packagist and installed with [composer](https://getcomposer.org/). 12 | 13 | _Note: the version of this package is equal to the version (i.e., git tag) of unoconv._ 14 | 15 | Require the package with: 16 | 17 | ``` 18 | php composer.phar require leodido/unoconv "0.6" 19 | ``` 20 | 21 | The binary will be located at: 22 | 23 | ``` 24 | vendor/leodido/unoconv/bin/unconv 25 | ``` 26 | 27 | Also a symlink will be created in your configured `bin/` folder, e.g: 28 | 29 | ``` 30 | vendor/bin/unoconv 31 | ``` 32 | 33 | Enjoy. 34 | 35 | --- 36 | 37 | [![Analytics](https://ga-beacon.appspot.com/UA-49657176-1/unoconv)](https://github.com/igrigorik/ga-beacon) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /bin/unoconv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | ### This program is free software; you can redistribute it and/or modify 4 | ### it under the terms of the GNU General Public License as published by 5 | ### the Free Software Foundation; version 2 only 6 | ### 7 | ### This program is distributed in the hope that it will be useful, 8 | ### but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 | ### GNU General Public License for more details. 11 | ### 12 | ### You should have received a copy of the GNU General Public License 13 | ### along with this program; if not, write to the Free Software 14 | ### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | ### Copyright 2007-2010 Dag Wieers 16 | 17 | from __future__ import print_function 18 | 19 | from distutils.version import LooseVersion 20 | import getopt 21 | import glob 22 | import os 23 | import subprocess 24 | import sys 25 | import time 26 | 27 | __version__ = "$Revision$" 28 | # $Source$ 29 | 30 | VERSION = '0.6' 31 | 32 | doctypes = ('document', 'graphics', 'presentation', 'spreadsheet') 33 | 34 | global convertor, office, ooproc, product 35 | ooproc = None 36 | exitcode = 0 37 | 38 | class Office: 39 | def __init__(self, basepath, urepath, unopath, pyuno, binary, python, pythonhome): 40 | self.basepath = basepath 41 | self.urepath = urepath 42 | self.unopath = unopath 43 | self.pyuno = pyuno 44 | self.binary = binary 45 | self.python = python 46 | self.pythonhome = pythonhome 47 | 48 | def __str__(self): 49 | return self.basepath 50 | 51 | def __repr__(self): 52 | return self.basepath 53 | 54 | ### Implement a path normalizer in order to make unoconv work on MacOS X 55 | ### (on which 'program' is a symlink to 'MacOSX' which seems to break unoconv) 56 | def realpath(*args): 57 | ''' Implement a combination of os.path.join(), os.path.abspath() and 58 | os.path.realpath() in order to normalize path constructions ''' 59 | ret = '' 60 | for arg in args: 61 | ret = os.path.join(ret, arg) 62 | return os.path.realpath(os.path.abspath(ret)) 63 | 64 | ### The first thing we ought to do is find a suitable Office installation 65 | ### with a compatible pyuno library that we can import. 66 | ### 67 | ### See: http://user.services.openoffice.org/en/forum/viewtopic.php?f=45&t=36370&p=166783 68 | 69 | def find_offices(): 70 | ret = [] 71 | extrapaths = [] 72 | 73 | ### Try using UNO_PATH first (in many incarnations, we'll see what sticks) 74 | if 'UNO_PATH' in os.environ: 75 | extrapaths += [ os.environ['UNO_PATH'], 76 | os.path.dirname(os.environ['UNO_PATH']), 77 | os.path.dirname(os.path.dirname(os.environ['UNO_PATH'])) ] 78 | 79 | else: 80 | 81 | if os.name in ( 'nt', 'os2' ): 82 | if 'PROGRAMFILES' in list(os.environ.keys()): 83 | extrapaths += glob.glob(os.environ['PROGRAMFILES']+'\\LibreOffice*') + \ 84 | glob.glob(os.environ['PROGRAMFILES']+'\\OpenOffice.org*') 85 | 86 | if 'PROGRAMFILES(X86)' in list(os.environ.keys()): 87 | extrapaths += glob.glob(os.environ['PROGRAMFILES(X86)']+'\\LibreOffice*') + \ 88 | glob.glob(os.environ['PROGRAMFILES(X86)']+'\\OpenOffice.org*') 89 | 90 | elif os.name in ( 'mac', ) or sys.platform in ( 'darwin', ): 91 | extrapaths += [ '/Applications/LibreOffice.app/Contents', 92 | '/Applications/NeoOffice.app/Contents', 93 | '/Applications/OpenOffice.org.app/Contents' ] 94 | 95 | else: 96 | extrapaths += glob.glob('/usr/lib*/libreoffice*') + \ 97 | glob.glob('/usr/lib*/openoffice*') + \ 98 | glob.glob('/usr/lib*/ooo*') + \ 99 | glob.glob('/opt/libreoffice*') + \ 100 | glob.glob('/opt/openoffice*') + \ 101 | glob.glob('/opt/ooo*') + \ 102 | glob.glob('/usr/local/libreoffice*') + \ 103 | glob.glob('/usr/local/openoffice*') + \ 104 | glob.glob('/usr/local/ooo*') + \ 105 | glob.glob('/usr/local/lib/libreoffice*') 106 | 107 | ### Find a working set for python UNO bindings 108 | for basepath in extrapaths: 109 | if os.name in ( 'nt', 'os2' ): 110 | officelibraries = ( 'pyuno.pyd', ) 111 | officebinaries = ( 'soffice.exe' ,) 112 | pythonbinaries = ( 'python.exe', ) 113 | pythonhomes = () 114 | elif os.name in ( 'mac', ) or sys.platform in ( 'darwin', ): 115 | officelibraries = ( 'pyuno.so', 'libpyuno.dylib' ) 116 | officebinaries = ( 'soffice.bin', 'soffice') 117 | pythonbinaries = ( 'python.bin', 'python' ) 118 | pythonhomes = ( 'OOoPython.framework/Versions/*/lib/python*', ) 119 | else: 120 | officelibraries = ( 'pyuno.so', ) 121 | officebinaries = ( 'soffice.bin', ) 122 | pythonbinaries = ( 'python.bin', 'python', ) 123 | pythonhomes = ( 'python-core-*', ) 124 | 125 | ### Older LibreOffice/OpenOffice and Windows use basis-link/ or basis/ 126 | libpath = 'error' 127 | for basis in ( 'basis-link', 'basis', '' ): 128 | for lib in officelibraries: 129 | if os.path.isfile(realpath(basepath, basis, 'program', lib)): 130 | libpath = realpath(basepath, basis, 'program') 131 | officelibrary = realpath(libpath, lib) 132 | info(3, "Found %s in %s" % (lib, libpath)) 133 | # Break the inner loop... 134 | break 135 | # Continue if the inner loop wasn't broken. 136 | else: 137 | continue 138 | # Inner loop was broken, break the outer. 139 | break 140 | else: 141 | continue 142 | 143 | ### MacOSX have soffice binaries installed in MacOS subdirectory, not program 144 | unopath = 'error' 145 | for basis in ( 'basis-link', 'basis', '' ): 146 | for bin in officebinaries: 147 | if os.path.isfile(realpath(basepath, basis, 'program', bin)): 148 | unopath = realpath(basepath, basis, 'program') 149 | officebinary = realpath(unopath, bin) 150 | info(3, "Found %s in %s" % (bin, unopath)) 151 | # Break the inner loop... 152 | break 153 | # Continue if the inner loop wasn't broken. 154 | else: 155 | continue 156 | # Inner loop was broken, break the outer. 157 | break 158 | else: 159 | continue 160 | 161 | ### Windows does not provide or need a URE/lib directory ? 162 | urepath = '' 163 | for basis in ( 'basis-link', 'basis', '' ): 164 | for ure in ( 'ure-link', 'ure', 'URE', '' ): 165 | if os.path.isfile(realpath(basepath, basis, ure, 'lib', 'unorc')): 166 | urepath = realpath(basepath, basis, ure) 167 | info(3, "Found %s in %s" % ('unorc', realpath(urepath, 'lib'))) 168 | # Break the inner loop... 169 | break 170 | # Continue if the inner loop wasn't broken. 171 | else: 172 | continue 173 | # Inner loop was broken, break the outer. 174 | break 175 | 176 | pythonhome = None 177 | for home in pythonhomes: 178 | if glob.glob(realpath(libpath, home)): 179 | pythonhome = glob.glob(realpath(libpath, home))[0] 180 | info(3, "Found %s in %s" % (home, pythonhome)) 181 | break 182 | 183 | # if not os.path.isfile(realpath(basepath, program, officebinary)): 184 | # continue 185 | # info(3, "Found %s in %s" % (officebinary, realpath(basepath, program))) 186 | 187 | # if not glob.glob(realpath(basepath, basis, program, 'python-core-*')): 188 | # continue 189 | 190 | for pythonbinary in pythonbinaries: 191 | if os.path.isfile(realpath(unopath, pythonbinary)): 192 | info(3, "Found %s in %s" % (pythonbinary, unopath)) 193 | ret.append(Office(basepath, urepath, unopath, officelibrary, officebinary, 194 | realpath(unopath, pythonbinary), pythonhome)) 195 | else: 196 | info(3, "Considering %s" % basepath) 197 | ret.append(Office(basepath, urepath, unopath, officelibrary, officebinary, 198 | sys.executable, None)) 199 | return ret 200 | 201 | def office_environ(office): 202 | ### Set PATH so that crash_report is found 203 | os.environ['PATH'] = realpath(office.basepath, 'program') + os.pathsep + os.environ['PATH'] 204 | 205 | ### Set UNO_PATH so that "officehelper.bootstrap()" can find soffice executable: 206 | os.environ['UNO_PATH'] = office.unopath 207 | 208 | ### Set URE_BOOTSTRAP so that "uno.getComponentContext()" bootstraps a complete 209 | ### UNO environment 210 | if os.name in ( 'nt', 'os2' ): 211 | os.environ['URE_BOOTSTRAP'] = 'vnd.sun.star.pathname:' + realpath(office.basepath, 'program', 'fundamental.ini') 212 | else: 213 | os.environ['URE_BOOTSTRAP'] = 'vnd.sun.star.pathname:' + realpath(office.basepath, 'program', 'fundamentalrc') 214 | 215 | ### Set LD_LIBRARY_PATH so that "import pyuno" finds libpyuno.so: 216 | if 'LD_LIBRARY_PATH' in os.environ: 217 | os.environ['LD_LIBRARY_PATH'] = office.unopath + os.pathsep + \ 218 | realpath(office.urepath, 'lib') + os.pathsep + \ 219 | os.environ['LD_LIBRARY_PATH'] 220 | else: 221 | os.environ['LD_LIBRARY_PATH'] = office.unopath + os.pathsep + \ 222 | realpath(office.urepath, 'lib') 223 | 224 | if office.pythonhome: 225 | for libpath in ( realpath(office.pythonhome, 'lib'), 226 | realpath(office.pythonhome, 'lib', 'lib-dynload'), 227 | realpath(office.pythonhome, 'lib', 'lib-tk'), 228 | realpath(office.pythonhome, 'lib', 'site-packages'), 229 | office.unopath): 230 | sys.path.insert(0, libpath) 231 | else: 232 | ### Still needed for system python using LibreOffice UNO bindings 233 | ### Although we prefer to use a system UNO binding in this case 234 | sys.path.append(office.unopath) 235 | 236 | def debug_office(): 237 | if 'URE_BOOTSTRAP' in os.environ: 238 | print('URE_BOOTSTRAP=%s' % os.environ['URE_BOOTSTRAP'], file=sys.stderr) 239 | if 'UNO_PATH' in os.environ: 240 | print('UNO_PATH=%s' % os.environ['UNO_PATH'], file=sys.stderr) 241 | if 'UNO_TYPES' in os.environ: 242 | print('UNO_TYPES=%s' % os.environ['UNO_TYPES'], file=sys.stderr) 243 | print('PATH=%s' % os.environ['PATH']) 244 | if 'PYTHONHOME' in os.environ: 245 | print('PYTHONHOME=%s' % os.environ['PYTHONHOME'], file=sys.stderr) 246 | if 'PYTHONPATH' in os.environ: 247 | print('PYTHONPATH=%s' % os.environ['PYTHONPATH'], file=sys.stderr) 248 | if 'LD_LIBRARY_PATH' in os.environ: 249 | print('LD_LIBRARY_PATH=%s' % os.environ['LD_LIBRARY_PATH'], file=sys.stderr) 250 | 251 | def python_switch(office): 252 | if office.pythonhome: 253 | os.environ['PYTHONHOME'] = office.pythonhome 254 | os.environ['PYTHONPATH'] = realpath(office.pythonhome, 'lib') + os.pathsep + \ 255 | realpath(office.pythonhome, 'lib', 'lib-dynload') + os.pathsep + \ 256 | realpath(office.pythonhome, 'lib', 'lib-tk') + os.pathsep + \ 257 | realpath(office.pythonhome, 'lib', 'site-packages') + os.pathsep + \ 258 | office.unopath 259 | 260 | os.environ['UNO_PATH'] = office.unopath 261 | 262 | info(3, "-> Switching from %s to %s" % (sys.executable, office.python)) 263 | if os.name in ('nt', 'os2'): 264 | ### os.execv is broken on Windows and can't properly parse command line 265 | ### arguments and executable name if they contain whitespaces. subprocess 266 | ### fixes that behavior. 267 | ret = subprocess.call([office.python] + sys.argv[0:]) 268 | sys.exit(ret) 269 | else: 270 | 271 | ### Set LD_LIBRARY_PATH so that "import pyuno" finds libpyuno.so: 272 | if 'LD_LIBRARY_PATH' in os.environ: 273 | os.environ['LD_LIBRARY_PATH'] = office.unopath + os.pathsep + \ 274 | realpath(office.urepath, 'lib') + os.pathsep + \ 275 | os.environ['LD_LIBRARY_PATH'] 276 | else: 277 | os.environ['LD_LIBRARY_PATH'] = office.unopath + os.pathsep + \ 278 | realpath(office.urepath, 'lib') 279 | 280 | try: 281 | os.execvpe(office.python, [office.python, ] + sys.argv[0:], os.environ) 282 | except OSError: 283 | ### Mac OS X versions prior to 10.6 do not support execv in 284 | ### a process that contains multiple threads. Instead of 285 | ### re-executing in the current process, start a new one 286 | ### and cause the current process to exit. This isn't 287 | ### ideal since the new process is detached from the parent 288 | ### terminal and thus cannot easily be killed with ctrl-C, 289 | ### but it's better than not being able to autoreload at 290 | ### all. 291 | ### Unfortunately the errno returned in this case does not 292 | ### appear to be consistent, so we can't easily check for 293 | ### this error specifically. 294 | ret = os.spawnvpe(os.P_WAIT, office.python, [office.python, ] + sys.argv[0:], os.environ) 295 | sys.exit(ret) 296 | 297 | class Fmt: 298 | def __init__(self, doctype, name, extension, summary, filter): 299 | self.doctype = doctype 300 | self.name = name 301 | self.extension = extension 302 | self.summary = summary 303 | self.filter = filter 304 | 305 | def __str__(self): 306 | return "%s [.%s]" % (self.summary, self.extension) 307 | 308 | def __repr__(self): 309 | return "%s/%s" % (self.name, self.doctype) 310 | 311 | class FmtList: 312 | def __init__(self): 313 | self.list = [] 314 | 315 | def add(self, doctype, name, extension, summary, filter): 316 | self.list.append(Fmt(doctype, name, extension, summary, filter)) 317 | 318 | def byname(self, name): 319 | ret = [] 320 | for fmt in self.list: 321 | if fmt.name == name: 322 | ret.append(fmt) 323 | return ret 324 | 325 | def byextension(self, extension): 326 | ret = [] 327 | for fmt in self.list: 328 | if os.extsep + fmt.extension == extension: 329 | ret.append(fmt) 330 | return ret 331 | 332 | def bydoctype(self, doctype, name): 333 | ret = [] 334 | for fmt in self.list: 335 | if fmt.name == name and fmt.doctype == doctype: 336 | ret.append(fmt) 337 | return ret 338 | 339 | def display(self, doctype): 340 | print("The following list of %s formats are currently available:\n" % doctype, file=sys.stderr) 341 | for fmt in self.list: 342 | if fmt.doctype == doctype: 343 | print(" %-8s - %s" % (fmt.name, fmt), file=sys.stderr) 344 | print(file=sys.stderr) 345 | 346 | fmts = FmtList() 347 | 348 | ### TextDocument 349 | fmts.add('document', 'bib', 'bib', 'BibTeX', 'BibTeX_Writer') ### 22 350 | fmts.add('document', 'doc', 'doc', 'Microsoft Word 97/2000/XP', 'MS Word 97') ### 29 351 | fmts.add('document', 'doc6', 'doc', 'Microsoft Word 6.0', 'MS WinWord 6.0') ### 24 352 | fmts.add('document', 'doc95', 'doc', 'Microsoft Word 95', 'MS Word 95') ### 28 353 | fmts.add('document', 'docbook', 'xml', 'DocBook', 'DocBook File') ### 39 354 | fmts.add('document', 'docx', 'docx', 'Microsoft Office Open XML', 'Office Open XML Text') 355 | fmts.add('document', 'docx7', 'docx', 'Microsoft Office Open XML', 'MS Word 2007 XML') 356 | fmts.add('document', 'fodt', 'fodt', 'OpenDocument Text (Flat XML)', 'OpenDocument Text Flat XML') 357 | fmts.add('document', 'html', 'html', 'HTML Document (OpenOffice.org Writer)', 'HTML (StarWriter)') ### 3 358 | fmts.add('document', 'latex', 'ltx', 'LaTeX 2e', 'LaTeX_Writer') ### 31 359 | fmts.add('document', 'mediawiki', 'txt', 'MediaWiki', 'MediaWiki') 360 | fmts.add('document', 'odt', 'odt', 'ODF Text Document', 'writer8') ### 10 361 | fmts.add('document', 'ooxml', 'xml', 'Microsoft Office Open XML', 'MS Word 2003 XML') ### 11 362 | fmts.add('document', 'ott', 'ott', 'Open Document Text', 'writer8_template') ### 21 363 | fmts.add('document', 'pdb', 'pdb', 'AportisDoc (Palm)', 'AportisDoc Palm DB') 364 | fmts.add('document', 'pdf', 'pdf', 'Portable Document Format', 'writer_pdf_Export') ### 18 365 | fmts.add('document', 'psw', 'psw', 'Pocket Word', 'PocketWord File') 366 | fmts.add('document', 'rtf', 'rtf', 'Rich Text Format', 'Rich Text Format') ### 16 367 | fmts.add('document', 'sdw', 'sdw', 'StarWriter 5.0', 'StarWriter 5.0') ### 23 368 | fmts.add('document', 'sdw4', 'sdw', 'StarWriter 4.0', 'StarWriter 4.0') ### 2 369 | fmts.add('document', 'sdw3', 'sdw', 'StarWriter 3.0', 'StarWriter 3.0') ### 20 370 | fmts.add('document', 'stw', 'stw', 'Open Office.org 1.0 Text Document Template', 'writer_StarOffice_XML_Writer_Template') ### 9 371 | fmts.add('document', 'sxw', 'sxw', 'Open Office.org 1.0 Text Document', 'StarOffice XML (Writer)') ### 1 372 | fmts.add('document', 'text', 'txt', 'Text Encoded', 'Text (encoded)') ### 26 373 | fmts.add('document', 'txt', 'txt', 'Text', 'Text') ### 34 374 | fmts.add('document', 'uot', 'uot', 'Unified Office Format text','UOF text') ### 27 375 | fmts.add('document', 'vor', 'vor', 'StarWriter 5.0 Template', 'StarWriter 5.0 Vorlage/Template') ### 6 376 | fmts.add('document', 'vor4', 'vor', 'StarWriter 4.0 Template', 'StarWriter 4.0 Vorlage/Template') ### 5 377 | fmts.add('document', 'vor3', 'vor', 'StarWriter 3.0 Template', 'StarWriter 3.0 Vorlage/Template') ### 4 378 | fmts.add('document', 'wps', 'wps', 'Microsoft Works', 'MS_Works') 379 | fmts.add('document', 'xhtml', 'html', 'XHTML Document', 'XHTML Writer File') ### 33 380 | 381 | ### WebDocument 382 | fmts.add('web', 'etext', 'txt', 'Text Encoded (OpenOffice.org Writer/Web)', 'Text (encoded) (StarWriter/Web)') ### 14 383 | fmts.add('web', 'html10', 'html', 'OpenOffice.org 1.0 HTML Template', 'writer_web_StarOffice_XML_Writer_Web_Template') ### 11 384 | fmts.add('web', 'html', 'html', 'HTML Document', 'HTML') ### 2 385 | fmts.add('web', 'html', 'html', 'HTML Document Template', 'writerweb8_writer_template') ### 13 386 | fmts.add('web', 'mediawiki', 'txt', 'MediaWiki', 'MediaWiki_Web') ### 9 387 | fmts.add('web', 'pdf', 'pdf', 'PDF - Portable Document Format', 'writer_web_pdf_Export') ### 10 388 | fmts.add('web', 'sdw3', 'sdw', 'StarWriter 3.0 (OpenOffice.org Writer/Web)', 'StarWriter 3.0 (StarWriter/Web)') ### 3 389 | fmts.add('web', 'sdw4', 'sdw', 'StarWriter 4.0 (OpenOffice.org Writer/Web)', 'StarWriter 4.0 (StarWriter/Web)') ### 4 390 | fmts.add('web', 'sdw', 'sdw', 'StarWriter 5.0 (OpenOffice.org Writer/Web)', 'StarWriter 5.0 (StarWriter/Web)') ### 5 391 | fmts.add('web', 'txt', 'txt', 'OpenOffice.org Text (OpenOffice.org Writer/Web)', 'writerweb8_writer') ### 12 392 | fmts.add('web', 'text10', 'txt', 'OpenOffice.org 1.0 Text Document (OpenOffice.org Writer/Web)', 'writer_web_StarOffice_XML_Writer') ### 15 393 | fmts.add('web', 'text', 'txt', 'Text (OpenOffice.org Writer/Web)', 'Text (StarWriter/Web)') ### 8 394 | fmts.add('web', 'vor4', 'vor', 'StarWriter/Web 4.0 Template', 'StarWriter/Web 4.0 Vorlage/Template') ### 6 395 | fmts.add('web', 'vor', 'vor', 'StarWriter/Web 5.0 Template', 'StarWriter/Web 5.0 Vorlage/Template') ### 7 396 | 397 | ### Spreadsheet 398 | fmts.add('spreadsheet', 'csv', 'csv', 'Text CSV', 'Text - txt - csv (StarCalc)') ### 16 399 | fmts.add('spreadsheet', 'dbf', 'dbf', 'dBASE', 'dBase') ### 22 400 | fmts.add('spreadsheet', 'dif', 'dif', 'Data Interchange Format', 'DIF') ### 5 401 | fmts.add('spreadsheet', 'fods', 'fods', 'OpenDocument Spreadsheet (Flat XML)', 'OpenDocument Spreadsheet Flat XML') 402 | fmts.add('spreadsheet', 'html', 'html', 'HTML Document (OpenOffice.org Calc)', 'HTML (StarCalc)') ### 7 403 | fmts.add('spreadsheet', 'ods', 'ods', 'ODF Spreadsheet', 'calc8') ### 15 404 | fmts.add('spreadsheet', 'ooxml', 'xml', 'Microsoft Excel 2003 XML', 'MS Excel 2003 XML') ### 23 405 | fmts.add('spreadsheet', 'ots', 'ots', 'ODF Spreadsheet Template', 'calc8_template') ### 14 406 | fmts.add('spreadsheet', 'pdf', 'pdf', 'Portable Document Format', 'calc_pdf_Export') ### 34 407 | fmts.add('spreadsheet', 'pxl', 'pxl', 'Pocket Excel', 'Pocket Excel') 408 | fmts.add('spreadsheet', 'sdc', 'sdc', 'StarCalc 5.0', 'StarCalc 5.0') ### 31 409 | fmts.add('spreadsheet', 'sdc4', 'sdc', 'StarCalc 4.0', 'StarCalc 4.0') ### 11 410 | fmts.add('spreadsheet', 'sdc3', 'sdc', 'StarCalc 3.0', 'StarCalc 3.0') ### 29 411 | fmts.add('spreadsheet', 'slk', 'slk', 'SYLK', 'SYLK') ### 35 412 | fmts.add('spreadsheet', 'stc', 'stc', 'OpenOffice.org 1.0 Spreadsheet Template', 'calc_StarOffice_XML_Calc_Template') ### 2 413 | fmts.add('spreadsheet', 'sxc', 'sxc', 'OpenOffice.org 1.0 Spreadsheet', 'StarOffice XML (Calc)') ### 3 414 | fmts.add('spreadsheet', 'uos', 'uos', 'Unified Office Format spreadsheet', 'UOF spreadsheet') ### 9 415 | fmts.add('spreadsheet', 'vor3', 'vor', 'StarCalc 3.0 Template', 'StarCalc 3.0 Vorlage/Template') ### 18 416 | fmts.add('spreadsheet', 'vor4', 'vor', 'StarCalc 4.0 Template', 'StarCalc 4.0 Vorlage/Template') ### 19 417 | fmts.add('spreadsheet', 'vor', 'vor', 'StarCalc 5.0 Template', 'StarCalc 5.0 Vorlage/Template') ### 20 418 | fmts.add('spreadsheet', 'xhtml', 'xhtml', 'XHTML', 'XHTML Calc File') ### 26 419 | fmts.add('spreadsheet', 'xls', 'xls', 'Microsoft Excel 97/2000/XP', 'MS Excel 97') ### 12 420 | fmts.add('spreadsheet', 'xls5', 'xls', 'Microsoft Excel 5.0', 'MS Excel 5.0/95') ### 8 421 | fmts.add('spreadsheet', 'xls95', 'xls', 'Microsoft Excel 95', 'MS Excel 95') ### 10 422 | fmts.add('spreadsheet', 'xlt', 'xlt', 'Microsoft Excel 97/2000/XP Template', 'MS Excel 97 Vorlage/Template') ### 6 423 | fmts.add('spreadsheet', 'xlt5', 'xlt', 'Microsoft Excel 5.0 Template', 'MS Excel 5.0/95 Vorlage/Template') ### 28 424 | fmts.add('spreadsheet', 'xlt95', 'xlt', 'Microsoft Excel 95 Template', 'MS Excel 95 Vorlage/Template') ### 21 425 | fmts.add('spreadsheet', 'xlsx', 'xlsx', 'Microsoft Excel 2007/2010 XML', 'Calc MS Excel 2007 XML') 426 | 427 | ### Graphics 428 | fmts.add('graphics', 'bmp', 'bmp', 'Windows Bitmap', 'draw_bmp_Export') ### 21 429 | fmts.add('graphics', 'emf', 'emf', 'Enhanced Metafile', 'draw_emf_Export') ### 15 430 | fmts.add('graphics', 'eps', 'eps', 'Encapsulated PostScript', 'draw_eps_Export') ### 48 431 | fmts.add('graphics', 'fodg', 'fodg', 'OpenDocument Drawing (Flat XML)', 'OpenDocument Drawing Flat XML') 432 | fmts.add('graphics', 'gif', 'gif', 'Graphics Interchange Format', 'draw_gif_Export') ### 30 433 | fmts.add('graphics', 'html', 'html', 'HTML Document (OpenOffice.org Draw)', 'draw_html_Export') ### 37 434 | fmts.add('graphics', 'jpg', 'jpg', 'Joint Photographic Experts Group', 'draw_jpg_Export') ### 3 435 | fmts.add('graphics', 'met', 'met', 'OS/2 Metafile', 'draw_met_Export') ### 43 436 | fmts.add('graphics', 'odd', 'odd', 'OpenDocument Drawing', 'draw8') ### 6 437 | fmts.add('graphics', 'otg', 'otg', 'OpenDocument Drawing Template', 'draw8_template') ### 20 438 | fmts.add('graphics', 'pbm', 'pbm', 'Portable Bitmap', 'draw_pbm_Export') ### 14 439 | fmts.add('graphics', 'pct', 'pct', 'Mac Pict', 'draw_pct_Export') ### 41 440 | fmts.add('graphics', 'pdf', 'pdf', 'Portable Document Format', 'draw_pdf_Export') ### 28 441 | fmts.add('graphics', 'pgm', 'pgm', 'Portable Graymap', 'draw_pgm_Export') ### 11 442 | fmts.add('graphics', 'png', 'png', 'Portable Network Graphic', 'draw_png_Export') ### 2 443 | fmts.add('graphics', 'ppm', 'ppm', 'Portable Pixelmap', 'draw_ppm_Export') ### 5 444 | fmts.add('graphics', 'ras', 'ras', 'Sun Raster Image', 'draw_ras_Export') ## 31 445 | fmts.add('graphics', 'std', 'std', 'OpenOffice.org 1.0 Drawing Template', 'draw_StarOffice_XML_Draw_Template') ### 53 446 | fmts.add('graphics', 'svg', 'svg', 'Scalable Vector Graphics', 'draw_svg_Export') ### 50 447 | fmts.add('graphics', 'svm', 'svm', 'StarView Metafile', 'draw_svm_Export') ### 55 448 | fmts.add('graphics', 'swf', 'swf', 'Macromedia Flash (SWF)', 'draw_flash_Export') ### 23 449 | fmts.add('graphics', 'sxd', 'sxd', 'OpenOffice.org 1.0 Drawing', 'StarOffice XML (Draw)') ### 26 450 | fmts.add('graphics', 'sxd3', 'sxd', 'StarDraw 3.0', 'StarDraw 3.0') ### 40 451 | fmts.add('graphics', 'sxd5', 'sxd', 'StarDraw 5.0', 'StarDraw 5.0') ### 44 452 | fmts.add('graphics', 'sxw', 'sxw', 'StarOffice XML (Draw)', 'StarOffice XML (Draw)') 453 | fmts.add('graphics', 'tiff', 'tiff', 'Tagged Image File Format', 'draw_tif_Export') ### 13 454 | fmts.add('graphics', 'vor', 'vor', 'StarDraw 5.0 Template', 'StarDraw 5.0 Vorlage') ### 36 455 | fmts.add('graphics', 'vor3', 'vor', 'StarDraw 3.0 Template', 'StarDraw 3.0 Vorlage') ### 35 456 | fmts.add('graphics', 'wmf', 'wmf', 'Windows Metafile', 'draw_wmf_Export') ### 8 457 | fmts.add('graphics', 'xhtml', 'xhtml', 'XHTML', 'XHTML Draw File') ### 45 458 | fmts.add('graphics', 'xpm', 'xpm', 'X PixMap', 'draw_xpm_Export') ### 19 459 | 460 | ### Presentation 461 | fmts.add('presentation', 'bmp', 'bmp', 'Windows Bitmap', 'impress_bmp_Export') ### 15 462 | fmts.add('presentation', 'emf', 'emf', 'Enhanced Metafile', 'impress_emf_Export') ### 16 463 | fmts.add('presentation', 'eps', 'eps', 'Encapsulated PostScript', 'impress_eps_Export') ### 17 464 | fmts.add('presentation', 'fodp', 'fodp', 'OpenDocument Presentation (Flat XML)', 'OpenDocument Presentation Flat XML') 465 | fmts.add('presentation', 'gif', 'gif', 'Graphics Interchange Format', 'impress_gif_Export') ### 18 466 | fmts.add('presentation', 'html', 'html', 'HTML Document (OpenOffice.org Impress)', 'impress_html_Export') ### 43 467 | fmts.add('presentation', 'jpg', 'jpg', 'Joint Photographic Experts Group', 'impress_jpg_Export') ### 19 468 | fmts.add('presentation', 'met', 'met', 'OS/2 Metafile', 'impress_met_Export') ### 20 469 | fmts.add('presentation', 'odg', 'odg', 'ODF Drawing (Impress)', 'impress8_draw') ### 29 470 | fmts.add('presentation', 'odp', 'odp', 'ODF Presentation', 'impress8') ### 9 471 | fmts.add('presentation', 'otp', 'otp', 'ODF Presentation Template', 'impress8_template') ### 38 472 | fmts.add('presentation', 'pbm', 'pbm', 'Portable Bitmap', 'impress_pbm_Export') ### 21 473 | fmts.add('presentation', 'pct', 'pct', 'Mac Pict', 'impress_pct_Export') ### 22 474 | fmts.add('presentation', 'pdf', 'pdf', 'Portable Document Format', 'impress_pdf_Export') ### 23 475 | fmts.add('presentation', 'pgm', 'pgm', 'Portable Graymap', 'impress_pgm_Export') ### 24 476 | fmts.add('presentation', 'png', 'png', 'Portable Network Graphic', 'impress_png_Export') ### 25 477 | fmts.add('presentation', 'potm', 'potm', 'Microsoft PowerPoint 2007/2010 XML Template', 'Impress MS PowerPoint 2007 XML Template') 478 | fmts.add('presentation', 'pot', 'pot', 'Microsoft PowerPoint 97/2000/XP Template', 'MS PowerPoint 97 Vorlage') ### 3 479 | fmts.add('presentation', 'ppm', 'ppm', 'Portable Pixelmap', 'impress_ppm_Export') ### 26 480 | fmts.add('presentation', 'pptx', 'pptx', 'Microsoft PowerPoint 2007/2010 XML', 'Impress MS PowerPoint 2007 XML') ### 36 481 | fmts.add('presentation', 'pps', 'pps', 'Microsoft PowerPoint 97/2000/XP (Autoplay)', 'MS PowerPoint 97 Autoplay') ### 36 482 | fmts.add('presentation', 'ppt', 'ppt', 'Microsoft PowerPoint 97/2000/XP', 'MS PowerPoint 97') ### 36 483 | fmts.add('presentation', 'pwp', 'pwp', 'PlaceWare', 'placeware_Export') ### 30 484 | fmts.add('presentation', 'ras', 'ras', 'Sun Raster Image', 'impress_ras_Export') ### 27 485 | fmts.add('presentation', 'sda', 'sda', 'StarDraw 5.0 (OpenOffice.org Impress)', 'StarDraw 5.0 (StarImpress)') ### 8 486 | fmts.add('presentation', 'sdd', 'sdd', 'StarImpress 5.0', 'StarImpress 5.0') ### 6 487 | fmts.add('presentation', 'sdd3', 'sdd', 'StarDraw 3.0 (OpenOffice.org Impress)', 'StarDraw 3.0 (StarImpress)') ### 42 488 | fmts.add('presentation', 'sdd4', 'sdd', 'StarImpress 4.0', 'StarImpress 4.0') ### 37 489 | fmts.add('presentation', 'sxd', 'sxd', 'OpenOffice.org 1.0 Drawing (OpenOffice.org Impress)', 'impress_StarOffice_XML_Draw') ### 31 490 | fmts.add('presentation', 'sti', 'sti', 'OpenOffice.org 1.0 Presentation Template', 'impress_StarOffice_XML_Impress_Template') ### 5 491 | fmts.add('presentation', 'svg', 'svg', 'Scalable Vector Graphics', 'impress_svg_Export') ### 14 492 | fmts.add('presentation', 'svm', 'svm', 'StarView Metafile', 'impress_svm_Export') ### 13 493 | fmts.add('presentation', 'swf', 'swf', 'Macromedia Flash (SWF)', 'impress_flash_Export') ### 34 494 | fmts.add('presentation', 'sxi', 'sxi', 'OpenOffice.org 1.0 Presentation', 'StarOffice XML (Impress)') ### 41 495 | fmts.add('presentation', 'tiff', 'tiff', 'Tagged Image File Format', 'impress_tif_Export') ### 12 496 | fmts.add('presentation', 'uop', 'uop', 'Unified Office Format presentation', 'UOF presentation') ### 4 497 | fmts.add('presentation', 'vor', 'vor', 'StarImpress 5.0 Template', 'StarImpress 5.0 Vorlage') ### 40 498 | fmts.add('presentation', 'vor3', 'vor', 'StarDraw 3.0 Template (OpenOffice.org Impress)', 'StarDraw 3.0 Vorlage (StarImpress)') ###1 499 | fmts.add('presentation', 'vor4', 'vor', 'StarImpress 4.0 Template', 'StarImpress 4.0 Vorlage') ### 39 500 | fmts.add('presentation', 'vor5', 'vor', 'StarDraw 5.0 Template (OpenOffice.org Impress)', 'StarDraw 5.0 Vorlage (StarImpress)') ### 2 501 | fmts.add('presentation', 'wmf', 'wmf', 'Windows Metafile', 'impress_wmf_Export') ### 11 502 | fmts.add('presentation', 'xhtml', 'xml', 'XHTML', 'XHTML Impress File') ### 33 503 | fmts.add('presentation', 'xpm', 'xpm', 'X PixMap', 'impress_xpm_Export') ### 10 504 | 505 | class Options: 506 | def __init__(self, args): 507 | self.connection = None 508 | self.debug = False 509 | self.doctype = None 510 | self.exportfilter = [] 511 | self.exportfilteroptions = "" 512 | self.filenames = [] 513 | self.format = None 514 | self.importfilter = [] 515 | self.importfilteroptions = "" 516 | self.listener = False 517 | self.nolaunch = False 518 | self.output = None 519 | self.password = None 520 | self.pipe = None 521 | self.port = '2002' 522 | self.server = '127.0.0.1' 523 | self.showlist = False 524 | self.stdout = False 525 | self.template = None 526 | self.timeout = 6 527 | self.verbose = 0 528 | 529 | ### Get options from the commandline 530 | try: 531 | opts, args = getopt.getopt (args, 'c:Dd:e:f:hi:Llo:np:s:T:t:vV', 532 | ['connection=', 'debug', 'doctype=', 'export=', 'format=', 533 | 'help', 'import', 'listener', 'no-launch', 'output=', 534 | 'outputpath', 'password=', 'pipe=', 'port=', 'server=', 535 | 'timeout=', 'show', 'stdout', 'template', 'verbose', 536 | 'version'] ) 537 | except getopt.error as exc: 538 | print('unoconv: %s, try unoconv -h for a list of all the options' % str(exc)) 539 | sys.exit(255) 540 | 541 | for opt, arg in opts: 542 | if opt in ['-h', '--help']: 543 | self.usage() 544 | print() 545 | self.help() 546 | sys.exit(1) 547 | elif opt in ['-c', '--connection']: 548 | self.connection = arg 549 | elif opt in ['--debug']: 550 | self.debug = True 551 | elif opt in ['-d', '--doctype']: 552 | self.doctype = arg 553 | elif opt in ['-e', '--export']: 554 | l = arg.split('=') 555 | if len(l) == 2: 556 | (name, value) = l 557 | if name in ('FilterOptions'): 558 | self.exportfilteroptions = value 559 | elif value in ('True', 'true'): 560 | self.exportfilter.append( PropertyValue( name, 0, True, 0 ) ) 561 | elif value in ('False', 'false'): 562 | self.exportfilter.append( PropertyValue( name, 0, False, 0 ) ) 563 | else: 564 | try: 565 | self.exportfilter.append( PropertyValue( name, 0, int(value), 0 ) ) 566 | except ValueError: 567 | self.exportfilter.append( PropertyValue( name, 0, value, 0 ) ) 568 | else: 569 | print('Warning: Option %s cannot be parsed, ignoring.' % arg, file=sys.stderr) 570 | elif opt in ['-f', '--format']: 571 | self.format = arg 572 | elif opt in ['-i', '--import']: 573 | l = arg.split('=') 574 | if len(l) == 2: 575 | (name, value) = l 576 | if name in ('FilterOptions'): 577 | self.importfilteroptions = value 578 | elif value in ('True', 'true'): 579 | self.importfilter.append( PropertyValue( name, 0, True, 0 ) ) 580 | elif value in ('False', 'false'): 581 | self.importfilter.append( PropertyValue( name, 0, False, 0 ) ) 582 | else: 583 | try: 584 | self.importfilter.append( PropertyValue( name, 0, int(value), 0 ) ) 585 | except ValueError: 586 | self.importfilter.append( PropertyValue( name, 0, value, 0 ) ) 587 | else: 588 | print('Warning: Option %s cannot be parsed, ignoring.' % arg, file=sys.stderr) 589 | elif opt in ['-l', '--listener']: 590 | self.listener = True 591 | elif opt in ['-n', '--no-launch']: 592 | self.nolaunch = True 593 | elif opt in ['-o', '--output']: 594 | self.output = arg 595 | elif opt in ['--outputpath']: 596 | print('Warning: This option is deprecated by --output.', file=sys.stderr) 597 | self.output = arg 598 | elif opt in ['--password']: 599 | self.password = arg 600 | elif opt in ['--pipe']: 601 | self.pipe = arg 602 | elif opt in ['-p', '--port']: 603 | self.port = arg 604 | elif opt in ['-s', '--server']: 605 | self.server = arg 606 | elif opt in ['--show']: 607 | self.showlist = True 608 | elif opt in ['--stdout']: 609 | self.stdout = True 610 | elif opt in ['-t', '--template']: 611 | self.template = arg 612 | elif opt in ['-T', '--timeout']: 613 | self.timeout = int(arg) 614 | elif opt in ['-v', '--verbose']: 615 | self.verbose = self.verbose + 1 616 | elif opt in ['-V', '--version']: 617 | self.version() 618 | sys.exit(255) 619 | 620 | ### Enable verbosity 621 | if self.verbose >= 2: 622 | print('Verbosity set to level %d' % self.verbose, file=sys.stderr) 623 | 624 | self.filenames = args 625 | 626 | if not self.listener and not self.showlist and self.doctype != 'list' and not self.filenames: 627 | print('unoconv: you have to provide a filename as argument', file=sys.stderr) 628 | print('Try `unoconv -h\' for more information.', file=sys.stderr) 629 | sys.exit(255) 630 | 631 | ### Set connection string 632 | if not self.connection: 633 | if not self.pipe: 634 | self.connection = "socket,host=%s,port=%s;urp;StarOffice.ComponentContext" % (self.server, self.port) 635 | # self.connection = "socket,host=%s,port=%s;urp;" % (self.server, self.port) 636 | else: 637 | self.connection = "pipe,name=%s;urp;StarOffice.ComponentContext" % (self.pipe) 638 | 639 | ### Make it easier for people to use a doctype (first letter is enough) 640 | if self.doctype: 641 | for doctype in doctypes: 642 | if doctype.startswith(self.doctype): 643 | self.doctype = doctype 644 | 645 | ### Check if the user request to see the list of formats 646 | if self.showlist or self.format == 'list': 647 | if self.doctype: 648 | fmts.display(self.doctype) 649 | else: 650 | for t in doctypes: 651 | fmts.display(t) 652 | sys.exit(0) 653 | 654 | ### If no format was specified, probe it or provide it 655 | if not self.format: 656 | l = sys.argv[0].split('2') 657 | if len(l) == 2: 658 | self.format = l[1] 659 | else: 660 | self.format = 'pdf' 661 | 662 | def version(self): 663 | ### Get office product information 664 | product = uno.getComponentContext().ServiceManager.createInstance("com.sun.star.configuration.ConfigurationProvider").createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", UnoProps(nodepath="/org.openoffice.Setup/Product")) 665 | 666 | print('unoconv %s' % VERSION) 667 | print('Written by Dag Wieers ') 668 | print('Homepage at http://dag.wieers.com/home-made/unoconv/') 669 | print() 670 | print('platform %s/%s' % (os.name, sys.platform)) 671 | print('python %s' % sys.version) 672 | print(product.ooName, product.ooSetupVersion) 673 | # print 674 | # print 'build revision $Rev$' 675 | 676 | def usage(self): 677 | print('usage: unoconv [options] file [file2 ..]', file=sys.stderr) 678 | 679 | def help(self): 680 | print('''Convert from and to any format supported by LibreOffice 681 | 682 | unoconv options: 683 | -c, --connection=string use a custom connection string 684 | -d, --doctype=type specify document type 685 | (document, graphics, presentation, spreadsheet) 686 | -e, --export=name=value set export filter options 687 | eg. -e PageRange=1-2 688 | -f, --format=format specify the output format 689 | -i, --import=string set import filter option string 690 | eg. -i utf8 691 | -l, --listener start a permanent listener to use by unoconv clients 692 | -n, --no-launch fail if no listener is found (default: launch one) 693 | -o, --output=name output basename, filename or directory 694 | --pipe=name alternative method of connection using a pipe 695 | -p, --port=port specify the port (default: 2002) 696 | to be used by client or listener 697 | --password=string provide a password to decrypt the document 698 | -s, --server=server specify the server address (default: 127.0.0.1) 699 | to be used by client or listener 700 | --show list the available output formats 701 | --stdout write output to stdout 702 | -t, --template=file import the styles from template (.ott) 703 | -T, --timeout=secs timeout after secs if connection to listener fails 704 | -v, --verbose be more and more verbose (-vvv for debugging) 705 | ''', file=sys.stderr) 706 | 707 | class Convertor: 708 | def __init__(self): 709 | global exitcode, ooproc, office, product 710 | unocontext = None 711 | 712 | ### Do the LibreOffice component dance 713 | self.context = uno.getComponentContext() 714 | self.svcmgr = self.context.ServiceManager 715 | resolver = self.svcmgr.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", self.context) 716 | 717 | ### Test for an existing connection 718 | info(3, 'Connection type: %s' % op.connection) 719 | try: 720 | unocontext = resolver.resolve("uno:%s" % op.connection) 721 | except NoConnectException as e: 722 | # info(3, "Existing listener not found.\n%s" % e) 723 | info(3, "Existing listener not found.") 724 | 725 | if op.nolaunch: 726 | die(113, "Existing listener not found. Unable start listener by parameters. Aborting.") 727 | 728 | ### Start our own OpenOffice instance 729 | info(3, "Launching our own listener using %s." % office.binary) 730 | try: 731 | product = self.svcmgr.createInstance("com.sun.star.configuration.ConfigurationProvider").createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", UnoProps(nodepath="/org.openoffice.Setup/Product")) 732 | if product.ooName not in ('LibreOffice', 'LOdev') or LooseVersion(product.ooSetupVersion) <= LooseVersion('3.3'): 733 | ooproc = subprocess.Popen([office.binary, "-headless", "-invisible", "-nocrashreport", "-nodefault", "-nofirststartwizard", "-nologo", "-norestore", "-accept=%s" % op.connection], env=os.environ) 734 | else: 735 | ooproc = subprocess.Popen([office.binary, "--headless", "--invisible", "--nocrashreport", "--nodefault", "--nofirststartwizard", "--nologo", "--norestore", "--accept=%s" % op.connection], env=os.environ) 736 | info(2, '%s listener successfully started. (pid=%s)' % (product.ooName, ooproc.pid)) 737 | 738 | ### Try connection to it for op.timeout seconds (flakky OpenOffice) 739 | timeout = 0 740 | while timeout <= op.timeout: 741 | ### Is it already/still running ? 742 | retcode = ooproc.poll() 743 | if retcode != None: 744 | info(3, "Process %s (pid=%s) exited with %s." % (office.binary, ooproc.pid, retcode)) 745 | break 746 | try: 747 | unocontext = resolver.resolve("uno:%s" % op.connection) 748 | break 749 | except NoConnectException: 750 | time.sleep(0.5) 751 | timeout += 0.5 752 | except: 753 | raise 754 | else: 755 | error("Failed to connect to %s (pid=%s) in %d seconds.\n%s" % (office.binary, ooproc.pid, op.timeout, e)) 756 | except Exception as e: 757 | raise 758 | error("Launch of %s failed.\n%s" % (office.binary, e)) 759 | 760 | if not unocontext: 761 | die(251, "Unable to connect or start own listener. Aborting.") 762 | 763 | ### And some more LibreOffice magic 764 | unosvcmgr = unocontext.ServiceManager 765 | self.desktop = unosvcmgr.createInstanceWithContext("com.sun.star.frame.Desktop", unocontext) 766 | self.cwd = unohelper.systemPathToFileUrl( os.getcwd() ) 767 | 768 | ### List all filters 769 | # self.filters = unosvcmgr.createInstanceWithContext( "com.sun.star.document.FilterFactory", unocontext) 770 | # for filter in self.filters.getElementNames(): 771 | # print filter 772 | # #print dir(filter), dir(filter.format) 773 | 774 | def getformat(self, inputfn): 775 | doctype = None 776 | 777 | ### Get the output format from mapping 778 | if op.doctype: 779 | outputfmt = fmts.bydoctype(op.doctype, op.format) 780 | else: 781 | outputfmt = fmts.byname(op.format) 782 | 783 | if not outputfmt: 784 | outputfmt = fmts.byextension(os.extsep + op.format) 785 | 786 | ### If no doctype given, check list of acceptable formats for input file ext doctype 787 | ### FIXME: This should go into the for-loop to match each individual input filename 788 | if outputfmt: 789 | inputext = os.path.splitext(inputfn)[1] 790 | inputfmt = fmts.byextension(inputext) 791 | if inputfmt: 792 | for fmt in outputfmt: 793 | if inputfmt[0].doctype == fmt.doctype: 794 | doctype = inputfmt[0].doctype 795 | outputfmt = fmt 796 | break 797 | else: 798 | outputfmt = outputfmt[0] 799 | # print >>sys.stderr, 'unoconv: format `%s\' is part of multiple doctypes %s, selecting `%s\'.' % (format, [fmt.doctype for fmt in outputfmt], outputfmt[0].doctype) 800 | else: 801 | outputfmt = outputfmt[0] 802 | 803 | ### No format found, throw error 804 | if not outputfmt: 805 | if doctype: 806 | print('unoconv: format [%s/%s] is not known to unoconv.' % (op.doctype, op.format), file=sys.stderr) 807 | else: 808 | print('unoconv: format [%s] is not known to unoconv.' % op.format, file=sys.stderr) 809 | die(1) 810 | 811 | return outputfmt 812 | 813 | def convert(self, inputfn): 814 | global exitcode 815 | 816 | document = None 817 | outputfmt = self.getformat(inputfn) 818 | 819 | if op.verbose > 0: 820 | print('Input file:', inputfn, file=sys.stderr) 821 | 822 | if not os.path.exists(inputfn): 823 | print('unoconv: file `%s\' does not exist.' % inputfn, file=sys.stderr) 824 | exitcode = 1 825 | 826 | try: 827 | ### Import phase 828 | phase = "import" 829 | 830 | ### Load inputfile 831 | inputprops = UnoProps(Hidden=True, ReadOnly=True, UpdateDocMode=QUIET_UPDATE) 832 | 833 | # if op.password: 834 | # info = UnoProps(algorithm-name="PBKDF2", salt="salt", iteration-count=1024, hash="hash") 835 | # inputprops += UnoProps(ModifyPasswordInfo=info) 836 | 837 | ### Cannot use UnoProps for FilterData property 838 | if op.importfilteroptions: 839 | # print "Import filter options: %s" % op.importfilteroptions 840 | inputprops += UnoProps(FilterOptions=op.importfilteroptions) 841 | 842 | ### Cannot use UnoProps for FilterData property 843 | if op.importfilter: 844 | inputprops += ( PropertyValue( "FilterData", 0, uno.Any("[]com.sun.star.beans.PropertyValue", tuple( op.importfilter ), ), 0 ), ) 845 | 846 | inputurl = unohelper.absolutize(self.cwd, unohelper.systemPathToFileUrl(inputfn)) 847 | document = self.desktop.loadComponentFromURL( inputurl , "_blank", 0, inputprops ) 848 | 849 | if not document: 850 | raise UnoException("The document '%s' could not be opened." % inputurl, None) 851 | 852 | ### Import style template 853 | phase = "import-style" 854 | if op.template: 855 | if os.path.exists(op.template): 856 | info(1, "Template file: %s" % op.template) 857 | templateprops = UnoProps(OverwriteStyles=True) 858 | templateurl = unohelper.absolutize(self.cwd, unohelper.systemPathToFileUrl(op.template)) 859 | document.StyleFamilies.loadStylesFromURL(templateurl, templateprops) 860 | else: 861 | print('unoconv: template file `%s\' does not exist.' % op.template, file=sys.stderr) 862 | exitcode = 1 863 | 864 | ### Update document links 865 | phase = "update-links" 866 | try: 867 | document.updateLinks() 868 | except AttributeError: 869 | # the document doesn't implement the XLinkUpdate interface 870 | pass 871 | 872 | ### Update document indexes 873 | phase = "update-indexes" 874 | for ii in range(2): 875 | # At first update Table-of-Contents. 876 | # ToC grows, so page numbers grows too. 877 | # On second turn update page numbers in ToC. 878 | try: 879 | document.refresh() 880 | indexes = document.getDocumentIndexes() 881 | except AttributeError: 882 | # the document doesn't implement the XRefreshable and/or 883 | # XDocumentIndexesSupplier interfaces 884 | break 885 | else: 886 | for i in range(0, indexes.getCount()): 887 | indexes.getByIndex(i).update() 888 | 889 | info(1, "Selected output format: %s" % outputfmt) 890 | info(2, "Selected office filter: %s" % outputfmt.filter) 891 | info(2, "Used doctype: %s" % outputfmt.doctype) 892 | 893 | ### Export phase 894 | phase = "export" 895 | 896 | outputprops = UnoProps(FilterName=outputfmt.filter, OutputStream=OutputStream(), Overwrite=True) 897 | 898 | ### Set default filter options 899 | if op.exportfilteroptions: 900 | # print "Export filter options: %s" % op.exportfilteroptions 901 | outputprops += UnoProps(FilterOptions=op.exportfilteroptions) 902 | else: 903 | if outputfmt.filter == 'Text (encoded)': 904 | outputprops += UnoProps(FilterOptions="76,LF") 905 | 906 | elif outputfmt.filter == 'Text': 907 | outputprops += UnoProps(FilterOptions="76") 908 | 909 | elif outputfmt.filter == 'Text - txt - csv (StarCalc)': 910 | outputprops += UnoProps(FilterOptions="44,34,76") 911 | 912 | 913 | ### Cannot use UnoProps for FilterData property 914 | if op.exportfilter: 915 | outputprops += ( PropertyValue( "FilterData", 0, uno.Any("[]com.sun.star.beans.PropertyValue", tuple( op.exportfilter ), ), 0 ), ) 916 | 917 | if not op.stdout: 918 | (outputfn, ext) = os.path.splitext(inputfn) 919 | if not op.output: 920 | outputfn = outputfn + os.extsep + outputfmt.extension 921 | elif len(op.filenames) > 1: 922 | outputfn = op.output + os.extsep + outputfmt.extension 923 | else: 924 | outputfn = realpath(op.output, os.path.basename(outputfn) + os.extsep + outputfmt.extension) 925 | 926 | outputurl = unohelper.absolutize( self.cwd, unohelper.systemPathToFileUrl(outputfn) ) 927 | info(1, "Output file: %s" % outputfn) 928 | else: 929 | outputurl = "private:stream" 930 | 931 | try: 932 | document.storeToURL(outputurl, tuple(outputprops) ) 933 | except IOException as e: 934 | raise UnoException("Unable to store document to %s (ErrCode %d)\n\nProperties: %s" % (outputurl, e.ErrCode, outputprops), None) 935 | 936 | phase = "dispose" 937 | document.dispose() 938 | document.close(True) 939 | 940 | except SystemError as e: 941 | error("unoconv: SystemError during %s phase:\n%s" % (phase, e)) 942 | exitcode = 1 943 | 944 | except RuntimeException as e: 945 | error("unoconv: RuntimeException during %s phase:\nOffice probably died. %s" % (phase, e)) 946 | exitcode = 6 947 | 948 | except DisposedException as e: 949 | error("unoconv: DisposedException during %s phase:\nOffice probably died. %s" % (phase, e)) 950 | exitcode = 7 951 | 952 | except IllegalArgumentException as e: 953 | error("UNO IllegalArgument during %s phase:\nSource file cannot be read. %s" % (phase, e)) 954 | exitcode = 8 955 | 956 | except IOException as e: 957 | # for attr in dir(e): print '%s: %s', (attr, getattr(e, attr)) 958 | error("unoconv: IOException during %s phase:\n%s" % (phase, e.Message)) 959 | exitcode = 3 960 | 961 | except CannotConvertException as e: 962 | # for attr in dir(e): print '%s: %s', (attr, getattr(e, attr)) 963 | error("unoconv: CannotConvertException during %s phase:\n%s" % (phase, e.Message)) 964 | exitcode = 4 965 | 966 | except UnoException as e: 967 | if hasattr(e, 'ErrCode'): 968 | error("unoconv: UnoException during %s phase in %s (ErrCode %d)" % (phase, repr(e.__class__), e.ErrCode)) 969 | exitcode = e.ErrCode 970 | pass 971 | if hasattr(e, 'Message'): 972 | error("unoconv: UnoException during %s phase:\n%s" % (phase, e.Message)) 973 | exitcode = 5 974 | else: 975 | error("unoconv: UnoException during %s phase in %s" % (phase, repr(e.__class__))) 976 | exitcode = 2 977 | pass 978 | 979 | class Listener: 980 | def __init__(self): 981 | global product 982 | 983 | info(1, "Start listener on %s:%s" % (op.server, op.port)) 984 | self.context = uno.getComponentContext() 985 | self.svcmgr = self.context.ServiceManager 986 | try: 987 | resolver = self.svcmgr.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", self.context) 988 | product = self.svcmgr.createInstance("com.sun.star.configuration.ConfigurationProvider").createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", UnoProps(nodepath="/org.openoffice.Setup/Product")) 989 | try: 990 | unocontext = resolver.resolve("uno:%s" % op.connection) 991 | except NoConnectException as e: 992 | pass 993 | else: 994 | info(1, "Existing %s listener found, nothing to do." % product.ooName) 995 | return 996 | if product.ooName != "LibreOffice" or LooseVersion(product.ooSetupVersion) <= LooseVersion('3.3'): 997 | subprocess.call([office.binary, "-headless", "-invisible", "-nocrashreport", "-nodefault", "-nologo", "-nofirststartwizard", "-norestore", "-accept=%s" % op.connection], env=os.environ) 998 | else: 999 | subprocess.call([office.binary, "--headless", "--invisible", "--nocrashreport", "--nodefault", "--nologo", "--nofirststartwizard", "--norestore", "--accept=%s" % op.connection], env=os.environ) 1000 | except Exception as e: 1001 | error("Launch of %s failed.\n%s" % (office.binary, e)) 1002 | else: 1003 | info(1, "Existing %s listener found, nothing to do." % product.ooName) 1004 | 1005 | def error(msg): 1006 | "Output error message" 1007 | print(msg, file=sys.stderr) 1008 | 1009 | def info(level, msg): 1010 | "Output info message" 1011 | if 'op' not in globals(): 1012 | pass 1013 | elif op.verbose >= 3 and level >= 3: 1014 | print("DEBUG:", msg, file=sys.stderr) 1015 | elif not op.stdout and level <= op.verbose: 1016 | print(msg, file=sys.stdout) 1017 | elif level <= op.verbose: 1018 | print(msg, file=sys.stderr) 1019 | 1020 | def die(ret, msg=None): 1021 | "Print optional error and exit with errorcode" 1022 | global convertor, ooproc, office 1023 | 1024 | if msg: 1025 | error('Error: %s' % msg) 1026 | 1027 | ### Did we start our own listener instance ? 1028 | if not op.listener and ooproc and convertor: 1029 | 1030 | ### If there is a GUI now attached to the instance, disable listener 1031 | if convertor.desktop.getCurrentFrame(): 1032 | info(2, 'Trying to stop %s GUI listener.' % product.ooName) 1033 | try: 1034 | if product.ooName != "LibreOffice" or product.ooSetupVersion <= 3.3: 1035 | subprocess.Popen([office.binary, "-headless", "-invisible", "-nocrashreport", "-nodefault", "-nofirststartwizard", "-nologo", "-norestore", "-unaccept=%s" % op.connection], env=os.environ) 1036 | else: 1037 | subprocess.Popen([office.binary, "--headless", "--invisible", "--nocrashreport", "--nodefault", "--nofirststartwizard", "--nologo", "--norestore", "--unaccept=%s" % op.connection], env=os.environ) 1038 | ooproc.wait() 1039 | info(2, '%s listener successfully disabled.' % product.ooName) 1040 | except Exception as e: 1041 | error("Terminate using %s failed.\n%s" % (office.binary, e)) 1042 | 1043 | ### If there is no GUI attached to the instance, terminate instance 1044 | else: 1045 | info(3, 'Terminating %s instance.' % product.ooName) 1046 | try: 1047 | convertor.desktop.terminate() 1048 | except DisposedException: 1049 | info(2, '%s instance unsuccessfully closed, sending TERM signal.' % product.ooName) 1050 | try: 1051 | ooproc.terminate() 1052 | except AttributeError: 1053 | os.kill(ooproc.pid, 15) 1054 | info(3, 'Waiting for %s instance to exit.' % product.ooName) 1055 | ooproc.wait() 1056 | 1057 | ### LibreOffice processes may get stuck and we have to kill them 1058 | ### Is it still running ? 1059 | if ooproc.poll() == None: 1060 | info(1, '%s instance still running, please investigate...' % product.ooName) 1061 | ooproc.wait() 1062 | info(2, '%s instance unsuccessfully terminated, sending KILL signal.' % product.ooName) 1063 | try: 1064 | ooproc.kill() 1065 | except AttributeError: 1066 | os.kill(ooproc.pid, 9) 1067 | info(3, 'Waiting for %s with pid %s to disappear.' % (ooproc.pid, product.ooName)) 1068 | ooproc.wait() 1069 | 1070 | # allow Python GC to garbage collect pyuno object *before* exit call 1071 | # which avoids random segmentation faults --vpa 1072 | convertor = None 1073 | 1074 | sys.exit(ret) 1075 | 1076 | def main(): 1077 | global convertor, exitcode 1078 | convertor = None 1079 | 1080 | try: 1081 | if op.listener: 1082 | listener = Listener() 1083 | 1084 | if op.filenames: 1085 | convertor = Convertor() 1086 | for inputfn in op.filenames: 1087 | convertor.convert(inputfn) 1088 | 1089 | except NoConnectException as e: 1090 | error("unoconv: could not find an existing connection to LibreOffice at %s:%s." % (op.server, op.port)) 1091 | if op.connection: 1092 | info(0, "Please start an LibreOffice instance on server '%s' by doing:\n\n unoconv --listener --server %s --port %s\n\nor alternatively:\n\n soffice -nologo -nodefault -accept=\"%s\"" % (op.server, op.server, op.port, op.connection)) 1093 | else: 1094 | info(0, "Please start an LibreOffice instance on server '%s' by doing:\n\n unoconv --listener --server %s --port %s\n\nor alternatively:\n\n soffice -nologo -nodefault -accept=\"socket,host=%s,port=%s;urp;\"" % (op.server, op.server, op.port, op.server, op.port)) 1095 | info(0, "Please start an soffice instance on server '%s' by doing:\n\n soffice -nologo -nodefault -accept=\"socket,host=127.0.0.1,port=%s;urp;\"" % (op.server, op.port)) 1096 | exitcode = 1 1097 | # except UnboundLocalError: 1098 | # die(252, "Failed to connect to remote listener.") 1099 | except OSError: 1100 | error("Warning: failed to launch Office suite. Aborting.") 1101 | 1102 | ### Main entrance 1103 | if __name__ == '__main__': 1104 | exitcode = 0 1105 | 1106 | info(3, 'sysname=%s, platform=%s, python=%s, python-version=%s' % (os.name, sys.platform, sys.executable, sys.version)) 1107 | 1108 | for of in find_offices(): 1109 | if of.python != sys.executable and not sys.executable.startswith(of.basepath): 1110 | python_switch(of) 1111 | office_environ(of) 1112 | # debug_office() 1113 | try: 1114 | import uno, unohelper 1115 | office = of 1116 | break 1117 | except: 1118 | # debug_office() 1119 | print("unoconv: Cannot find a suitable pyuno library and python binary combination in %s" % of, file=sys.stderr) 1120 | print("ERROR:", sys.exc_info()[1], file=sys.stderr) 1121 | print(file=sys.stderr) 1122 | else: 1123 | # debug_office() 1124 | print("unoconv: Cannot find a suitable office installation on your system.", file=sys.stderr) 1125 | print("ERROR: Please locate your office installation and send your feedback to:", file=sys.stderr) 1126 | print(" http://github.com/dagwieers/unoconv/issues", file=sys.stderr) 1127 | sys.exit(1) 1128 | 1129 | ### Now that we have found a working pyuno library, let's import some classes 1130 | from com.sun.star.beans import PropertyValue 1131 | from com.sun.star.connection import NoConnectException 1132 | from com.sun.star.document.UpdateDocMode import QUIET_UPDATE 1133 | from com.sun.star.lang import DisposedException, IllegalArgumentException 1134 | from com.sun.star.io import IOException, XOutputStream 1135 | from com.sun.star.script import CannotConvertException 1136 | from com.sun.star.uno import Exception as UnoException 1137 | from com.sun.star.uno import RuntimeException 1138 | 1139 | ### And now that we have those classes, build on them 1140 | class OutputStream( unohelper.Base, XOutputStream ): 1141 | def __init__( self ): 1142 | self.closed = 0 1143 | 1144 | def closeOutput(self): 1145 | self.closed = 1 1146 | 1147 | def writeBytes( self, seq ): 1148 | sys.stdout.buffer.write( seq.value ) 1149 | 1150 | def flush( self ): 1151 | pass 1152 | 1153 | def UnoProps(**args): 1154 | props = [] 1155 | for key in args: 1156 | prop = PropertyValue() 1157 | prop.Name = key 1158 | prop.Value = args[key] 1159 | props.append(prop) 1160 | return tuple(props) 1161 | 1162 | op = Options(sys.argv[1:]) 1163 | 1164 | info(2, "Using office base path: %s" % office.basepath) 1165 | info(2, "Using office binary path: %s" % office.unopath) 1166 | 1167 | try: 1168 | main() 1169 | except KeyboardInterrupt as e: 1170 | die(6, 'Exiting on user request') 1171 | die(exitcode) --------------------------------------------------------------------------------