python-odf (1.3.1+dfsg-1) unstable; urgency=medium

* New upstream version, which allowed to remove all previous patches.
  * Set HTML_TIMESTAMP = NO (Closes: #777635).
  * Added patch which (Closes: #783789).
  * Remove doc/ in source, because of uglified JS.
  * Updated watch file.

# imported from the archive
This commit is contained in:
W. Martin Borgert 2015-07-25 15:36:45 +02:00
commit e06f58a6c6
256 changed files with 96473 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build/
.pyc

202
APACHE-LICENSE-2.0.txt Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

24
ChangeLog Normal file
View File

@ -0,0 +1,24 @@
1.3
A new edition of odfpy to support Python3.
This version has been reworked by Georges Khaznadar <georgesk@debian.org>,
to add Python3 support.
1.2
Support ODF 1.2
past 0.9
Made sure userfield.py works with OpenOffice.org 3.x files.
0.9
Updated to ODF version 1.1
0.8
Added the ability to load a document.
0.7
Refactored command-line tool odfuserfied into odf.userfield as a
library and adapted odfuserfied to use this library.
0.6.1
TODO: collect changes from svn check-in messages
0.6
Implemented subobjects.

339
GPL-LICENSE-2.txt Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) 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
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. 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.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the 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 a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE 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.
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
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision 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, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This 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.

5
MANIFEST.in Normal file
View File

@ -0,0 +1,5 @@
include *.txt api-for-odfpy.odt
recursive-include * *.1 *.docbook Makefile
recursive-include tests *.txt *.py runtests *.odt *.ods *.odp
recursive-include examples *.txt *.py
recursive-include contrib *.txt *.py

93
README.md Normal file
View File

@ -0,0 +1,93 @@
# ODFPY
This is a collection of utility programs written in Python to manipulate
OpenDocument 1.2 files.
How to proceed: Each application has its own directory. In there, look
at the manual pages. The Python-based tools need the odf library. Just
make a symbolic link like this: ln -s ../odf odf
... or type: make
For your own use of the odf library, see api-for-odfpy.odt
## INSTALLATION
First you get the package.
$ git clone https://github.com/eea/odfpy.git
Then you can build and install the library for Python2 and Python3:
```
$ python setup.py build
$ python3 setup.py build
$ su
# python setup.py install
# python3 setup.py install
```
The library is incompatible with PyXML.
## ISSUES
If you run the tests with python3, you will probably see one error.
It is probably a flaw in the command assertRaises of Python3: the
right exception is raised, but it is not correctly identified by
Python3's assertRaises.
## REDISTRIBUTION LICENSE
This project, with the exception of the OpenDocument schemas, are
Copyright (C) 2006-2014, Daniel Carrera, Alex Hudson, Søren Roug,
Thomas Zander, Roman Fordinal, Michael Howitz and Georges Khaznadar.
It is distributed under both GNU General Public License v.2 or (at
your option) any later version or APACHE License v.2.
See GPL-LICENSE-2.txt and APACHE-LICENSE-2.0.txt.
The OpenDocument RelaxNG Schemas are Copyright © OASIS Open 2005. See
the schema files for their copyright notice.
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.
## TODO / IDEAS
* html2odf
Alex Hudson has been contracted to produce a command-line html2odf
converter. It should include support for images, tables, CSS, etc.
He will provide a C# version first, and later a C version.
* odf2pdf
A valuable tool, but one that is hard to do. PDF is an immensely
popular format, but it's tricky to make PDFs. With an odf2pdf tool
available, many developers would use ODF purely for the purpose of
generating a PDF later. The latest idea is to hire KOffice
developers and get them to trim down KOffice into a converter.
* pdf2odf
This conversion is less likely to produce good results, but it
might be worth a shot. Poppler is a pdf library that can convert
PDF into XML. Maybe we can convert that XML to ODF.
http://webcvs.freedesktop.org/poppler/poppler/
* odfclean
A command-line program that removes unused automatic styles,
metadata and track-changes. Some companies might like to send all
out-going files through odfclean to remove any information they
don't want others to see.
* odf2xliff
Create XLIFF extraction and merge application. XLIFF is a OASIS file
for translations. You extract the text strings, send them to the translator
and then import them. It allows you to work on the document in the
meantime and only retranslate the changed parts.
* odfdiff
A program that can generate a diff between two ODF files. Useful for
SVN commit messages. This is very difficult to do. But see:
http://www.manageability.org/blog/stuff/open-source-xml-diff-in-java/view
http://freshmeat.net/projects/xmldiff/
* odfsign
Sign and verify the signature(s) of an ODF document.

BIN
api-for-odfpy.odt Normal file

Binary file not shown.

2312
config.dox Normal file

File diff suppressed because it is too large Load Diff

6
contrib/ODFFile/Makefile Normal file
View File

@ -0,0 +1,6 @@
odf:
ln -s ../../odf
clean:
rm -f odf

213
contrib/ODFFile/ODFFile.py Normal file
View File

@ -0,0 +1,213 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
#Python imports
import zipfile
from odf.odf2xhtml import ODF2XHTML
from odf.namespaces import OFFICENS, TEXTNS, XLINKNS
from odf.opendocument import odmimetypes
#Zope imports
from OFS.Image import File, cookId
from Globals import InitializeClass
from Globals import DTMLFile
from AccessControl import ClassSecurityInfo
from AccessControl.Permissions import view_management_screens, view, change_images_and_files
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
class ODF2XHTMLBody(ODF2XHTML):
def rewritelink(self, imghref):
imghref = imghref.replace("Pictures/","index_html?pict=")
return imghref
manage_addODFFileForm=DTMLFile('dtml/odffileAdd', globals())
def manage_addODFFile(self, id='', file='',title='', precondition='', content_type='', conversion='embedded',
REQUEST=None):
"""Add a new File object.
Creates a new File object 'id' with the contents of 'file'"""
id = str(id)
title = str(title)
conversion = str(conversion)
content_type = str(content_type)
precondition = str(precondition)
suffix = ''
newid, title = cookId(id, title, file)
if id == '' and newid[-4:-2]== '.o' and newid[-2] in ['d','t']:
id = newid[:-4]
suffix = id[-3:]
else:
id = newid
self = self.this()
# First, we create the file without data:
self._setObject(id, ODFFile(id, title, '', suffix, content_type, precondition, conversion))
# Now we "upload" the data. By doing this in two steps, we
# can use a database trick to make the upload more efficient.
if file:
self._getOb(id).manage_upload(file)
if content_type:
self._getOb(id).content_type = content_type
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
class ODFFile(File):
""" ODFFile class """
meta_type = "OpenDocument File"
# icon = 'misc_/ODFFile/presentation'
# manage_options = (
# (File.manage_options[5],
# File.manage_options[3],
# File.manage_options[6],)
# )
security = ClassSecurityInfo()
def __init__(self, id, title, file, suffix, content_type='', precondition='', conversion='embedded'):
""" constructor """
self.xhtml = "<h1>Nothing uploaded</h1>"
self.conversion = conversion
self.suffix = suffix
self._pictures = {}
File.__dict__['__init__'](self, id, title, file, content_type, precondition)
###########################
# ZMI FORMS #
###########################
security.declareProtected(view, 'index_html')
def index_html(self, REQUEST=None, RESPONSE=None):
""" Show the HTML part """
if REQUEST.has_key('pict'):
return self.Pictures(REQUEST['pict'], REQUEST, RESPONSE)
rsp = []
if self.conversion == 'embedded':
rsp.append(self.standard_html_header(self, REQUEST, RESPONSE))
rsp.append(self.xhtml)
if self.conversion == 'embedded':
rsp.append(self.standard_html_footer(self, REQUEST, RESPONSE))
return(''.join(rsp))
manage_editForm = DTMLFile('dtml/odfEdit',globals())
manage_editForm._setName('manage_editForm')
manage=manage_main = manage_editForm
manage_uploadForm = manage_editForm
def manage_edit(self, title, content_type, precondition='',
filedata=None, conversion='none', REQUEST=None):
"""
Changes the title and content type attributes of the OpenDocument File.
"""
ODFFile.inheritedAttribute('manage_edit')(self, title, content_type, precondition, filedata)
conversion = str(conversion)
if self.conversion != conversion:
self.conversion = conversion
self.update_xhtml()
if REQUEST:
message="Saved changes."
return self.manage_main(self,REQUEST,manage_tabs_message=message)
security.declareProtected(change_images_and_files, 'uploadFile')
def uploadFile(self, file):
""" asociates a file to the ODFFile object """
data, size = self._read_data(file)
content_type = self._get_content_type(file, data, self.__name__, 'undefined')
self.update_data(data, content_type, size)
self._p_changed = 1
security.declareProtected(view, 'download')
def download(self, REQUEST, RESPONSE):
""" set for download asociated file """
self.REQUEST.RESPONSE.setHeader('Content-Type', self.content_type)
self.REQUEST.RESPONSE.setHeader('Content-Length', self.size)
self.REQUEST.RESPONSE.setHeader('Content-Disposition', 'attachment;filename="' + self.id() + self.suffix + '"')
return ODFFile.inheritedAttribute('index_html')(self, REQUEST, RESPONSE)
security.declareProtected(view, 'download')
def picture_list(self, REQUEST, RESPONSE):
""" Show list of pictures """
return "\n".join(self._pictures.keys())
security.declareProtected(view, 'download')
def Pictures(self, pict, REQUEST, RESPONSE):
""" set for download asociated file """
suffices = {
'wmf':'image/x-wmf',
'png':'image/png',
'gif':'image/gif',
'jpg':'image/jpeg',
'jpeg':'image/jpeg'
}
suffix = pict[pict.rfind(".")+1:]
ct = suffices.get(suffix,'application/octet-stream')
self.REQUEST.RESPONSE.setHeader('Content-Type', ct)
return self._pictures[pict]
def _save_pictures(self, fd):
self._pictures = {}
z = zipfile.ZipFile(fd)
for zinfo in z.infolist():
if zinfo.filename[0:9] == 'Pictures/':
pictname = zinfo.filename[9:]
self._pictures[pictname] = z.read(zinfo.filename)
z.close()
# private
update_xhtml__roles__=()
def update_xhtml(self):
if self.size == 0:
return
if self.conversion == 'embedded':
odhandler = ODF2XHTMLBody(embedable=True)
else:
odhandler = ODF2XHTMLBody(embedable=False)
fd = StringIO(str(self.data))
self._save_pictures(fd)
fd.seek(0)
self.xhtml = odhandler.odf2xhtml(fd).encode('us-ascii','xmlcharrefreplace')
self.title = odhandler.title
update_data__roles__=()
def update_data(self, data, content_type=None, size=None):
File.__dict__['update_data'](self, data, content_type, size)
suffix = odmimetypes.get(content_type)
if suffix:
self.suffix = suffix
self.update_xhtml()
InitializeClass(ODFFile)

View File

@ -0,0 +1,3 @@
ODFFile is a Zope 2.x product that will accept an ODT file, and show it as HTML incl.
images. It can optionally put the standard_html_header and standard_html_footer around the
content.

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007-2008 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
#Zope imports
from ODFFile import ODFFile, manage_addODFFileForm, manage_addODFFile
from AccessControl.Permissions import add_documents_images_and_files
#from App.ImageFile import ImageFile
def initialize(context):
""" initialize the ODFFile component """
#register classes
context.registerClass(
ODFFile,
permission=add_documents_images_and_files,
constructors = (manage_addODFFileForm, manage_addODFFile),
icon = 'images/openofficeorg-oasis-text.gif'
)
context.registerHelp()
context.registerHelpTitle('ODFFile')
#misc_ = {
# 'text':ImageFile('images/openofficeorg-oasis-text.gif', globals()),
# 'presentation':ImageFile('images/openofficeorg-oasis-presentation.gif', globals()),
# 'spreadsheet':ImageFile('images/openofficeorg-oasis-spreadsheet.gif', globals())
# }

View File

@ -0,0 +1,149 @@
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
You can update the data for this ODF file object using the form below.
Select a data file from your local computer by clicking the <em>browse</em>
button and click <em>upload</em> to update the contents of the
file. You may also edit the file content directly if the content is a
text type and small enough to be edited in a text area.
</p>
<form action="&dtml-URL1;" method="post" enctype="multipart/form-data">
<table cellpadding="2" cellspacing="0" width="100%" border="0">
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" value="<dtml-if
title>&dtml-title;</dtml-if>">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Content Type
</div>
</td>
<td align="left" valign="top">
<input type="text" name="content_type:required" size="40" value="<dtml-if
content_type>&dtml-content_type;</dtml-if>">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Precondition
</div>
</td>
<td align="left" valign="top">
<input type="text" name="precondition" size="40" value="<dtml-if
precondition>&dtml-precondition;</dtml-if>">
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Conversion
</div>
</td>
<td align="left" valign="top">
<input type="radio" name="conversion" id="noconv" value="none" <dtml-if "conversion == 'none'">checked="checked"</dtml-if>/> <label for="noconv">No conversion</label>
<input type="radio" name="conversion" id="rawhtml" value="rawhtml" <dtml-if "conversion == 'rawhtml'">checked="checked"</dtml-if>/> <label for="rawhtml">Raw html</label>
<input type="radio" name="conversion" id="embedded" value="embedded" <dtml-if "conversion == 'embedded'">checked="checked"</dtml-if>/> <label for="embedded">With site header/footer</label>
</td>
</tr>
<dtml-let ct=getContentType>
<tr>
<td align="left" valign="top">
<div class="form-label">
Last Modified
</div>
</td>
<td align="left" valign="top">
<div class="form-text">
<dtml-var bobobase_modification_time fmt="%Y-%m-%d %H:%M">
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
File Size
</div>
</td>
<td align="left" valign="top">
<div class="form-text">
<dtml-var size thousands_commas> bytes
</div>
</td>
</tr>
</dtml-let>
<tr>
<td></td>
<td align="left" valign="top">
<div class="form-element">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="manage_edit:method"
value="Save Changes">
</dtml-if>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<br />
<div class="form-label">
File Data
</div>
</td>
<td align="left" valign="top">
<br />
<input type="file" name="file" size="25" />
</td>
</tr>
<tr>
<td></td>
<td align="left" valign="top">
<div class="form-element">
<dtml-if wl_isLocked>
<em>Locked by WebDAV</em>
<dtml-else>
<input class="form-element" type="submit" name="manage_upload:method"
value="Upload">
</dtml-if>
</div>
</td>
</tr>
<tr>
<td align="left" valign="top">
<br />
<div class="form-label">
Orig. Data
</div>
</td>
<td align="left" valign="top">
<br />
<a href="&dtml-absolute_url;/download">Download original</a>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>

View File

@ -0,0 +1,69 @@
<dtml-var manage_page_header>
<dtml-var expr="manage_form_title(this(), _,form_title='Add ODF File',)">
<p class="form-help">
Select a file to upload from your local computer by clicking the
<em>Browse</em> button.
</p>
<form action="manage_addODFFile" method="post"
enctype="multipart/form-data">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Conversion
</div>
</td>
<td align="left" valign="top">
<input type="radio" name="conversion" id="noconv" value="none"/> <label for="noconv">No conversion</label>
<input type="radio" name="conversion" id="rawhtml" value="rawhtml"/> <label for="rawhtml">Raw html</label>
<input type="radio" name="conversion" id="embedded" value="embedded" checked="checked"/> <label for="embedded">With site header/footer</label>
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
File
</div>
</td>
<td align="left" valign="top">
<input type="file" name="file" size="25" value="" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>

View File

@ -0,0 +1,5 @@
OpenDocument File: Use OpenDocument as web content management
OpenDocument File will accept an ODF file and show it as HTML incl.
images. It can optionally put the standard_html_header and
standard_html_footer around the content.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

View File

@ -0,0 +1 @@
0.1

View File

@ -0,0 +1,14 @@
all: odf gbtext2odt.1
txt: gbtext2odt.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

View File

@ -0,0 +1,76 @@
.\" Title: gbtext2odt
.\" Author:
.\" Generator: DocBook XSL Stylesheets v1.72.0 <http://docbook.sf.net/>
.\" Date: 09/01/2007
.\" Manual:
.\" Source:
.\"
.TH "GBTEXT2ODT" "1" "09/01/2007" "" ""
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.SH "NAME"
gbtext2odt \- Create OpenDocument from Project Gutenberg text
.SH "SYNOPSIS"
.HP 11
\fBgbtext2odt\fR [\-e\ \fIencoding\fR] [\-a\ \fIauthor\fR] [\-c\ \fIcreation\ date\fR] [\-l\ \fIlanguage\fR] [\-p\ \fIpublisher\fR] [\-t\ \fItitle\fR] [\-T] [\fIinputfile\fR]
.SH "DESCRIPTION"
.PP
Project Gutenberg is the first and largest single collection of free electronic books, or eBooks. The project started in 1971, and the chosen format is "Plain Vanilla ASCII," and this makes the text frustrating to read. Therefore the gbtext2odt program will convert such a text to OpenDocument and add some light markup. The idea behind the program is to test the feasibility of using OpenDocument for archival of documents.
.PP
"Inputfile" is assumed to be an eBook from Project Gutenberg in text form. Books work pretty well, whereas plays, such as
\fIRomeo and Juliet\fR, will probably be messed up.
.SH "OPTIONS"
.PP
\-e \fIencoding\fR
.RS 4
Enter the encoding of the source eBook. Common encodings are: iso\-8859\-1, cp1252 (default), ascii and utf\-8
.RE
.PP
\-a \fIauthor\fR
.RS 4
The name of the author. Entered into the metadata.
.RE
.PP
\-c \fIcreation date\fR
.RS 4
The date of the creation. Entered into the metadata. This can be the date of conversion, or the date the author completed his document. The format must be in ISO 8601 format. I.e. YYYY\-MM\-DD or YYYY\-MM\-DDTHH:MM:SS.
.RE
.PP
\-l \fIlanguage\fR
.RS 4
Language of the eBook. It consists of a two or three letter language code taken from the ISO 639 standard optionally followed by a hyphen and a two\-letter country code.
.RE
.PP
\-p \fIpublisher\fR
.RS 4
The name of the publisher. Entered into the metadata. Defaults to Gutenberg Project
.RE
.PP
\-t \fItitle\fR
.RS 4
The title of the document. Entered into the metadata.
.RE
.PP
\-T
.RS 4
Use the title as the output filename, rather than based on the input filename.
.RE
.SH "EXAMPLE"
.PP
Conversion of Herodotus\(cq Histories from around 430 BC. Known from the movie
[The English Patient].
.sp
.RS 4
.nf
wget http://www.gutenberg.org/dirs/etext01/1hofh10.txt
gbtext2odt \-e cp1252 \-t "The history of Herodotus \(em Volume 1" \-a Herodotus \-l en \-T 1hofh10.txt
.fi
.RE
.SH "SEE ALSO"
.PP
http://www.gutenberg.org
.SH "ISSUES"
.PP
OpenOffice doesn't handle creation dates before the year 1000.

View File

@ -0,0 +1,159 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="gbtext2odt">
<refmeta>
<refentrytitle>gbtext2odt</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>gbtext2odt</refname>
<refpurpose>Create OpenDocument from Project Gutenberg text</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>gbtext2odt</command>
<arg choice="opt">-e <replaceable>encoding</replaceable></arg>
<arg choice="opt">-a <replaceable>author</replaceable></arg>
<arg choice="opt">-c <replaceable>creation date</replaceable></arg>
<arg choice="opt">-l <replaceable>language</replaceable></arg>
<arg choice="opt">-n <replaceable>etext</replaceable></arg>
<arg choice="opt">-p <replaceable>publisher</replaceable></arg>
<arg choice="opt">-t <replaceable>title</replaceable></arg>
<arg choice="opt">-T</arg>
<arg choice="opt"><replaceable>inputfile</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para>
Project Gutenberg is the first and largest single collection of free
electronic books, or eBooks.
The project started in 1971, and the chosen format is
"Plain Vanilla ASCII," and this makes the text frustrating to read.
Therefore the gbtext2odt program will convert such a text to
OpenDocument and add some light markup.
The idea behind the program is to test the feasibility of using
OpenDocument for archival of documents.
</para>
<para>
&quot;Inputfile&quot; is assumed to be an eBook from Project
Gutenberg in text form. Books work pretty well, whereas plays,
such as <emphasis>Romeo and Juliet</emphasis>, will probably be
messed up.
</para>
</refsect1>
<refsect1><title>Options</title>
<variablelist>
<varlistentry>
<term>-e <replaceable>encoding</replaceable></term>
<listitem>
<para>
Enter the encoding of the source eBook. Common encodings are: iso-8859-1,
cp1252 (default), ascii and utf-8
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-a <replaceable>author</replaceable></term>
<listitem>
<para>
The name of the author. Entered into the metadata.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-c <replaceable>creation date</replaceable></term>
<listitem>
<para>
The date of the creation. Entered into the metadata.
This can be the date of conversion, or the date the author completed his document.
The format must be in ISO 8601 format. I.e. YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-l <replaceable>language</replaceable></term>
<listitem>
<para>
Language of the eBook. It consists of a two or three letter language code
taken from the ISO 639 standard optionally
followed by a hyphen and a two-letter country code.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-n <replaceable>etext</replaceable></term>
<listitem>
<para>
Adds the Gutenberg E-text number to the metadata.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-p <replaceable>publisher</replaceable></term>
<listitem>
<para>
The name of the publisher. Entered into the metadata.
Defaults to Gutenberg Project
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-t <replaceable>title</replaceable></term>
<listitem>
<para>
The title of the document. Entered into the metadata.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-T</term>
<listitem>
<para>
Use the title as the output filename, rather than based on the input filename.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1><title>Example</title>
<para>
Conversion of <citetitle>Herodotus Histories</citetitle> from around 430 BCE. Known from the movie
<citetitle>The English Patient</citetitle>. The OpenDocument standard doesn't understand creation dates
that are before common era, so we won't add the publication date to the meta data.
</para>
<screen>
wget http://www.gutenberg.org/dirs/etext01/1hofh10.txt
gbtext2odt -e cp1252 -t "The history of Herodotus — Volume 1" -a Herodotus -l en -T 1hofh10.txt
</screen>
</refsect1>
<refsect1><title>See Also</title>
<para>
http://www.gutenberg.org
</para>
</refsect1>
<refsect1><title>Issues</title>
<para>
OpenOffice doesn't handle creation dates before the year 1000.
</para>
</refsect1>
</refentry>

269
contrib/gutenberg/gbtext2odt.py Executable file
View File

@ -0,0 +1,269 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odf.opendocument import OpenDocumentText
from odf import style, text, dc, meta
import sys, getopt, time
def usage():
sys.stderr.write("""Usage: %s [-l language] [-e encoding] [-T] [-a author]
\t[-c creation_date] [-d description] [-n etext] [-p publisher] [-t title] inputfile\n""" % sys.argv[0])
try:
opts, args = getopt.getopt(sys.argv[1:], "a:n:c:d:e:l:p:t:T", ["author=",
"date=", "created=", "description=", "number=", "title=",
"language=", "publisher=", "encoding="])
except getopt.GetoptError:
usage()
sys.exit(2)
language = None
description = None
encoding = 'cp1252' # Codepage 1252 is a superset of ASCII and ISO-8859-1
argencoding = 'utf-8'
creator = ""
creationdate = None
title = ""
ebooknum = None
publisher = "Project Gutenberg"
copyrights = "http://www.gutenberg.org/license"
fn_is_title = False
for o, a in opts:
if o in ("-l", "--language"):
if len(a) > 3 and a[2] != '-' and a[3] != '-' or len(a) > 6:
sys.stderr.write("""Language must be a two or three letter language code optionally
\tfollowed by a hyphen and a two-letter country code""")
sys.exit(2)
language = a
elif o in ("-e", "--encoding"):
encoding = a
elif o in ("-a", "--author"):
creator = unicode(a, argencoding)
elif o in ("-d", "--description"):
description = a
elif o in ("-c", "--date", "--created"):
if len(a) > 10 and a[10] != "T":
sys.stderr.write("""Date must be in ISO8601 format (YYYY-MM-DDTHH:MM:SS)\n""")
sys.exit(2)
if len(a) < 10 or (len(a) == 10 and a[4] != "-" and a[7] != "-"):
sys.stderr.write("""Date must be in ISO8601 format (YYYY-MM-DD)\n""")
sys.exit(2)
creationdate = a
elif o in ("-p", "--publisher"):
publisher = a
elif o in ("-n", "--number"):
ebooknum = unicode(a, argencoding)
elif o in ("-t", "--title"):
title = unicode(a, argencoding)
elif o == "-T":
fn_is_title = True
if len(args) != 1:
usage()
sys.exit(2)
doc=OpenDocumentText()
textdoc = doc.text
if creator != "":
doc.meta.addElement(meta.InitialCreator(text=creator))
doc.meta.addElement(dc.Creator(text=creator))
if creationdate is not None:
doc.meta.addElement(meta.CreationDate(text=creationdate))
doc.meta.addElement(dc.Date(text=creationdate))
if description is not None:
doc.meta.addElement(dc.Description(text=description))
if title != "":
doc.meta.addElement(dc.Title(text=title))
if language is not None:
doc.meta.addElement(dc.Language(text=language))
if publisher is not None:
# doc.meta.addElement(dc.Publisher(text=publisher))
doc.meta.addElement(meta.UserDefined(name="Publisher", text=publisher))
if copyrights is not None:
# doc.meta.addElement(dc.Rights(text=copyrights))
doc.meta.addElement(meta.UserDefined(name="Rights", text=copyrights))
if ebooknum is not None:
doc.meta.addElement(meta.UserDefined(name="EText", text=ebooknum))
arial = style.FontFace(name="Arial", fontfamily="Arial", fontfamilygeneric="swiss", fontpitch="variable")
doc.fontfacedecls.addElement(arial)
# Paragraph styles
standardstyle = style.Style(name="Standard", family="paragraph")
standardstyle.addElement(style.ParagraphProperties(marginbottom="0cm", margintop="0cm" ))
doc.styles.addElement(standardstyle)
h1style = style.Style(name="Heading 1", family="paragraph", defaultoutlinelevel="1")
h1style.addElement(style.TextProperties(attributes={'fontsize':"20pt", 'fontweight':"bold"}))
doc.styles.addElement(h1style)
textbodystyle = style.Style(name="Text body", family="paragraph", parentstylename=standardstyle)
textbodystyle.addElement(style.ParagraphProperties(attributes={'marginbottom':"0.212cm", 'margintop':"0cm",
'textalign':"justify", 'justifysingleword':"false"}))
doc.styles.addElement(textbodystyle)
subtitlestyle = style.Style(name="Subtitle", family="paragraph", nextstylename=textbodystyle)
subtitlestyle.addElement(style.ParagraphProperties(textalign="center") )
subtitlestyle.addElement(style.TextProperties(fontsize="14pt", fontstyle="italic", fontname="Arial"))
doc.styles.addElement(subtitlestyle)
titlestyle = style.Style(name="Title", family="paragraph", nextstylename=subtitlestyle)
titlestyle.addElement(style.ParagraphProperties(textalign="center") )
titlestyle.addElement(style.TextProperties(fontsize="18pt", fontweight="bold", fontname="Arial"))
doc.styles.addElement(titlestyle)
# Text styles
emphasisstyle = style.Style(name="Emphasis",family="text")
emphasisstyle.addElement(style.TextProperties(fontstyle="italic"))
doc.styles.addElement(emphasisstyle)
# Make the Gutenberg sections grey
sectstyle = style.Style(name="Sect1", family="section")
sectstyle.addElement(style.SectionProperties(backgroundcolor="#e6e6e6"))
doc.automaticstyles.addElement(sectstyle)
FULLLINE=55
paragraph=[]
def addparagraph(section):
""" Join the paragraph list and add it to the section
"""
global paragraph
p = ' '.join(paragraph)
textsegs = p.split('_')
para = text.P(stylename=textbodystyle)
section.addElement(para)
if len(textsegs) > 1 and (len(textsegs) % 2) == 1:
# We have found some kursive text segments
for i in range(len(textsegs)):
if len(textsegs[i]) > 0:
if (i % 2) == 1:
y = text.Span(stylename=emphasisstyle, text=textsegs[i])
para.addElement(y)
else:
para.addText(textsegs[i])
else:
para.addText(p)
def cleantext(s):
if s[0] == '"' or s[-1] == '"':
ls=list(s)
if ls[0] == '"': ls[0] = u''
if ls[-1] == '"': ls[-1] = u''
s = ''.join(ls)
s = s.replace('" ',u'')
s = s.replace(' "',u'')
s = s.replace("'m",u"m") # I'm
s = s.replace("'s",u"s") # genitive case
s = s.replace("'t",u"t") # don't, doesn't, haven't
s = s.replace("'S",u"S") # genitive case
s = s.replace("'T",u"T") # DON'T, etc
s = s.replace("l'",u"l") # French
s = s.replace("d'",u"d") # French
if s.find('---') < 0: # Don't replace double dash for lines
s = s.replace('--',u'')
return s
def pretext(section, line, linelen):
section.addElement(text.P(stylename=standardstyle, text=line))
def posttext(section, line, linelen):
section.addElement(text.P(stylename=standardstyle, text=line))
def mainpart(section, line, linelen):
global paragraph
if linelen > 0 and len(paragraph) == 0 and \
line.upper() == line and line.upper() != line.lower():
# Headlines are always upper case
style = h1style
l = cleantext(line)
section.addElement(text.H(outlinelevel=1, stylename=h1style, text=l))
elif linelen >= FULLLINE:
# In the middle of a paragraph
paragraph.append(cleantext(line))
elif linelen == 0:
# End of paragraph
if len(paragraph) > 0:
addparagraph(section)
paragraph=[]
elif linelen < FULLLINE and len(paragraph) > 0:
# Short tail of paragraph
paragraph.append(cleantext(line))
else:
if line == title or line == title + " by " + creator:
section.addElement(text.P( stylename=titlestyle, text=cleantext(line)))
return
if line == "by" or line == creator:
section.addElement(text.P( stylename=subtitlestyle, text=cleantext(line)))
return
if len(paragraph) > 0:
addparagraph(section)
paragraph=[]
section.addElement(text.P(stylename=textbodystyle, text=cleantext(line)))
PRETEXT = 1
MAINPART = 2
POSTTEXT = 3
textpart = PRETEXT
# Start in the preamble
section = text.Section(stylename=sectstyle, name="preamble") #, display="none")
textdoc.addElement(section)
filename = args[0]
if fn_is_title and title is not None and title != "":
outfn = title
else:
suffixi = filename.rfind(".")
if suffixi > 1:
outfn = filename[:suffixi]
else:
outfn = "interimname"
f = open(filename)
for rawline in f:
line = unicode(rawline.strip(), encoding)
linelen = len(line)
if line.find("*** END OF TH") == 0:
textpart = POSTTEXT
section = text.Section(stylename=sectstyle, name="license") #, display="none")
textdoc.addElement(section)
if textpart == PRETEXT:
pretext(section, line, linelen)
if line.find("*** START OF TH") == 0 or \
line.find("*END THE SMALL PRINT!") == 0 or \
line.find("*END*THE SMALL PRINT!") == 0:
textpart = MAINPART
elif textpart == MAINPART:
section = textdoc
mainpart(section, line, linelen)
else:
posttext(section, line, linelen)
# print d.contentxml()
doc.save(outfn, True)

View File

@ -0,0 +1,8 @@
all: fellowship.content
fellowship.content: html2odt.py
python html2odt.py http://opendocumentfellowship.org/ >fellowship.content
clean:
rm -f *.content *.meta *.styles

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
chead = ['''<?xml version="1.0" encoding="UTF-8"?>\n''',
'''<office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" office:version="1.0">''',
'''<office:scripts/>''',
'''<office:font-face-decls>''',
'''<style:font-face style:name="Luxi Sans1" svg:font-family="&apos;Luxi Sans&apos;" style:font-pitch="variable"/>''',
'''<style:font-face style:name="Nimbus Roman No9 L" svg:font-family="&apos;Nimbus Roman No9 L&apos;" style:font-family-generic="roman" style:font-pitch="variable"/>''',
'''<style:font-face style:name="Luxi Sans" svg:font-family="&apos;Luxi Sans&apos;" style:font-family-generic="swiss" style:font-pitch="variable"/>''',
'''</office:font-face-decls>''',
'''<office:automatic-styles/>''',
'''<office:body>''',
'''<office:text>''',
'''<office:forms form:automatic-focus="false" form:apply-design-mode="false"/>''',
'''<text:sequence-decls>''',
'''<text:sequence-decl text:display-outline-level="0" text:name="Illustration"/>''',
'''<text:sequence-decl text:display-outline-level="0" text:name="Table"/>''',
'''<text:sequence-decl text:display-outline-level="0" text:name="Text"/>''',
'''<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>''',
'''</text:sequence-decls>''']
cmiddle = [
'''<text:p text:style-name="Standard"/>''',]
cfoot = [ '''</office:text>''',
'''</office:body>''',
'''</office:document-content>''']
def content():
return ''.join(chead + cmiddle + cfoot)

View File

@ -0,0 +1,495 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
#
import string, sys, re
import urllib2, htmlentitydefs, urlparse
from urllib import quote_plus
from HTMLParser import HTMLParser
from cgi import escape,parse_header
from types import StringType
import emptycontent
def checkurl(url, http_proxy=None):
""" grab and convert url
"""
url = string.strip(url)
# if url.lower()[:5] != "http:":
# raise IOError, "Only http is accepted"
if http_proxy:
_proxies = { 'http': http_proxy }
else:
_proxies = {}
proxy_support = urllib2.ProxyHandler(_proxies)
opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
urllib2.install_opener(opener)
req = urllib2.Request(url)
req.add_header("User-agent", "HTML2ODT: Convert HTML to OpenDocument")
conn = urllib2.urlopen(req)
if not conn:
raise IOError, "Failure in open"
data = conn.read()
headers = conn.info()
conn.close()
encoding = 'iso8859-1' #Standard HTML
if headers.has_key('content-type'):
(ct, parms) = parse_header(headers['content-type'])
if parms.has_key('charset'):
encoding = parms['charset']
mhp = HTML2ODTParser(encoding, url)
failure = ""
mhp.feed(data)
text = mhp.result() # Flush the buffer
return text
entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]')
charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]')
incomplete = re.compile('&[a-zA-Z#]')
ampersand = re.compile('&')
def listget(list, key, default=None):
for l in list:
if l[0] == key:
default = l[1]
return default
class TagObject:
def __init__(self, tag, attrs, output_loc):
self.tag = tag
self.attrs = attrs
self.output_loc = output_loc
class HTML2ODTParser(HTMLParser):
def __init__(self, encoding, baseurl):
HTMLParser.__init__(self)
self.encoding = encoding
(scheme, host, path, params, fragment) = urlparse.urlsplit(baseurl)
lastslash = path.rfind('/')
if lastslash > -1:
path = path[:lastslash]
self.baseurl = urlparse.urlunsplit((scheme, host, path,'',''))
self.basehost = urlparse.urlunsplit((scheme, host, '','',''))
self.sectnum = 0
self.tagstack = []
self.pstack = []
self.processelem = True
self.processcont = True
self.__data = []
self.elements = {
'a': (self.s_html_a, self.e_html_a),
'base': ( self.output_base, None),
'br': ( self.output_br, None),
'caption': ( self.output_caption, None),
'col': ( self.s_html_col, None),
'dd': ( self.s_html_dd, None),
'dt': ( self.s_html_dt, None),
'div': ( self.s_html_section, self.e_html_section),
'em': ( self.s_html_emphasis, self.e_html_emphasis),
'h1': ( self.s_html_headline, self.e_html_headline),
'h2': ( self.s_html_headline, self.e_html_headline),
'h3': ( self.s_html_headline, self.e_html_headline),
'h4': ( self.s_html_headline, self.e_html_headline),
'h5': ( self.s_html_headline, self.e_html_headline),
'h6': ( self.s_html_headline, self.e_html_headline),
'head': ( self.s_ignorexml, None),
'img': ( self.output_img, None),
'li': ( self.s_html_li, self.e_html_li),
'meta': ( self.meta_encoding, None),
'ol': ( self.output_ol, self.e_html_list),
'p': ( self.s_html_block, self.e_html_block),
'span': ( self.s_html_span, self.e_html_span),
'strong':( self.s_html_emphasis, self.e_html_emphasis),
'table':( self.s_html_table, self.e_html_table),
'td': ( self.s_html_td, self.e_html_td),
'th': ( self.s_html_td, self.e_html_td),
'title':( self.s_html_title, self.e_html_title),
'tr': ( self.s_html_tr, self.e_html_tr),
'ul': ( self.output_ul, self.e_html_list),
'input':( self.output_input, None),
'select':( self.output_select, None),
'textarea':( self.output_textarea, None),
}
def result(self):
""" Return a string
String must be in UNICODE
"""
str = string.join(self.__data,'')
self.__data = []
return str
def meta_name(self, attrs):
""" Look in meta tag for textual info"""
foundit = 0
# Is there a name attribute?
for attr in attrs:
if attr[0] == 'name' and string.lower(attr[1]) in ('description',
'keywords','title',
'dc.description','dc.keywords','dc.title'
):
foundit = 1
if foundit == 0:
return 0
# Is there a content attribute?
content = self.find_attr(attrs,'content')
if content:
self.handle_data(u' ')
self.handle_attr(content)
self.handle_data(u' ')
return 1
def meta_encoding(self, tag, attrs):
""" Look in meta tag for page encoding (Content-Type)"""
foundit = 0
# Is there a content-type attribute?
for attr in attrs:
if attr[0] == 'http-equiv' and string.lower(attr[1]) == 'content-type':
foundit = 1
if foundit == 0:
return 0
# Is there a content attribute?
for attr in attrs:
if attr[0] == 'content':
(ct, parms) = parse_header(attr[1])
if parms.has_key('charset'):
self.encoding = parms['charset']
return 1
def s_ignorexml(self, tag, attrs):
self.processelem = False
def output_base(self, tag, attrs):
""" Change the document base if there is a base tag """
baseurl = listget(attrs, 'href', self.baseurl)
(scheme, host, path, params, fragment) = urlparse.urlsplit(baseurl)
lastslash = path.rfind('/')
if lastslash > -1:
path = path[:lastslash]
self.baseurl = urlparse.urlunsplit((scheme, host, path,'',''))
self.basehost = urlparse.urlunsplit((scheme, host, '','',''))
def output_br(self, tag, attrs):
self.write_odt(u'<text:line-break/>')
def s_html_emphasis(self, tag, attrs):
self.write_odt(u'<%s>' % tag)
def e_html_emphasis(self, tag):
self.write_odt(u'</%s>' % tag)
def s_html_span(self, tag, attrs):
self.write_odt(u'<text:span>')
def e_html_span(self, tag):
self.write_odt(u'</text:span>')
def s_html_title(self, tag, attrs):
# Put in meta.xml
self.write_odt(u'<dc:title>')
def e_html_title(self, tag):
# Put in meta.xml
self.write_odt(u'</dc:title>')
def output_img(self, tag, attrs):
src = listget(attrs, 'src', "Illegal IMG tag!")
alt = listget(attrs, 'alt', src)
# Must remember name of image and download it.
self.write_odt(u'<draw:image xlink:href="Pictures/%s" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>' % '00000.png')
def s_html_a(self, tag, attrs):
href = None
href = listget(attrs, 'href', None)
if href:
if href in ("", "#"):
href == self.baseurl
elif href.find("://") >= 0:
pass
elif href[0] == '/':
href = self.basehost + href
self.write_odt(u' <text:a xlink:type="simple" xlink:href="%s">' % escape(href))
else:
self.write_odt(u' <text:a>')
def e_html_a(self, tag):
self.write_odt(u'</text:a>')
def s_html_dd(self, tag, attrs):
self.write_odt(u'<text:p text:style-name="List_20_Contents">')
def s_html_dt(self, tag, attrs):
self.write_odt(u'<text:p text:style-name="List_20_Heading">')
def output_ul(self, tag, attrs):
self.write_odt(u'<text:list text:style-name="List_20_1">')
def output_ol(self, tag, attrs):
self.write_odt(u'<text:list text:style-name="Numbering_20_1">')
def e_html_list(self, tag):
self.write_odt(u'</text:list>')
def s_html_li(self, tag, attrs):
self.write_odt(u'<text:list-item><text:p text:style-name="P1">')
def e_html_li(self, tag):
self.write_odt(u'</text:p></text:list-item>')
def output_select(self, tag, attrs):
return
self.write_odt(u'<br/>Combo box:')
def output_textarea(self, tag, attrs):
return
self.write_odt(u'<form:textarea>')
def output_input(self, tag, attrs):
return
type = listget(attrs, 'type', "text")
value = listget(attrs, 'value', "")
if type == "text":
self.write_odt(u'<br/>Edit:')
elif type == "submit":
self.write_odt(u' %s' % value)
elif type == "checkbox":
#FIXME - Only works in XHTML
checked = listget(attrs, 'checked', "not checked")
self.write_odt(u'<br/>Checkbox:' % checked)
elif type == "radio":
checked = listget(attrs, 'checked', "not checked")
self.write_odt(u'<br/>Radio button:' % checked)
elif type == "file":
self.write_odt(u'File upload edit %s' % value)
self.write_odt(u'<br/>Browse button:')
def s_html_headline(self, tag, attrs):
self.write_odt(u'<text:h text:style-name="Heading_20_%s" text:outline-level="%s">' % (tag[1],tag[1]))
def e_html_headline(self, tag):
self.write_odt(u'</text:h>')
def s_html_table(self, tag, attrs):
self.write_odt(u'<table:table>')
def e_html_table(self, tag):
self.write_odt(u'</table:table>')
def s_html_td(self, tag, attrs):
self.write_odt(u'<table:table-cell>')
def e_html_td(self, tag):
self.write_odt(u'</table:table-cell>')
def s_html_tr(self, tag, attrs):
self.write_odt(u'<table:table-row>')
def e_html_tr(self, tag):
self.write_odt(u'</table:table-row>')
def s_html_col(self, tag, attrs):
self.write_odt(u'<table:table-column/>')
def output_caption(self, tag, attrs):
self.write_odt(u'Caption: ')
def s_html_section(self, tag, attrs):
""" Outputs block tag such as <p> and <div> """
name = self.find_attr(attrs,'id')
if name is None:
self.sectnum = self.sectnum + 1
name = "Sect%d" % self.sectnum
self.write_odt(u'<text:section text:name="%s">' % name)
def e_html_section(self, tag):
""" Outputs block tag such as <p> and <div> """
self.write_odt(u'</text:section>')
def s_html_block(self, tag, attrs):
""" Outputs block tag such as <p> and <div> """
self.write_odt(u'<text:p text:style-name="Text_20_body">')
def e_html_block(self, tag):
""" Outputs block tag such as <p> and <div> """
self.write_odt(u'</text:p>')
#
# HANDLE STARTTAG
#
def handle_starttag(self, tag, attrs):
self.pstack.append( (self.processelem, self.processcont) )
tagobj = TagObject(tag, attrs, self.last_data_pos())
self.tagstack.append(tagobj)
method = self.elements.get(tag, (None, None))[0]
if self.processelem and method:
method(tag, attrs)
#
# HANDLE END
#
def handle_endtag(self, tag):
"""
"""
tagobj = self.tagstack.pop()
method = self.elements.get(tag, (None, None))[1]
if self.processelem and method:
method(tag)
self.processelem, self.processcont = self.pstack.pop()
#
# Data operations
#
def handle_data(self, data):
if self.processelem and self.processcont:
self.write_odt(escape(data))
def write_odt(self, data):
""" Collect the data to show on the webpage """
if type(data) == StringType:
data = unicode(data, self.encoding)
self.__data.append(data)
def last_data_pos(self):
return len(self.__data)
def find_attr(self, attrs, key):
""" Run through the attibutes to find a specific one
return None if not found
"""
for attr in attrs:
if attr[0] == key:
return attr[1]
return None
#
# Tagstack operations
#
def find_tag(self, tag):
""" Run down the stack to find the last entry with the same tag name
Not Tested
"""
for tagitem in range(len(self.tagstack), 0, -1):
if tagitem.tag == tag:
return tagitem
return None
def handle_charref(self, name):
""" Handle character reference for UNICODE
"""
if name[0] in ('x', 'X'):
try:
n = int(name[1:],16)
except ValueError:
return
else:
try:
n = int(name)
except ValueError:
return
if not 0 <= n <= 65535:
return
self.handle_data(unichr(n))
def handle_entityref(self, name):
"""Handle entity references.
"""
table = htmlentitydefs.name2codepoint
if name in table:
self.handle_data(unichr(table[name]))
else:
return
def handle_attr(self, attrval):
""" Scan attribute values for entities and resolve them
Simply calls handle_data
"""
i = 0
n = len(attrval)
while i < n:
match = ampersand.search(attrval, i) #
if match:
j = match.start()
else:
j = n
if i < j: self.handle_data(attrval[i:j])
i = j
if i == n: break
startswith = attrval.startswith
if startswith('&#', i):
match = charref.match(attrval, i)
if match:
name = match.group()[2:-1]
self.handle_charref(name)
k = match.end()
if not startswith(';', k-1):
k = k - 1
i = k
continue
else:
break
elif startswith('&', i):
match = entityref.match(attrval, i)
if match:
name = match.group(1)
self.handle_entityref(name)
k = match.end()
if not startswith(';', k-1):
k = k - 1
i = k
continue
match = incomplete.match(attrval, i)
if match:
# match.group() will contain at least 2 chars
if match.group() == attrval[i:]:
self.error("EOF in middle of entity or char ref")
# incomplete
break
elif (i + 1) < n:
# not the end of the buffer, and can't be confused
# with some other construct
self.handle_data("&")
i = i + 1
else:
break
else:
assert 0, "interesting.search() lied"
# end while
if i < n:
self.handle_data(attrval[i:n])
i = n
if __name__ == "__main__":
import sys
result = checkurl(sys.argv[1])
sys.stdout.write('\n'.join(emptycontent.chead))
sys.stdout.write(result.encode('utf-8'))
sys.stdout.write('\n'.join(emptycontent.cfoot))
sys.stdout.write('\n')

View File

@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odf.style import Style, ParagraphProperties, TextProperties
def addStandardStyles(doc):
style = Style(name="Standard", family="paragraph", attributes={'class':"text"})
doc.styles.addElement(style)
style = Style(name="Text_20_body", displayname="Text body", family="paragraph", parentstylename="Standard", attributes={'class':"text"})
p = ParagraphProperties(margintop="0cm", marginbottom="0.212cm")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="List_20_Contents", displayname="List Contents", family="paragraph", parentstylename="Standard", attributes={'class':"html"})
p = ParagraphProperties(marginleft="1cm", marginright="0cm", textindent="0cm", autotextindent="false")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="List_20_Heading", displayname="List Heading", family="paragraph", parentstylename="Standard",
nextstylename="List_20_Contents", attributes={'class':"html"})
p = ParagraphProperties(marginleft="0cm", marginright="0cm", textindent="0cm", autotextindent="false")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Text_20_body_20_indent", displayname="Text body indent", family="paragraph", parentstylename="Text_20_body", attributes={'class':"text"})
p = ParagraphProperties(marginleft="0.499cm", marginright="0cm", textindent="0cm", autotextindent="false")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Heading", family="paragraph", parentstylename="Standard", nextstylename="Text_20_body", attributes={'class':"text"})
p = ParagraphProperties(margintop="0.423cm", marginbottom="0.212cm", keepwithnext="always")
style.addElement(p)
p = TextProperties(fontname="Nimbus Sans L", fontsize="14pt", fontnameasian="DejaVu LGC Sans", fontsizeasian="14pt", fontnamecomplex="DejaVu LGC Sans", fontsizecomplex="14pt")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Heading_20_1", displayname="Heading 1", family="paragraph", parentstylename="Heading", nextstylename="Text_20_body", attributes={'class':"text"}, defaultoutlinelevel=1)
p = TextProperties(fontsize="115%", fontweight="bold", fontsizeasian="115%", fontweightasian="bold", fontsizecomplex="115%", fontweightcomplex="bold")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Heading_20_2", displayname="Heading 2", family="paragraph", parentstylename="Heading", nextstylename="Text_20_body", attributes={'class':"text"}, defaultoutlinelevel=2)
p = TextProperties(fontsize="14pt", fontstyle="italic", fontweight="bold", fontsizeasian="14pt", fontstyleasian="italic", fontweightasian="bold", fontsizecomplex="14pt", fontstylecomplex="italic", fontweightcomplex="bold")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Heading_20_3", displayname="Heading 3", family="paragraph", parentstylename="Heading", nextstylename="Text_20_body", attributes={'class':"text"}, defaultoutlinelevel=3)
p = TextProperties(fontsize="14pt", fontweight="bold", fontsizeasian="14pt", fontweightasian="bold", fontsizecomplex="14pt", fontweightcomplex="bold")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="List", family="paragraph", parentstylename="Text_20_body", attributes={'class':"list"})
doc.styles.addElement(style)
style = Style(name="Caption", family="paragraph", parentstylename="Standard", attributes={'class':"extra"})
p = ParagraphProperties(margintop="0.212cm", marginbottom="0.212cm", numberlines="false", linenumber="0")
style.addElement(p)
p = TextProperties(fontsize="12pt", fontstyle="italic", fontsizeasian="12pt", fontstyleasian="italic", fontsizecomplex="12pt", fontstylecomplex="italic")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Index", family="paragraph", parentstylename="Standard", attributes={'class':"index"})
p = ParagraphProperties(numberlines="false", linenumber=0)
doc.styles.addElement(style)
style = Style(name="Source_20_Text", displayname="Source Text", family="text")
p = TextProperties(fontname="Courier", fontnameasian="Courier", fontnamecomplex="Courier")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Variable", family="text")
p = TextProperties(fontstyle="italic", fontstyleasian="italic", fontstylecomplex="italic")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Emphasis", family="text")
p = TextProperties(fontstyle="italic", fontstyleasian="italic", fontstylecomplex="italic")
style.addElement(p)
doc.styles.addElement(style)
style = Style(name="Strong_20_Emphasis", displayname="Strong Emphasis", family="text")
p = TextProperties(fontweight="bold", fontweightasian="bold", fontweightcomplex="bold")
style.addElement(p)
doc.styles.addElement(style)
# Automatic styles
style = Style(name="Bold", displayname="Bold", family="text")
p = TextProperties(fontweight="bold", fontweightasian="bold", fontweightcomplex="bold")
style.addElement(p)
doc.automaticstyles.addElement(style)
style = Style(name="Italic", family="text")
p = TextProperties(fontstyle="italic", fontstyleasian="italic", fontstylecomplex="italic")
style.addElement(p)
doc.automaticstyles.addElement(style)

View File

@ -0,0 +1,619 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2008-2009 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
#
import string, sys, re, getopt
import urllib2, htmlentitydefs, urlparse
from urllib import quote_plus
from HTMLParser import HTMLParser
from cgi import escape,parse_header
from types import StringType
from odf.opendocument import OpenDocumentText, load
from odf import dc, text, table
import htmlstyles
def converturl(url, document=None):
""" grab and convert url
"""
url = string.strip(url)
# if url.lower()[:5] != "http:":
# raise IOError, "Only http is accepted"
_proxies = {}
proxy_support = urllib2.ProxyHandler(_proxies)
opener = urllib2.build_opener(proxy_support, urllib2.HTTPHandler)
urllib2.install_opener(opener)
req = urllib2.Request(url)
req.add_header("User-agent", "HTML2ODT: Convert HTML to OpenDocument")
conn = urllib2.urlopen(req)
if not conn:
raise IOError, "Failure in open"
data = conn.read()
headers = conn.info()
conn.close()
encoding = 'iso8859-1' #Standard HTML
if headers.has_key('content-type'):
(ct, parms) = parse_header(headers['content-type'])
if parms.has_key('charset'):
encoding = parms['charset']
mhp = HTML2ODTParser(document, encoding, url)
mhp.feed(data)
return mhp
entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]')
charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]')
incomplete = re.compile('&[a-zA-Z#]')
ampersand = re.compile('&')
def listget(list, key, default=None):
for l in list:
if l[0] == key:
default = l[1]
return default
class TagObject:
def __init__(self, tag, attrs, output_loc):
self.tag = tag
self.attrs = attrs
self.output_loc = output_loc
class HTML2ODTParser(HTMLParser):
def __init__(self, document, encoding, baseurl):
HTMLParser.__init__(self)
self.doc = document
self.curr = self.doc.text
if self.doc.getStyleByName("Standard") is None:
style = Style(name="Standard", family="paragraph", attributes={'class':"text"})
self.doc.styles.addElement(style)
if self.doc.getStyleByName("Text_20_body") is None:
style = Style(name="Text_20_body", displayname="Text body", family="paragraph",
parentstylename="Standard", attributes={'class':"text"})
p = ParagraphProperties(margintop="0cm", marginbottom="0.212cm")
style.addElement(p)
self.doc.styles.addElement(style)
if self.doc.getStyleByName("Heading") is None:
style = Style(name="Heading", family="paragraph", parentstylename="Standard",
nextstylename="Text_20_body", attributes={'class':"text"})
p = ParagraphProperties(margintop="0.423cm", marginbottom="0.212cm", keepwithnext="always")
style.addElement(p)
p = TextProperties(fontname="Nimbus Sans L", fontsize="14pt",
fontnameasian="DejaVu LGC Sans", fontsizeasian="14pt",
fontnamecomplex="DejaVu LGC Sans", fontsizecomplex="14pt")
style.addElement(p)
self.doc.styles.addElement(style)
self.encoding = encoding
(scheme, host, path, params, fragment) = urlparse.urlsplit(baseurl)
lastslash = path.rfind('/')
if lastslash > -1:
path = path[:lastslash]
self.baseurl = urlparse.urlunsplit((scheme, host, path,'',''))
self.basehost = urlparse.urlunsplit((scheme, host, '','',''))
self.sectnum = 0
self.tagstack = []
self.pstack = []
self.processelem = True
self.processcont = True
self.__data = []
self.elements = {
'a': (self.s_html_a, self.close_tag),
'base': ( self.output_base, None),
'b': ( self.s_html_fontstyle, self.close_tag),
'big': ( self.s_html_fontstyle, self.close_tag),
'br': ( self.output_br, None),
'col': ( self.s_html_col, None),
'dd': ( self.s_html_dd, self.close_tag),
'dt': ( self.s_html_dt, None),
'div': ( self.s_html_section, self.e_html_section),
'em': ( self.s_html_emphasis, self.close_tag),
'h1': ( self.s_html_headline, self.close_tag),
'h2': ( self.s_html_headline, self.close_tag),
'h3': ( self.s_html_headline, self.close_tag),
'h4': ( self.s_html_headline, self.close_tag),
'h5': ( self.s_html_headline, self.close_tag),
'h6': ( self.s_html_headline, self.close_tag),
'head': ( self.s_ignorexml, None),
'i': ( self.s_html_fontstyle, self.close_tag),
'img': ( self.output_img, None),
'li': ( self.s_html_li, self.e_html_li),
'meta': ( self.meta_encoding, None),
'ol': ( self.output_ol, self.e_html_list),
'p': ( self.s_html_block, self.e_html_block),
's': ( self.s_html_fontstyle, self.close_tag),
'small':( self.s_html_fontstyle, self.close_tag),
'span': ( self.s_html_span, self.close_tag),
'strike':( self.s_html_fontstyle, self.close_tag),
'strong':( self.s_html_emphasis, self.close_tag),
'table':( self.s_html_table, self.e_html_table),
'td': ( self.s_html_td, self.close_tag),
'th': ( self.s_html_td, self.close_tag),
'title':( self.s_html_title, self.e_html_title),
'tr': ( self.s_html_tr, self.close_tag),
'tt': ( self.s_html_fontstyle, self.close_tag),
'u': ( self.s_html_fontstyle, self.close_tag),
'ul': ( self.output_ul, self.e_html_list),
'var': ( self.s_html_emphasis, self.close_tag),
}
def result(self):
""" Return a string
String must be in UNICODE
"""
str = string.join(self.__data,'')
self.__data = []
return str
def meta_name(self, attrs):
""" Look in meta tag for textual info"""
foundit = 0
# Is there a name attribute?
for attr in attrs:
if attr[0] == 'name' and string.lower(attr[1]) in ('description',
'keywords','title',
'dc.description','dc.keywords','dc.title'
):
foundit = 1
if foundit == 0:
return 0
# Is there a content attribute?
content = self.find_attr(attrs,'content')
if content:
self.handle_data(u' ')
self.handle_attr(content)
self.handle_data(u' ')
return 1
def meta_encoding(self, tag, attrs):
""" Look in meta tag for page encoding (Content-Type)"""
foundit = 0
# Is there a content-type attribute?
for attr in attrs:
if attr[0] == 'http-equiv' and string.lower(attr[1]) == 'content-type':
foundit = 1
if foundit == 0:
return 0
# Is there a content attribute?
for attr in attrs:
if attr[0] == 'content':
(ct, parms) = parse_header(attr[1])
if parms.has_key('charset'):
self.encoding = parms['charset']
return 1
def s_ignorexml(self, tag, attrs):
self.processelem = False
def output_base(self, tag, attrs):
""" Change the document base if there is a base tag """
baseurl = listget(attrs, 'href', self.baseurl)
(scheme, host, path, params, fragment) = urlparse.urlsplit(baseurl)
lastslash = path.rfind('/')
if lastslash > -1:
path = path[:lastslash]
self.baseurl = urlparse.urlunsplit((scheme, host, path,'',''))
self.basehost = urlparse.urlunsplit((scheme, host, '','',''))
def output_br(self, tag, attrs):
self.curr.addElement(text.LineBreak())
def s_html_font(self, tag, attrs):
""" 15.2.1 Font style elements: the TT, I, B, BIG, SMALL,
STRIKE, S, and U elements
"""
tagdict = {
}
def s_html_emphasis(self, tag, attrs):
""" 9.2.1 Phrase elements: EM, STRONG, DFN, CODE, SAMP, KBD,
VAR, CITE, ABBR, and ACRONYM
"""
tagdict = {
'cite': ['Citation', {'fontstyle':"italic", 'fontstyleasian':"italic", 'fontstylecomplex':"italic" }],
'code': ['Source_20_Text', {'fontname':"Courier", 'fontnameasian':"Courier",'fontnamecomplex':"Courier" }],
'dfn': ['Definition',{ }],
'em': ['Emphasis', {'fontstyle':"italic", 'fontstyleasian':"italic", 'fontstylecomplex':"italic" }],
'strong': ['Strong_20_Emphasis': {'fontweight':"bold",'fontweightasian':"bold",'fontweightcomplex':"bold"}],
'var': ['Variable', {'fontstyle':"italic", 'fontstyleasian':"italic", 'fontstylecomplex':"italic" }],
}
stylename = tagdict.get(tag,'Emphasis')
# Add the styles we need to the stylesheet
if stylename == "Source_20_Text" and self.doc.getStyleByName(stylename) is None:
style = Style(name="Source_20_Text", displayname="Source Text", family="text")
p = TextProperties(fontname="Courier", fontnameasian="Courier", fontnamecomplex="Courier")
style.addElement(p)
self.doc.styles.addElement(style)
e = text.Span(stylename=stylename)
self.curr.addElement(e)
self.curr = e
def s_html_fontstyle(self, tag, attrs):
""" 15.2.1 Font style elements: the TT, I, B, BIG, SMALL,
STRIKE, S, and U elements
('tt' is not considered an automatic style by OOo)
"""
tagdict = {
'b': ['BoldX',{'fontweight':"bold",
'fontweightasian':"bold",'fontweightcomplex':"bold" }],
'big': ['BigX', {'fontsize':"120%"}],
'i': ['ItalicX', {'fontstyle':"italic", 'fontstyleasian':"italic", 'fontstylecomplex':"italic" }],
'tt': ['TeletypeX', {'fontname':"Courier", 'fontnameasian':"Courier", 'fontnamecomplex':"Courier" }],
's': ['StrikeX', {'textlinethroughstyle':"solid"}],
'small': ['SmallX', {'fontsize':"80%"}],
'strike': ['StrikeX', {'textlinethroughstyle':"solid"}],
'u': ['UnderlineX', {'textunderlinestyle':"solid", 'textunderlinewidth':"auto",
'textunderlinecolor':"fontcolor"}],
}
stylename,styledecl = tagdict.get(tag,[None,None])
if stylename and self.doc.getStyleByName(stylename) is None:
style = Style(name=stylename, family="text")
style.addElement(TextProperties(attributes=styledecl))
self.doc.automaticstyles.addElement(style)
if stylename:
e = text.Span(stylename=stylename)
else:
e = text.Span()
self.curr.addElement(e)
self.curr = e
def s_html_span(self, tag, attrs):
e = text.Span()
self.curr.addElement(e)
self.curr = e
def s_html_title(self, tag, attrs):
e = dc.Title()
self.doc.meta.addElement(e)
self.curr = e
def e_html_title(self, tag):
self.curr = self.curr.parentNode
def output_img(self, tag, attrs):
src = listget(attrs, 'src', "Illegal IMG tag!")
alt = listget(attrs, 'alt', src)
# Must remember name of image and download it.
self.write_odt(u'<draw:image xlink:href="Pictures/%s" xlink:type="simple" xlink:show="embed" xlink:actuate="onLoad"/>' % '00000.png')
def s_html_a(self, tag, attrs):
href = None
href = listget(attrs, 'href', None)
if href:
if href in ("", "#"):
href == self.baseurl
elif href.find("://") >= 0:
pass
elif href[0] == '/':
href = self.basehost + href
e = text.A(type="simple", href=href)
else:
e = text.A()
# if self.curr.parentNode.qname != text.P().qname:
# p = text.P()
# self.curr.addElement(p)
# self.curr = p
self.curr.addElement(e)
self.curr = e
def close_tag(self, tag):
self.curr = self.curr.parentNode
def s_html_dd(self, tag, attrs):
if self.doc.getStyleByName("List_20_Contents") is None:
style = Style(name="List_20_Contents", displayname="List Contents", family="paragraph",
parentstylename="Standard", attributes={'class':"html"})
p = ParagraphProperties(marginleft="1cm", marginright="0cm", textindent="0cm", autotextindent="false")
style.addElement(p)
self.doc.styles.addElement(style)
e = text.P(stylename="List_20_Contents")
self.curr.addElement(e)
self.curr = e
def s_html_dt(self, tag, attrs):
if self.doc.getStyleByName("List_20_Heading") is None:
style = Style(name="List_20_Heading", displayname="List Heading", family="paragraph", parentstylename="Standard",
nextstylename="List_20_Contents", attributes={'class':"html"})
p = ParagraphProperties(marginleft="0cm", marginright="0cm", textindent="0cm", autotextindent="false")
style.addElement(p)
self.doc.styles.addElement(style)
e = text.P(stylename="List_20_Heading")
self.curr.addElement(e)
self.curr = e
def output_ul(self, tag, attrs):
self.write_odt(u'<text:list text:style-name="List_20_1">')
def output_ol(self, tag, attrs):
self.write_odt(u'<text:list text:style-name="Numbering_20_1">')
def e_html_list(self, tag):
self.write_odt(u'</text:list>')
def s_html_li(self, tag, attrs):
self.write_odt(u'<text:list-item><text:p text:style-name="P1">')
def e_html_li(self, tag):
self.write_odt(u'</text:p></text:list-item>')
def s_html_headline(self, tag, attrs):
stylename = "Heading_20_%s" % tag[1]
if stylename == "Heading_20_1" and self.doc.getStyleByName("Heading_20_1") is None:
style = Style(name="Heading_20_1", displayname="Heading 1",
family="paragraph", parentstylename="Heading", nextstylename="Text_20_body",
attributes={'class':"text"}, defaultoutlinelevel=1)
p = TextProperties(fontsize="115%", fontweight="bold", fontsizeasian="115%",
fontweightasian="bold", fontsizecomplex="115%", fontweightcomplex="bold")
style.addElement(p)
self.doc.styles.addElement(style)
if stylename == "Heading_20_2" and self.doc.getStyleByName("Heading_20_2") is None:
style = Style(name="Heading_20_2", displayname="Heading 2",
family="paragraph", parentstylename="Heading", nextstylename="Text_20_body",
attributes={'class':"text"}, defaultoutlinelevel=2)
p = TextProperties(fontsize="14pt", fontstyle="italic", fontweight="bold",
fontsizeasian="14pt", fontstyleasian="italic", fontweightasian="bold",
fontsizecomplex="14pt", fontstylecomplex="italic", fontweightcomplex="bold")
style.addElement(p)
self.doc.styles.addElement(style)
if stylename == "Heading_20_3" and self.doc.getStyleByName("Heading_20_3") is None:
style = Style(name="Heading_20_3", displayname="Heading 3",
family="paragraph", parentstylename="Heading", nextstylename="Text_20_body",
attributes={'class':"text"}, defaultoutlinelevel=3)
p = TextProperties(fontsize="14pt", fontweight="bold", fontsizeasian="14pt",
fontweightasian="bold", fontsizecomplex="14pt", fontweightcomplex="bold")
style.addElement(p)
self.doc.styles.addElement(style)
e = text.H(stylename="Heading_20_%s" % tag[1], outlinelevel=tag[1])
self.curr.addElement(e)
self.curr = e
def s_html_table(self, tag, attrs):
e = table.Table()
self.curr.addElement(e)
self.curr = e
def e_html_table(self, tag):
self.curr = self.curr.parentNode
def s_html_td(self, tag, attrs):
e = table.TableCell()
self.curr.addElement(e)
self.curr = e
def s_html_tr(self, tag, attrs):
e = table.TableRow()
self.curr.addElement(e)
self.curr = e
def s_html_col(self, tag, attrs):
e = table.TableColumn()
self.curr.addElement(e)
def s_html_section(self, tag, attrs):
""" Outputs block tag such as <p> and <div> """
name = self.find_attr(attrs,'id')
if name is None:
self.sectnum = self.sectnum + 1
name = "Sect%d" % self.sectnum
e = text.Section(name=name)
self.curr.addElement(e)
self.curr = e
def e_html_section(self, tag):
""" Outputs block tag such as <p> and <div> """
self.curr = self.curr.parentNode
def s_html_block(self, tag, attrs):
""" Outputs block tag such as <p> and <div> """
e = text.P(stylename="Text_20_body")
self.curr.addElement(e)
self.curr = e
def e_html_block(self, tag):
""" Outputs block tag such as <p> and <div> """
self.curr = self.curr.parentNode
#
# HANDLE STARTTAG
#
def handle_starttag(self, tag, attrs):
self.pstack.append( (self.processelem, self.processcont) )
tagobj = TagObject(tag, attrs, self.last_data_pos())
self.tagstack.append(tagobj)
method = self.elements.get(tag, (None, None))[0]
if self.processelem and method:
method(tag, attrs)
#
# HANDLE END
#
def handle_endtag(self, tag):
"""
"""
tagobj = self.tagstack.pop()
method = self.elements.get(tag, (None, None))[1]
if self.processelem and method:
method(tag)
self.processelem, self.processcont = self.pstack.pop()
#
# Data operations
#
def handle_data(self, data):
if data.strip() == '': return
if self.processelem and self.processcont:
self.curr.addText(data)
def write_odt(self, data):
""" Collect the data to show on the webpage """
if type(data) == StringType:
data = unicode(data, self.encoding)
self.__data.append(data)
def last_data_pos(self):
return len(self.__data)
def find_attr(self, attrs, key):
""" Run through the attibutes to find a specific one
return None if not found
"""
for attr in attrs:
if attr[0] == key:
return attr[1]
return None
#
# Tagstack operations
#
def find_tag(self, tag):
""" Run down the stack to find the last entry with the same tag name
Not Tested
"""
for tagitem in range(len(self.tagstack), 0, -1):
if tagitem.tag == tag:
return tagitem
return None
def handle_charref(self, name):
""" Handle character reference for UNICODE
"""
if name[0] in ('x', 'X'):
try:
n = int(name[1:],16)
except ValueError:
return
else:
try:
n = int(name)
except ValueError:
return
if not 0 <= n <= 65535:
return
self.handle_data(unichr(n))
def handle_entityref(self, name):
"""Handle entity references.
"""
table = htmlentitydefs.name2codepoint
if name in table:
self.handle_data(unichr(table[name]))
else:
return
def handle_attr(self, attrval):
""" Scan attribute values for entities and resolve them
Simply calls handle_data
"""
i = 0
n = len(attrval)
while i < n:
match = ampersand.search(attrval, i) #
if match:
j = match.start()
else:
j = n
if i < j: self.handle_data(attrval[i:j])
i = j
if i == n: break
startswith = attrval.startswith
if startswith('&#', i):
match = charref.match(attrval, i)
if match:
name = match.group()[2:-1]
self.handle_charref(name)
k = match.end()
if not startswith(';', k-1):
k = k - 1
i = k
continue
else:
break
elif startswith('&', i):
match = entityref.match(attrval, i)
if match:
name = match.group(1)
self.handle_entityref(name)
k = match.end()
if not startswith(';', k-1):
k = k - 1
i = k
continue
match = incomplete.match(attrval, i)
if match:
# match.group() will contain at least 2 chars
if match.group() == attrval[i:]:
self.error("EOF in middle of entity or char ref")
# incomplete
break
elif (i + 1) < n:
# not the end of the buffer, and can't be confused
# with some other construct
self.handle_data("&")
i = i + 1
else:
break
else:
assert 0, "interesting.search() lied"
# end while
if i < n:
self.handle_data(attrval[i:n])
i = n
def usage():
sys.stderr.write("Usage: %s [-a] inputurl outputfile\n" % sys.argv[0])
if __name__ == "__main__":
try:
opts, args = getopt.getopt(sys.argv[1:], "a", ["append"])
except getopt.GetoptError:
usage()
sys.exit(2)
appendto = False
for o, a in opts:
if o in ("-a", "--append"):
appendto = True
if appendto:
doc = load(args[1])
else:
doc = OpenDocumentText()
result = converturl(args[0], doc)
print result.doc.xml()
result.doc.save(args[1])

15
contrib/odf2epub/Makefile Normal file
View File

@ -0,0 +1,15 @@
all: odf odf2epub.1
txt: odf2epub.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

350
contrib/odf2epub/odf2epub Executable file
View File

@ -0,0 +1,350 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2010 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odf.odf2xhtml import ODF2XHTML
from odf.namespaces import TEXTNS, XLINKNS
from odf.opendocument import load
import sys, getopt, time, zipfile
from StringIO import StringIO
from cgi import escape
UNIXPERMS = 0100644 << 16L # -rw-r--r--
def escaped(string):
return escape(string).encode('us-ascii','xmlcharrefreplace')
def usage():
sys.stderr.write("Usage: %s [-c cover-image] [-o output-file] [-p] inputfile\n" % sys.argv[0])
class NavpointEntry:
def __init__(self, anchor, title, chapter, level):
self.anchor = anchor
self.title = title
self.chapter = chapter
self.level = level
class ODF2EPUB(ODF2XHTML):
in_toc = False
navpoint_list = []
chapters = []
headerpart = []
cur_html_name = 'chapter0.xhtml'
def __init__(self, generate_css=True, embedable=False):
ODF2XHTML.__init__(self, generate_css, embedable)
self.elements[(TEXTNS, "table-of-content")] = (self.s_text_table_of_content, self.e_text_table_of_content)
def s_text_table_of_content(self, tag, attrs):
self.in_toc = True
def e_text_table_of_content(self, tag, attrs):
self.in_toc = False
# def s_text_a(self, tag, attrs):
# if self.in_toc:
# href = attrs[(XLINKNS,"href")].split("|")[0]
# if href[0] == "#":
# n = NavpointEntry(self.get_anchor(href[1:]), href[1:])
# self.navpoint_list.append(n)
# return ODF2XHTML.s_text_a(self, tag, attrs)
# def e_text_a(self, tag, attrs):
# pass
def s_text_h(self, tag, attrs):
""" Handle a heading
If the heading is a level 1 heading, then split the HTML file.
We have to be careful, because the heading can be inside a frame or a table.
"""
level = int(attrs[(TEXTNS,'outline-level')])
if level == 1:
tags_to_keep = self.htmlstack[:]
tags_to_close = self.htmlstack[:]
tags_to_close.reverse()
for htag,hattrs,hblock in tags_to_close:
if htag == 'body':
self.generate_footnotes()
self._resetfootnotes()
self.closetag(htag)
# I have to do this rather ugly, as the saved header part doesn't
# go through the self.opentag() method
self.chapters.append(''.join(self.headerpart + self.lines))
self.lines = []
self.htmlstack = tags_to_keep[:2] # Only <html> and <body>
for htag,hattrs,hblock in tags_to_keep[2:]:
self.opentag(htag,hattrs,hblock)
return ODF2XHTML.s_text_h(self, tag, attrs)
def e_text_h(self, tag, attrs):
""" Headings end """
level = int(attrs[(TEXTNS,'outline-level')])
if level > 6: level = 6 # Heading levels go only to 6 in XHTML
if level < 1: level = 1
lev = self.headinglevels[1:level+1]
outline = '.'.join(map(str,lev) )
heading = ''.join(self.data)
anchor = self.get_anchor("%s.%s" % ( outline, heading))
n = NavpointEntry(anchor, heading, len(self.chapters), level)
self.navpoint_list.append(n)
return ODF2XHTML.e_text_h(self, tag, attrs)
def s_office_text(self, tag, attrs):
""" Save all the lines up to and including the <body> tag
so I can split the file into more files
"""
ODF2XHTML.s_office_text(self, tag, attrs)
self.headerpart = self.lines
self.lines = []
def e_office_document_content(self, tag, attrs):
ODF2XHTML.e_office_document_content(self, tag, attrs)
self.chapters.append(''.join(self.headerpart + self.lines))
self.lines = []
class EPublication:
mimetype = "application/epub+zip"
coverimage = None
coverhtml = """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Cover</title>
<style type="text/css"> img { max-width: 100%; } </style>
</head>
<body>
<div id="cover-image">
<img src="Pictures/cover.jpg" alt="Cover image"/>
</div>
</body>
</html>"""
container = """<?xml version="1.0"?>
<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
<rootfiles>
<rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>
</rootfiles>
</container>"""
content_opf_head = """<?xml version="1.0"?>
<package xmlns="http://www.idpf.org/2007/opf" unique-identifier="BookID" version="2.0">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
<dc:title>%s</dc:title>
<dc:language>%s</dc:language>
<dc:identifier id="BookID" opf:scheme="URI">%s</dc:identifier>
<dc:creator>%s</dc:creator>
%s
</metadata>
<manifest>
<item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/>
<item id="styles-css" href="styles.css" media-type="text/css"/>"""
toc_ncx_head = """<?xml version="1.0"?>
<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN"
"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
<head>
<meta name="dtb:uid" content="%s"/>
<meta name="dtb:depth" content="2"/>
<meta name="dtb:totalPageCount" content="0"/>
<meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle>
<text>%s</text>
</docTitle>
<navMap>
<navPoint id="navPoint-1" playOrder="1">
<navLabel>
<text>Start</text>
</navLabel>
<content src="chapter0.xhtml"/>
</navPoint>"""
toc_ncx_foot = """ </navMap>
</ncx>"""
def __init__(self, filename, coverimage):
self.doc = load(filename)
self.coverimage = coverimage
self.odhandler = ODF2EPUB(True, False)
self.odhandler.add_style_file("styles.css")
self.odhandler.load(self.doc)
def save(self, outputfile):
""" Save the document under the filename """
if outputfile == '-':
outputfp = zipfile.ZipFile(sys.stdout,"w")
else:
outputfp = zipfile.ZipFile(outputfile, "w")
self._zipwrite(outputfp)
outputfp.close()
def _zipwrite(self, outputfp):
""" Write the document to an open file pointer """
now = time.localtime()[:6]
xhtml = self.odhandler.xhtml()
# Write mimetype - uncompressed
zout = zipfile.ZipInfo('mimetype', now)
zout.compress_type = zipfile.ZIP_STORED
zout.external_attr = UNIXPERMS
outputfp.writestr(zout, self.mimetype)
# Write META-INF/container.xml
zout = zipfile.ZipInfo('META-INF/container.xml', now)
zout.compress_type = zipfile.ZIP_DEFLATED
zout.external_attr = UNIXPERMS
outputfp.writestr(zout, self.container)
# Write CSS part
zout = zipfile.ZipInfo('OEBPS/styles.css', now)
zout.compress_type = zipfile.ZIP_DEFLATED
zout.external_attr = UNIXPERMS
outputfp.writestr(zout, self.odhandler.css())
# Write HTML parts
for chapter in range(len(self.odhandler.chapters)):
zout = zipfile.ZipInfo('OEBPS/chapter%d.xhtml' % chapter, now)
zout.compress_type = zipfile.ZIP_DEFLATED
zout.external_attr = UNIXPERMS
xhtml = self.odhandler.chapters[chapter].encode('us-ascii','xmlcharrefreplace')
outputfp.writestr(zout, xhtml)
# Copy images over to output
for arcname, picturerec in self.doc.Pictures.items():
what_it_is, fileobj, mediatype = picturerec
zi = zipfile.ZipInfo("OEBPS/" + str(arcname), now)
zi.compress_type = zipfile.ZIP_STORED
zi.external_attr = UNIXPERMS
outputfp.writestr(zi, fileobj)
# Write content.opf
zout = zipfile.ZipInfo('OEBPS/content.opf', now)
zout.compress_type = zipfile.ZIP_DEFLATED
zout.external_attr = UNIXPERMS
opf = []
if self.coverimage:
covermeta = """<meta name="cover" content="cover-image"/>"""
else:
covermeta = ""
opf.append(self.content_opf_head % (escaped(self.odhandler.title), escaped(self.odhandler.language),
escaped(args[0]), escaped(self.odhandler.creator), covermeta))
if self.coverimage:
opf.append(""" <item id="cover-page" href="cover.xhtml" media-type="application/xhtml+xml"/>""")
opf.append(""" <item id="cover-image" href="Pictures/cover.jpg" media-type="image/jpeg"/>""")
for chapter in range(len(self.odhandler.chapters)):
opf.append(""" <item id="chapter%d.xhtml" href="chapter%d.xhtml" media-type="application/xhtml+xml"/>""" % (chapter, chapter))
# Write manifest of images.
for arcname, picturerec in self.doc.Pictures.items():
what_it_is, fileobj, mediatype = picturerec
opf.append(""" <item id="%s" href="%s" media-type="%s"/>""" % (arcname.replace('/','_'), arcname, mediatype))
opf.append("""</manifest>""")
opf.append("""<spine toc="ncx">""")
if self.coverimage:
opf.append(""" <itemref idref="cover-page" linear="no"/>""")
for chapter in range(len(self.odhandler.chapters)):
opf.append(""" <itemref idref="chapter%d.xhtml"/>""" % chapter)
opf.append("""</spine>""")
if self.coverimage:
opf.append("""<guide>""")
opf.append(""" <reference type="cover" title="Cover" href="cover.xhtml"/>""")
opf.append("""</guide>""")
opf.append('</package>')
outputfp.writestr(zout, '\n'.join(opf))
# Write toc.ncx
zout = zipfile.ZipInfo('OEBPS/toc.ncx', now)
zout.compress_type = zipfile.ZIP_DEFLATED
zout.external_attr = UNIXPERMS
opf = []
opf.append(self.toc_ncx_head % (escaped(args[0]), escaped(self.odhandler.title)))
# We basically force the first navpoint to be a level 1 heading, no
# matter what it was in reality. Then all other headings are either level 1 or 2.
np_inx = 2
np_level = 1
for np in self.odhandler.navpoint_list:
if np_inx == 2:
np.level = 1
if np.level > 2: np.level = 2
if np_inx != 2 and np.level <= np_level:
opf.append(""" </navPoint>""");
if np_inx != 2 and np.level < np_level:
opf.append(""" </navPoint>""");
opf.append(""" <navPoint id="navPoint-%d" playOrder="%d"> <!-- L%d -->
<navLabel>
<text>%s</text>
</navLabel>
<content src="chapter%d.xhtml#%s"/>
""" % (np_inx, np_inx, np.level, escaped(np.title), np.chapter, np.anchor))
np_inx += 1
np_level = np.level
opf.append(""" </navPoint>""");
if np_level > 1:
opf.append(""" </navPoint>""");
opf.append(self.toc_ncx_foot)
outputfp.writestr(zout, '\n'.join(opf))
# Write cover image
if self.coverimage:
outputfp.write(coverimage,'OEBPS/Pictures/cover.jpg', zipfile.ZIP_STORED)
zout = zipfile.ZipInfo('OEBPS/cover.xhtml', now)
zout.compress_type = zipfile.ZIP_DEFLATED
zout.external_attr = UNIXPERMS
outputfp.writestr(zout, self.coverhtml)
# zout = zipfile.ZipInfo('OEBPS/styles.css', now)
# zout.compress_type = zipfile.ZIP_DEFLATED
# zout.external_attr = UNIXPERMS
# css = self.odhandler.css().encode('us-ascii','xmlcharrefreplace')
# outputfp.writestr(zout, css)
try:
opts, args = getopt.getopt(sys.argv[1:], "c:po:", ["cover", "plain","output"])
except getopt.GetoptError:
usage()
sys.exit(2)
generatecss = True
embedable = False
outputfilename = "-"
coverimage = None
for o, a in opts:
if o in ("-c", "--cover"):
coverimage = a
if o in ("-p", "--plain"):
generatecss = False
if o in ("-o", "--output"):
outputfilename = a
if len(args) != 1:
usage()
sys.exit(2)
try:
epub = EPublication(args[0], coverimage)
epub.save(outputfilename)
except:
sys.stderr.write("Unable to open file %s or file is not OpenDocument\n" % sys.argv[1])
sys.exit(1)
sys.exit(0)

View File

@ -0,0 +1,72 @@
'\" t
.\" Title: odf2epub
.\" Author: S\(/oren Roug
.\" Generator: DocBook XSL Stylesheets v1.75.2 <http://docbook.sf.net/>
.\" Date: 05/16/2010
.\" Manual: User commands
.\" Source: odfpy
.\" Language: English
.\"
.TH "ODF2EPUB" "1" "05/16/2010" "odfpy" "User commands"
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
odf2epub \- Convert ODF to an ePub ebook
.SH "SYNOPSIS"
.HP \w'\fBodf2epub\fR\ 'u
\fBodf2epub\fR [\-p] [\-o\ \fIoutputfile\fR] [\-c\ \fIcover_image\fR] \fIpath\fR
.SH "DESCRIPTION"
.PP
\fBodf2epub\fR
is a program that will create an ebook (\&.epub) from the input file and will write the ebook to stdout or a file specified by \-o\&. "Path" is assumed to be an OpenDocument file of text, spreadsheet or presentation type\&.
.PP
If the document doesn\'t have a title in the properties, then the first heading of any level is used\&.
.SH "OPTIONS"
.PP
\-c, \-\-cover
.RS 4
The \-c argument add a cover image to the EPUB file\&. Make sure the cover image itself is scaled to less than 1000px in width and height\&. Best practice is to use an image in JPG or PNG format at 600 pixels wide by 800 pixels in height\&.
.RE
.PP
\-p, \-\-plain
.RS 4
The \-p flag will generate HTML without CSS\&.
.RE
.PP
\-o, \-\-output
.RS 4
Specify the output file with this flag\&. "\-" implies standard out\&.
.RE
.SH "EXAMPLE"
.sp
.if n \{\
.RS 4
.\}
.nf
odf2epub \-o example\&.epub odf\-file\&.odt
.fi
.if n \{\
.RE
.\}
.SH "BUGS"
.PP
The EPUB format has the following limitations when running on a mobile device\&. If these limits are not adhered to, EPUB files
\fImight\fR
not work on mobile devices\&. Image Size: 10MB uncompressed\&. Individual XHTML file sizes: 300k uncompressed/100k compressed\&. Odf2epub does not ensure that these limits are adhered to\&.
.SH "SEE ALSO"
.PP
\fBodf2xhtml\fR(1)
.SH "AUTHOR"
.PP
\fBS\(/oren Roug\fR
.RS 4
Original author
.RE

View File

@ -0,0 +1,92 @@
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="odf2epub">
<refentryinfo>
<productname>odfpy</productname>
<author><firstname>Søren</firstname><surname>Roug</surname>
<contrib>Original author</contrib>
</author>
</refentryinfo>
<refmeta>
<refentrytitle>odf2epub</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>odf2epub</refname>
<refpurpose>Convert ODF to an ePub ebook</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>odf2epub</command>
<arg choice="opt">-p </arg>
<arg choice="opt">-o <replaceable>outputfile</replaceable></arg>
<arg choice="opt">-c <replaceable>cover_image</replaceable></arg>
<arg choice="plain">
<replaceable>path</replaceable>
</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><command>odf2epub</command> is a program that will create
an ebook (.epub) from the input file and will write the ebook to stdout or a file specified by -o.
"Path" is assumed to be an
OpenDocument file of text, spreadsheet or presentation type.
</para>
<para>
If the document doesn't have a title in the properties, then the first heading of any level is used.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term>-c, --cover</term>
<listitem>
<para>
The -c argument add a cover image to the EPUB file.
Make sure the cover image itself is scaled to less than 1000px in width and height. Best practice
is to use an image in JPG or PNG format at 600 pixels wide by 800 pixels in height.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-p, --plain</term>
<listitem>
<para>
The -p flag will generate HTML without CSS.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-o, --output</term>
<listitem>
<para>
Specify the output file with this flag. "-" implies standard out.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Example</title>
<screen>
odf2epub -o example.epub odf-file.odt
</screen>
</refsect1>
<refsect1>
<title>Bugs</title>
<para>The EPUB format has the following limitations when running on a mobile device.
If these limits are not adhered to, EPUB files <emphasis>might</emphasis> not work on mobile devices. Image Size: 10MB uncompressed.
Individual XHTML file sizes: 300k uncompressed/100k compressed. Odf2epub does not ensure
that these limits are adhered to.
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<para><command>odf2xhtml</command>(1)</para>
</refsect1>
</refentry>

View File

@ -0,0 +1,15 @@
all: odf odf2gbzip.1
txt: odf2gbzip.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

64
contrib/odf2gbzip/odf2gbzip Executable file
View File

@ -0,0 +1,64 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odf.odf2xhtml import ODF2XHTML
import zipfile
import sys
from time import localtime
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
if len(sys.argv) != 3:
sys.stderr.write("Usage: %s inputfile outputfile\n" % sys.argv[0])
sys.exit(1)
inpath = sys.argv[1]
basename = inpath[max(inpath.rfind('/'), inpath.rfind('\\'), inpath.rfind(':'))+1:]
dot = basename.rfind('.')
if dot > 0: basename = basename[:dot]
now = localtime()[:6]
odhandler = ODF2XHTML()
result = odhandler.odf2xhtml(inpath).encode('us-ascii','xmlcharrefreplace')
try:
zout = zipfile.ZipFile(sys.argv[2], "w", zipfile.ZIP_DEFLATED)
except:
sys.stderr.write("Unable to open %s for writing\n" % sys.argv[2])
zipinfo = zipfile.ZipInfo('%s/%s.htm' % (basename, basename), now)
zipinfo.external_attr = 0100644 << 16L # Unix permissions
zout.writestr(zipinfo, result)
try:
z = zipfile.ZipFile(inpath)
except:
sys.stderr.write("Unable to open %s or file is not OpenDocument\n" % sys.argv[2])
for zinfo in z.infolist():
if zinfo.filename[0:9] == 'Pictures/':
zipinfo = zipfile.ZipInfo(basename + "/" + zinfo.filename, now)
zipinfo.external_attr = 0100644 << 16L # Unix permissions
zout.writestr(zipinfo, z.read(zinfo.filename))
z.close()
zout.close()

View File

@ -0,0 +1,35 @@
.\" Title: odf2gbzip
.\" Author:
.\" Generator: DocBook XSL Stylesheets v1.72.0 <http://docbook.sf.net/>
.\" Date: 09/29/2007
.\" Manual:
.\" Source:
.\"
.TH "ODF2GBZIP" "1" "09/29/2007" "" ""
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.SH "NAME"
odf2gbzip \- Convert ODF to zipped web archive
.SH "SYNOPSIS"
.HP 10
\fBodf2gbzip\fR \fIpath\fR
.SH "DESCRIPTION"
.PP
\fBodf2gbzip\fR
is a program that will create a zipped web archive according to the convention followed by Project Gutenberg.
.PP
"Path" is assumed to be an OpenDocument file of text, spreadsheet or presentation type.
.SH "EXAMPLE"
.sp
.RS 4
.nf
odf2gbzip odf\-file
.fi
.RE
.SH "SEE ALSO"
.PP
\fBodf2mht\fR(1)
\fBodf2war\fR(1)

View File

@ -0,0 +1,38 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="odf2gbzip">
<refnamediv>
<refname>odf2gbzip</refname>
<refpurpose>Convert ODF to zipped web archive</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>odf2gbzip</command>
<arg choice="plain"><replaceable>path</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para><command>odf2gbzip</command> is a program that will create
a zipped web archive according to the convention followed by Project Gutenberg.
</para>
<para>
&quot;Path&quot; is assumed to be an
OpenDocument file of text, spreadsheet or presentation type.
</para>
</refsect1>
<refsect1><title>Example</title>
<screen>
odf2gbzip odf-file
</screen>
</refsect1>
<refsect1><title>See Also</title>
<para>
<command>odf2mht</command>(1)
<command>odf2war</command>(1)
</para>
</refsect1>
</refentry>

15
contrib/odf2war/Makefile Normal file
View File

@ -0,0 +1,15 @@
all: odf odf2war.1
txt: odf2war.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../odf

61
contrib/odf2war/odf2war Executable file
View File

@ -0,0 +1,61 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2006 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odf.odf2xhtml import ODF2XHTML
import tarfile
import zipfile
import sys
from time import time
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
if len(sys.argv) != 3:
sys.stderr.write("Usage: %s inputfile outputfile\n" % sys.argv[0])
sys.exit(1)
now = long(time())
odhandler = ODF2XHTML()
result = odhandler.odf2xhtml(sys.argv[1]).encode('us-ascii','xmlcharrefreplace')
tar = tarfile.open(sys.argv[2], "w:gz")
tarinfo = tarfile.TarInfo()
tarinfo.name = 'index.html'
tarinfo.uid = 501
tarinfo.gid = 100
tarinfo.size = len(result)
tarinfo.mtime = now
#tarinfo.uname = "johndoe"
#tarinfo.gname = "fake"
tar.addfile(tarinfo, StringIO(result))
z = zipfile.ZipFile(sys.argv[1])
for zinfo in z.infolist():
if zinfo.filename[0:9] == 'Pictures/':
tarinfo = tarfile.TarInfo()
tarinfo.name = zinfo.filename
tarinfo.uid = 501
tarinfo.gid = 456
tarinfo.size = zinfo.file_size
tarinfo.mtime = now
#tarinfo.uname = "johndoe"
#tarinfo.gname = "fake"
tar.addfile(tarinfo, StringIO(z.read(zinfo.filename)))
z.close()
tar.close()

217
contrib/odf2war/odf2war.1 Normal file
View File

@ -0,0 +1,217 @@
.\" Title: odf2war
.\" Author: S\(/oren Roug
.\" Generator: DocBook XSL Stylesheets v1.74.0 <http://docbook.sf.net/>
.\" Date: 01/04/2009
.\" Manual: User commands
.\" Source: odfpy
.\" Language: English
.\"
.TH "ODF2WAR" "1" "01/04/2009" "odfpy" "User commands"
.\" -----------------------------------------------------------------
.\" * (re)Define some macros
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" toupper - uppercase a string (locale-aware)
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de toupper
.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
\\$*
.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH-xref - format a cross-reference to an SH section
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de SH-xref
.ie n \{\
.\}
.toupper \\$*
.el \{\
\\$*
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH - level-one heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SH
.\" put an extra blank line of space above the head in non-TTY output
.if t \{\
.sp 1
.\}
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[an-margin]u
.ti 0
.HTML-TAG ".NH \\n[an-level]"
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
\." make the size of the head bigger
.ps +3
.ft B
.ne (2v + 1u)
.ie n \{\
.\" if n (TTY output), use uppercase
.toupper \\$*
.\}
.el \{\
.nr an-break-flag 0
.\" if not n (not TTY), use normal case (not uppercase)
\\$1
.in \\n[an-margin]u
.ti 0
.\" if not n (not TTY), put a border/line under subheading
.sp -.6
\l'\n(.lu'
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SS - level-two heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SS
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[IN]u
.ti \\n[SN]u
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.ps \\n[PS-SS]u
\." make the size of the head bigger
.ps +2
.ft B
.ne (2v + 1u)
.if \\n[.$] \&\\$*
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BB/BE - put background/screen (filled box) around block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BB
.if t \{\
.sp -.5
.br
.in +2n
.ll -2n
.gcolor red
.di BX
.\}
..
.de EB
.if t \{\
.if "\\$2"adjust-for-leading-newline" \{\
.sp -1
.\}
.br
.di
.in
.ll
.gcolor
.nr BW \\n(.lu-\\n(.i
.nr BH \\n(dn+.5v
.ne \\n(BHu+.5v
.ie "\\$2"adjust-for-leading-newline" \{\
\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.el \{\
\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.in 0
.sp -.5v
.nf
.BX
.in
.sp .5v
.fi
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BM/EM - put colored marker in margin next to block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BM
.if t \{\
.br
.ll -2n
.gcolor red
.di BX
.\}
..
.de EM
.if t \{\
.br
.di
.ll
.gcolor
.nr BH \\n(dn
.ne \\n(BHu
\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[]
.in 0
.nf
.BX
.in
.fi
.\}
..
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "Name"
odf2war \- Convert ODF to KDE web archive
.SH "Synopsis"
.fam C
.HP \w'\fBodf2war\fR\ 'u
\fBodf2war\fR \fIpath\fR
.fam
.SH "Description"
.PP
\fBodf2war\fR
is a program that will create a KDE Web Archive (\&.war) that can be read by Konqueror filemanager\&. It will write the web archive to stdout\&. The WAR plugin is part of the kdeaddons package\&.
.PP
"Path" is assumed to be an OpenDocument file of text, spreadsheet or presentation type\&.
.SH "Example"
.sp
.if n \{\
.RS 4
.\}
.fam C
.ps -1
.nf
.if t \{\
.sp -1
.\}
.BB lightgray adjust-for-leading-newline
.sp -1
odf2war odf\-file
.EB lightgray adjust-for-leading-newline
.if t \{\
.sp 1
.\}
.fi
.fam
.ps +1
.if n \{\
.RE
.\}
.SH "See Also"
.PP
\fBodftools\fR(1),
\fBodf2mht\fR(1)
.SH "Author"
.PP
\fBS\(/oren Roug\fR
.RS 4
Original author
.RE

View File

@ -0,0 +1,51 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="odf2war">
<refentryinfo>
<productname>odfpy</productname>
<author><firstname>Søren</firstname><surname>Roug</surname>
<contrib>Original author</contrib>
</author>
</refentryinfo>
<refmeta>
<refentrytitle>odf2war</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>odf2war</refname>
<refpurpose>Convert ODF to KDE web archive</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>odf2war</command>
<arg choice="plain"><replaceable>path</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para><command>odf2war</command> is a program that will create
a KDE Web Archive (.war) that can be read by Konqueror filemanager. It
will write the web archive to stdout. The WAR plugin is part of the
kdeaddons package.
</para>
<para>
&quot;Path&quot; is assumed to be an
OpenDocument file of text, spreadsheet or presentation type.
</para>
</refsect1>
<refsect1><title>Example</title>
<screen>
odf2war odf-file
</screen>
</refsect1>
<refsect1><title>See Also</title>
<para>
<command>odftools</command>(1),
<command>odf2mht</command>(1)
</para>
</refsect1>
</refentry>

14
contrib/odfsign/Makefile Normal file
View File

@ -0,0 +1,14 @@
all: odf odfsign.1
txt: odfsign.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

92
contrib/odfsign/odfsign Executable file
View File

@ -0,0 +1,92 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2006 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# Requires PyXML
import sha,base64,zipfile,sys
from xml.dom.ext.reader import PyExpat
from xml.dom.ext.c14n import Canonicalize
import StringIO
def data_digest(data):
return base64.b64encode(sha.new(data).digest())
def xml_digest(dom):
s = StringIO.StringIO()
Canonicalize(dom,s)
canon = s.getvalue().encode('utf-8')
return data_digest(canon)
def get_signatureproperty(dom, ref):
property_list = dom.getElementsByTagNameNS('http://www.w3.org/2000/09/xmldsig#','SignatureProperty')
for p in property_list:
id = p.getAttributeNS(None,'Id')
if ref == id: return p
return None
def exitwithusage(exitcode=2):
sys.stderr.write("Usage: %s inputfile\n" % sys.argv[0])
sys.exit(exitcode)
if __name__ == '__main__':
if len(sys.argv) != 2:
exitwithusage()
z = zipfile.ZipFile(sys.argv[1])
namelist = z.namelist()
if "META-INF/documentsignatures.xml" not in namelist:
print "This document is not signed"
sys.exit()
documentsignatures_xml = z.read('META-INF/documentsignatures.xml')
reader = PyExpat.Reader()
doc = reader.fromString(documentsignatures_xml)
signature_list = doc.getElementsByTagNameNS('http://www.w3.org/2000/09/xmldsig#','Signature')
print "Document has %s signature(s)" % len(signature_list)
signnum = 1
for signature in signature_list:
date = signature.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/","date")[0].firstChild.nodeValue
# print date.__dict__
print "Checking signature #%d - signed on %s" % (signnum, date),
refs = signature.getElementsByTagNameNS('http://www.w3.org/2000/09/xmldsig#','Reference')
for ref in refs:
status = "OK"
uri = ref.getAttributeNS(None,'URI')
digest_value = ref.getElementsByTagNameNS('http://www.w3.org/2000/09/xmldsig#','DigestValue')[0].firstChild.nodeValue
if uri[0] != '#':
document = z.read(uri)
if uri[-4:] == ".xml" and len(document) != 0:
dom = PyExpat.Reader().fromString(document)
digest_actual = xml_digest(dom)
else:
digest_actual = data_digest(document)
else:
# FIXME
# fragments aren't canonicalized. See http://www.w3.org/TR/xmldsig-core/#sec-URI
xmlfragment = get_signatureproperty(doc, uri[1:])
if xmlfragment is None:
continue
digest_actual = digest_value
#digest_actual = xml_digest(xmlfragment)
if digest_value != digest_actual:
status = "failed"
print status
signnum += 1

217
contrib/odfsign/odfsign.1 Normal file
View File

@ -0,0 +1,217 @@
.\" Title: odfsign
.\" Author: S\(/oren Roug
.\" Generator: DocBook XSL Stylesheets v1.74.0 <http://docbook.sf.net/>
.\" Date: 03/16/2009
.\" Manual: User commands
.\" Source: odfpy
.\" Language: English
.\"
.TH "ODFSIGN" "1" "03/16/2009" "odfpy" "User commands"
.\" -----------------------------------------------------------------
.\" * (re)Define some macros
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" toupper - uppercase a string (locale-aware)
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de toupper
.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
\\$*
.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH-xref - format a cross-reference to an SH section
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de SH-xref
.ie n \{\
.\}
.toupper \\$*
.el \{\
\\$*
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH - level-one heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SH
.\" put an extra blank line of space above the head in non-TTY output
.if t \{\
.sp 1
.\}
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[an-margin]u
.ti 0
.HTML-TAG ".NH \\n[an-level]"
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
\." make the size of the head bigger
.ps +3
.ft B
.ne (2v + 1u)
.ie n \{\
.\" if n (TTY output), use uppercase
.toupper \\$*
.\}
.el \{\
.nr an-break-flag 0
.\" if not n (not TTY), use normal case (not uppercase)
\\$1
.in \\n[an-margin]u
.ti 0
.\" if not n (not TTY), put a border/line under subheading
.sp -.6
\l'\n(.lu'
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SS - level-two heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SS
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[IN]u
.ti \\n[SN]u
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.ps \\n[PS-SS]u
\." make the size of the head bigger
.ps +2
.ft B
.ne (2v + 1u)
.if \\n[.$] \&\\$*
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BB/BE - put background/screen (filled box) around block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BB
.if t \{\
.sp -.5
.br
.in +2n
.ll -2n
.gcolor red
.di BX
.\}
..
.de EB
.if t \{\
.if "\\$2"adjust-for-leading-newline" \{\
.sp -1
.\}
.br
.di
.in
.ll
.gcolor
.nr BW \\n(.lu-\\n(.i
.nr BH \\n(dn+.5v
.ne \\n(BHu+.5v
.ie "\\$2"adjust-for-leading-newline" \{\
\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.el \{\
\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.in 0
.sp -.5v
.nf
.BX
.in
.sp .5v
.fi
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BM/EM - put colored marker in margin next to block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BM
.if t \{\
.br
.ll -2n
.gcolor red
.di BX
.\}
..
.de EM
.if t \{\
.br
.di
.ll
.gcolor
.nr BH \\n(dn
.ne \\n(BHu
\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[]
.in 0
.nf
.BX
.in
.fi
.\}
..
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "Name"
odfsign \- Verify a signed ODF document
.SH "Synopsis"
.fam C
.HP \w'\fBodfsign\fR\ 'u
\fBodfsign\fR \fIpath\fR
.fam
.SH "Description"
.PP
\fBodfsign\fR
checks that the checksums used in the signature are correct\&.
\fIIt does not check that the x509 digest is correct!\fR
Therefore it is currently of little use\&.
.PP
"Path" is assumed to be an OpenDocument file of text, spreadsheet or presentation type\&.
.SH "Example"
.sp
.if n \{\
.RS 4
.\}
.fam C
.ps -1
.nf
.if t \{\
.sp -1
.\}
.BB lightgray adjust-for-leading-newline
.sp -1
odfsign odf\-file
.EB lightgray adjust-for-leading-newline
.if t \{\
.sp 1
.\}
.fi
.fam
.ps +1
.if n \{\
.RE
.\}
.SH "See Also"
.PP
http://www\&.w3\&.org/TR/xmldsig\-core
.SH "Author"
.PP
\fBS\(/oren Roug\fR
.RS 4
Original author
.RE

View File

@ -0,0 +1,50 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="odfsign">
<refentryinfo>
<productname>odfpy</productname>
<author><firstname>Søren</firstname><surname>Roug</surname>
<contrib>Original author</contrib>
</author>
</refentryinfo>
<refmeta>
<refentrytitle>odfsign</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>odfsign</refname>
<refpurpose>Verify a signed ODF document</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>odfsign</command>
<arg choice="plain"><replaceable>path</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para><command>odfsign</command> checks that the checksums
used in the signature are correct.
<emphasis>It does not check that the x509 digest is correct!</emphasis>
Therefore it is currently of little use.
</para>
<para>
&quot;Path&quot; is assumed to be an
OpenDocument file of text, spreadsheet or presentation type.
</para>
</refsect1>
<refsect1><title>Example</title>
<screen>
odfsign odf-file
</screen>
</refsect1>
<refsect1><title>See Also</title>
<para>
http://www.w3.org/TR/xmldsig-core
</para>
</refsect1>
</refentry>

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from setuptools import setup
PACKAGE = 'OdfToHtml'
VERSION = '0.1'
setup(name='OdfToHtml',
version='0.1',
packages=['odftohtml'],
author='Soren Roug',
author_email='soren.roug@eea.europa.eu',
description='A plugin for viewing ODF pages as HTML',
url='http://trac-hacks.org/wiki/OdfToHtmlConverter',
entry_points={'trac.plugins': ['odftohtml.odftohtml=odftohtml.odftohtml']})

14
contrib/odscell/Makefile Normal file
View File

@ -0,0 +1,14 @@
all: odf odscell.1
txt: odscell.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

184
contrib/odscell/odscell Executable file
View File

@ -0,0 +1,184 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2010 Kartikaya Gupta, https://staktrace.com/
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odf import opendocument
from odf.element import Text
from odf.table import *
from odf.text import P
from optparse import OptionParser
import sys,csv,re
def getSheet( file, sheetIndex ):
doc = opendocument.load( file )
try:
spreadsheet = doc.spreadsheet
except NameError:
sys.stderr.write("Error: file is not a spreadsheet\n")
return (None, None)
sheets = spreadsheet.getElementsByType( Table )
if sheetIndex > len( sheets ):
sys.stderr.write( "Error: spreadsheet has only %d sheets; requested invalid sheet %d\n" % (len( sheets ), sheetIndex + 1) )
return (None, None)
sheet = sheets[sheetIndex]
return (doc, sheet)
def displayCells( file, sheetIndex, rowIndex, colIndex, rowCount, colCount ):
(doc, sheet) = getSheet( file, sheetIndex )
if (doc == None or sheet == None):
return 1
rows = sheet.getElementsByType( TableRow )
if rowIndex + rowCount > len( rows ):
sys.stderr.write( "Error: sheet has only %d rows; requested invalid row %d\n" % (len( rows ), rowIndex + rowCount) )
return 1
csv_writer = csv.writer( sys.stdout )
for i in range( rowIndex, rowIndex + rowCount ):
row = rows[ i ]
cells = row.getElementsByType( TableCell )
if colIndex + colCount > len( cells ):
sys.stderr.write( "Error: row has only %d cells; requested invalid column %d\n" % (len( cells ), colIndex + colCount) )
return 1
for j in range( colIndex, colIndex + colCount ):
cells[ j ] = unicode( cells[ j ] )
csv_writer.writerow( cells[ colIndex : colIndex + colCount ] )
return 0
def updateCells( file, sheetIndex, rowIndex, colIndex, rowCount, colCount ):
(doc, sheet) = getSheet( file, sheetIndex )
if (doc == None or sheet == None):
return 1
data = sys.stdin.readlines()
if rowCount < 0:
rowCount = len( data )
elif len( data ) < rowCount:
sys.stderr.write( "Error: not enough rows in input\n" )
return 1
# add table rows as necessary
rows = sheet.getElementsByType( TableRow )
if rowIndex + rowCount > len( rows ):
for i in range( rowIndex + rowCount - len( rows ) ):
sheet.addElement( TableRow() )
rows = sheet.getElementsByType( TableRow )
csv_reader = csv.reader( data )
for i in range( rowIndex, rowIndex + rowCount ):
row = rows[ i ]
dataRow = csv_reader.next()
rowColCount = colCount
if rowColCount < 0:
rowColCount = len( dataRow )
elif len( dataRow ) < rowColCount:
sys.stderr.write( "Error: not enough columns in input on row %d\n" % (rowIndex - i + 1) )
return 1
cells = row.getElementsByType( TableCell )
if colIndex > len( cells ):
for j in range( len( cells ), colIndex ):
row.addElement( TableCell() )
cells = row.getElementsByType( TableCell )
for j in range( colIndex, colIndex + rowColCount ):
if j < len( cells ):
row.insertBefore( TableCell(), cells[ j ].nextSibling );
row.removeChild( cells[ j ] )
else:
row.addElement( TableCell() )
cells = row.getElementsByType( TableCell )
value = dataRow[ j - colIndex ]
if re.match( r'^[-]?\d+(\.\d+)?$', value.strip() ):
cells[ j ].setAttribute( 'valuetype', 'float' )
cells[ j ].setAttribute( 'value', value )
else:
cells[ j ].setAttribute( 'valuetype', 'string' )
cells[ j ].addElement( P( text = dataRow[ j - colIndex ] ) )
doc.save( file )
return 0
def parseCell( cell ):
cellmatch = re.match( r'^([A-Z]+)([0-9]+)$', cell )
if cellmatch == None:
sys.stderr.write( "Error: the cell specified was not in the required format of <column><row> (e.g. A1)" )
exit( 1 )
cellcol = cellmatch.group( 1 )
colIndex = 0
for i in range( len( cellcol ) ):
colIndex = (colIndex * 26) + (ord( cellcol[i] ) - ord( 'A' ) + 1)
colIndex = colIndex - 1
rowIndex = int( cellmatch.group( 2 ) ) - 1
return (colIndex, rowIndex)
def getCount( value, write, label ):
if value == None:
if write:
return -1
else:
return 1
value = int( value )
if value <= 0:
sys.stderr.write( "Error: illegal value specified for %s\n" % label )
exit( 1 )
return value
if __name__ == "__main__":
usage = "%prog file.ods cell"
parser = OptionParser( usage=usage, version="%prog 0.1" )
parser.add_option( '-r', '--rows', action='store', dest='rows', help=
'''Specify the height of the block of cells, in rows. Must be greater than zero. Defaults to 1 when
the -w option is not present. Defaults to the number of input rows when the -w option is present.''', default=None )
parser.add_option( '-c', '--cols', action='store', dest='cols', help=
'''Specify the width of the block of cells, in columns. Must be greater than zero. Defaults to 1 when
the -w option is not present. Defaults to the number of input columns when the -w option is present.''', default=None )
parser.add_option( '-s', '--sheet', action='store', dest='sheet', help='The sheet in the ODS file to read/modify. Must be greater than zero; defaults to 1.', default=1 )
parser.add_option( '-w', '--write', action='store_true', dest='write', help=
'''If specified, the spreadsheet will be modified with data from standard input. If not specified,
the cells from the spreadsheet will be written to standard output.''' )
(options, args) = parser.parse_args()
if len( args ) != 2:
parser.print_help()
exit( 1 )
file = args[0]
(colIndex, rowIndex) = parseCell( args[1] )
rowCount = getCount( options.rows, options.write, 'rows' )
colCount = getCount( options.cols, options.write, 'cols' )
sheet = int( options.sheet ) - 1
if sheet < 0:
sys.stderr.write( "Error: illegal value specified for sheet\n" )
exit( 1 )
if options.write:
exit( updateCells( file, sheet, rowIndex, colIndex, rowCount, colCount ) )
else:
exit( displayCells( file, sheet, rowIndex, colIndex, rowCount, colCount ) )

98
contrib/odscell/odscell.1 Normal file
View File

@ -0,0 +1,98 @@
'\" t
.\" Title: odscell
.\" Author: Kartikaya Gupta
.\" Generator: DocBook XSL Stylesheets v1.76.0 <http://docbook.sf.net/>
.\" Date: 12/22/2010
.\" Manual: User commands
.\" Source: odfpy
.\" Language: English
.\"
.TH "ODSCELL" "1" "12/22/2010" "odfpy" "User commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
odscell \- Read and update blocks of cells in OpenDocument spreadsheets files
.SH "SYNOPSIS"
.HP \w'\fBodscell\fR\ 'u
\fBodscell\fR \fIfile\&.ods\fR \fIcell\fR
.SH "DESCRIPTION"
.PP
This program reads a cell or block of cells from a file in ODS format, and prints it out in a CSV format to standard output\&. Alternatively, if the \-w flag is set, the program reads in a CSV\-formatted block of cells from standard input, and overwites a cell or block of cells in a file in ODS format\&.
.SH "OPTIONS"
.PP
\-\-version
.RS 4
Display the version and exit\&.
.RE
.PP
\-h, \-\-help
.RS 4
Display command usage\&.
.RE
.PP
\-r \fIROWS\fR, \-\-rows=\fIROWS\fR
.RS 4
Specify the height of the block of cells, in rows\&. Must be greater than zero\&. Defaults to 1 when the \-w option is not present\&. Defaults to the number of input rows when the \-w option is present\&.
.RE
.PP
\-c \fICOLS\fR, \-\-cols=\fICOLS\fR
.RS 4
Specify the width of the block of cells, in columns\&. Must be greater than zero\&. Defaults to 1 when the \-w option is not present\&. Defaults to the number of input columns when the \-w option is present\&.
.RE
.PP
\-s \fISHEET\fR, \-\-sheet=\fISHEET\fR
.RS 4
The sheet in the ODS file to read/modify\&. Must be greater than zero; defaults to 1\&.
.RE
.PP
\-w
.RS 4
If specified, the spreadsheet will be modified with data from standard input\&. If not specified, the cells from the spreadsheet will be written to standard output\&.
.RE
.PP
\fIfile\&.ods\fR
.RS 4
The ODS file to be read from or modified\&.
.RE
.PP
\fIcell\fR
.RS 4
The top\-left cell of the block of cells to be read from or modified\&. This should be specified in normal spreadsheet format, e\&.g\&. "A1" or "BA23"\&.
.RE
.SH "EXAMPLE"
.sp
.if n \{\
.RS 4
.\}
.nf
odscell foo\&.ods A4 # display value in cell A4 on sheet 1 of foo\&.ods
odscell \-r 2 \-c 2 foo\&.ods B2 # display values for cells B2,B3,C2,C3 on sheet 1 of foo\&.ods
echo "hello,world,garbage" | odscell \-c 2 \-w foo\&.ods A1 # write "hello" to cell A1 and "world" to cell A2 on sheet 1 of foo\&.ods
cat bar\&.csv | odscell \-s 2 \-w foo\&.ods A1 # put the CSV data from bar\&.csv into sheet 2 of foo\&.ods
.fi
.if n \{\
.RE
.\}
.SH "AUTHOR"
.PP
\fBKartikaya Gupta\fR
.RS 4
Original author of odscell
.RE

View File

@ -0,0 +1,117 @@
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="odscell">
<refentryinfo>
<productname>odfpy</productname>
<author><firstname>Kartikaya</firstname><surname>Gupta</surname>
<contrib>Original author of odscell</contrib>
</author>
</refentryinfo>
<refmeta>
<refentrytitle>odscell</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>odscell</refname>
<refpurpose>Read and update blocks of cells in OpenDocument spreadsheets files</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>odscell</command>
<arg choice="plain"><replaceable>file.ods</replaceable></arg>
<arg choice="plain"><replaceable>cell</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
This program reads a cell or block of cells from a file in ODS format, and prints it out
in a CSV format to standard output. Alternatively, if the -w flag is set, the program reads
in a CSV-formatted block of cells from standard input, and overwites a cell or block of cells
in a file in ODS format.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term>--version</term>
<listitem>
<para>
Display the version and exit.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-h, --help</term>
<listitem>
<para>
Display command usage.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-r <replaceable>ROWS</replaceable>, --rows=<replaceable>ROWS</replaceable></term>
<listitem>
<para>
Specify the height of the block of cells, in rows. Must be greater than zero. Defaults to 1 when
the -w option is not present. Defaults to the number of input rows when the -w option is present.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-c <replaceable>COLS</replaceable>, --cols=<replaceable>COLS</replaceable></term>
<listitem>
<para>
Specify the width of the block of cells, in columns. Must be greater than zero. Defaults to 1 when
the -w option is not present. Defaults to the number of input columns when the -w option is present.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-s <replaceable>SHEET</replaceable>, --sheet=<replaceable>SHEET</replaceable></term>
<listitem>
<para>
The sheet in the ODS file to read/modify. Must be greater than zero; defaults to 1.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-w</term>
<listitem>
<para>
If specified, the spreadsheet will be modified with data from standard input. If not specified,
the cells from the spreadsheet will be written to standard output.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>file.ods</replaceable></term>
<listitem>
<para>
The ODS file to be read from or modified.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable>cell</replaceable></term>
<listitem>
<para>
The top-left cell of the block of cells to be read from or modified. This should be specified
in normal spreadsheet format, e.g. "A1" or "BA23".
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Example</title>
<screen>
odscell foo.ods A4 # display value in cell A4 on sheet 1 of foo.ods
odscell -r 2 -c 2 foo.ods B2 # display values for cells B2,B3,C2,C3 on sheet 1 of foo.ods
echo "hello,world,garbage" | odscell -c 2 -w foo.ods A1 # write "hello" to cell A1 and "world" to cell A2 on sheet 1 of foo.ods
cat bar.csv | odscell -s 2 -w foo.ods A1 # put the CSV data from bar.csv into sheet 2 of foo.ods
</screen>
</refsect1>
</refentry>

View File

@ -0,0 +1,15 @@
all: odf odt2tracwiki.1
txt: odt2tracwiki.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

View File

@ -0,0 +1,57 @@
'\" t
.\" Title: odt2tracwiki
.\" Author: S\(/oren Roug
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: 01/09/2013
.\" Manual: User commands
.\" Source: odfpy
.\" Language: English
.\"
.TH "ODT2TRACWIKI" "1" "01/09/2013" "odfpy" "User commands"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
odt2tracwiki \- Convert ODF to Trac Wiki format
.SH "SYNOPSIS"
.HP \w'\fBodt2tracwiki\fR\ 'u
\fBodt2tracwiki\fR \fIpath\fR
.SH "DESCRIPTION"
.PP
\fBodt2tracwiki\fR
is a program that will create a Trac Wiki format\&. See http://trac\&.edgewall\&.org/\&. I expect to add a feature that will upload the page with images to a Trac Wiki page using XML\-RPC\&. http://trac\-hacks\&.org/wiki/XmlRpcPlugin
.PP
"Path" is assumed to be an OpenDocument file of text, spreadsheet or presentation type\&.
.SH "EXAMPLE"
.sp
.if n \{\
.RS 4
.\}
.nf
odt2tracwiki odf\-file
.fi
.if n \{\
.RE
.\}
.SH "AUTHOR"
.PP
\fBS\(/oren Roug\fR
.RS 4
Original author
.RE

View File

@ -0,0 +1,46 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="odt2tracwiki">
<refentryinfo>
<productname>odfpy</productname>
<author><firstname>Søren</firstname><surname>Roug</surname>
<contrib>Original author</contrib>
</author>
</refentryinfo>
<refmeta>
<refentrytitle>odt2tracwiki</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>odt2tracwiki</refname>
<refpurpose>Convert ODF to Trac Wiki format</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>odt2tracwiki</command>
<arg choice="plain"><replaceable>path</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para><command>odt2tracwiki</command> is a program that will create
a Trac Wiki format.
See http://trac.edgewall.org/.
I expect to add a feature that will upload the page with images to a Trac Wiki page
using XML-RPC. http://trac-hacks.org/wiki/XmlRpcPlugin
</para>
<para>
&quot;Path&quot; is assumed to be an
OpenDocument file of text, spreadsheet or presentation type.
</para>
</refsect1>
<refsect1><title>Example</title>
<screen>
odt2tracwiki odf-file
</screen>
</refsect1>
</refentry>

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
import sys
from odf.odf2moinmoin import ODF2MoinMoin
if __name__ == "__main__":
odt = ODF2MoinMoin(sys.argv[1])
out_utf8 = odt.toString().encode("utf-8")
sys.stdout.write(out_utf8)

View File

@ -0,0 +1,15 @@
all: odf odf2zwikimoin.1
txt: odf2zwikimoin.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

View File

@ -0,0 +1,51 @@
'\" t
.\" Title: odf2zwikimoin
.\" Author: [FIXME: author] [see http://docbook.sf.net/el/author]
.\" Generator: DocBook XSL Stylesheets v1.76.1 <http://docbook.sf.net/>
.\" Date: 01/09/2013
.\" Manual: [FIXME: manual]
.\" Source: [FIXME: source]
.\" Language: English
.\"
.TH "ODF2ZWIKIMOIN" "1" "01/09/2013" "[FIXME: source]" "[FIXME: manual]"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" http://bugs.debian.org/507673
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "NAME"
odf2zwikimoin \- Convert ODF to ZWiki MoinMoin format
.SH "SYNOPSIS"
.HP \w'\fBodf2zwikimoin\fR\ 'u
\fBodf2zwikimoin\fR \fIpath\fR
.SH "DESCRIPTION"
.PP
\fBodf2zwikimoin\fR
is a program that will create a ZWiki MoinMoin format\&. See http://zwiki\&.org/\&.
.PP
"Path" is assumed to be an OpenDocument file of text, spreadsheet or presentation type\&.
.SH "EXAMPLE"
.sp
.if n \{\
.RS 4
.\}
.nf
odf2zwikimoin odf\-file
.fi
.if n \{\
.RE
.\}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="odf2zwikimoin">
<refnamediv>
<refname>odf2zwikimoin</refname>
<refpurpose>Convert ODF to ZWiki MoinMoin format</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>odf2zwikimoin</command>
<arg choice="plain"><replaceable>path</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1><title>Description</title>
<para><command>odf2zwikimoin</command> is a program that will create
a ZWiki MoinMoin format.
See http://zwiki.org/.
</para>
<para>
&quot;Path&quot; is assumed to be an
OpenDocument file of text, spreadsheet or presentation type.
</para>
</refsect1>
<refsect1><title>Example</title>
<screen>
odf2zwikimoin odf-file
</screen>
</refsect1>
</refentry>

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
import sys
from odf.odf2moinmoin import ODF2MoinMoin
class ODF2ZWikiMoin(ODF2MoinMoin):
def __init__(self, filepath):
super(ODF2ZWikiMoin, self).__init__(filepath)
self.baseURL = "BaseURL"
def draw_image(self, node):
"""
"""
link = node.getAttribute("xlink:href")
if link and link[:2] == './': # Indicates a sub-object, which isn't supported
return "%s\n" % link
if link and link[:9] == 'Pictures/':
link = self.baseURL + "/" + link[9:]
return "%s\n" % link
def text_line_break(self, node):
return "\n"
if __name__ == "__main__":
odt = ODF2ZWikiMoin(sys.argv[1])
out_utf8 = odt.toString().encode("utf-8")
sys.stdout.write(out_utf8)

View File

@ -0,0 +1,533 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
"""
import pdb; pdb.set_trace()
odt2moinmoin
=======
odt2moinmoin converts files in Open Document Text format (ODT) into
ZWiki MoinMoin-formatted plain text.
Written by by [Yuri Takhteyev](http://www.freewisdom.org).
Project website: http://www.freewisdom.org/projects/odt2txt/
Contact: yuri [at] freewisdom.org
License: GPL 2 (http://www.gnu.org/copyleft/gpl.html) or BSD
Version: 0.1 (April 7, 2006)
"""
import sys, zipfile, xml.dom.minidom
from odf.namespaces import nsdict
from odf.elementtypes import *
IGNORED_TAGS = [
'draw:a'
'draw:g',
'draw:line',
'draw:object-ole',
'office:annotation',
'svg:desc',
] + [ nsdict[item[0]]+":"+item[1] for item in empty_elements]
INLINE_TAGS = [ nsdict[item[0]]+":"+item[1] for item in inline_elements]
FOOTNOTE_STYLES = ["Footnote"]
class TextProps:
""" Holds properties for a text style. """
def __init__ (self):
self.italic = False
self.bold = False
self.fixed = False
self.underlined = False
def setItalic (self, value):
if value == "italic":
self.italic = True
def setBold (self, value):
if value == "bold":
self.bold = True
def setFixed (self, value):
self.fixed = value
def __str__ (self):
return "[i=%s, h=i%s, fixed=%s]" % (str(self.italic),
str(self.bold),
str(self.fixed))
class ParagraphProps:
""" Holds properties of a paragraph style. """
def __init__ (self):
self.blockquote = False
self.headingLevel = 0
self.code = False
self.title = False
self.indented = 0
def setIndented (self, value):
self.indented = value
def setHeading (self, level):
self.headingLevel = level
def setTitle (self, value):
self.title = value
def setCode (self, value):
self.code = value
def __str__ (self):
return "[bq=%s, h=%d, code=%s]" % (str(self.blockquote),
self.headingLevel,
str(self.code))
class ListProperties:
""" Holds properties for a list style. """
def __init__ (self):
self.ordered = False
def setOrdered (self, value):
self.ordered = value
class OpenDocumentTextFile:
def __init__ (self, filepath):
self.footnotes = []
self.footnoteCounter = 0
self.textStyles = {"Standard": TextProps()}
self.paragraphStyles = {"Standard": ParagraphProps()}
self.listStyles = {}
self.fixedFonts = []
self.hasTitle = 0
self.baseURL = "BaseURL"
self.load(filepath)
def processFontDeclarations (self, fontDecl):
""" Extracts necessary font information from a font-declaration
element.
"""
for fontFace in fontDecl.getElementsByTagName("style:font-face"):
if fontFace.getAttribute("style:font-pitch") == "fixed":
self.fixedFonts.append(fontFace.getAttribute("style:name"))
def extractTextProperties (self, style, parent=None):
""" Extracts text properties from a style element. """
textProps = TextProps()
if parent:
parentProp = self.textStyles.get(parent, None)
if parentProp:
textProp = parentProp
textPropEl = style.getElementsByTagName("style:text-properties")
if not textPropEl: return textProps
textPropEl = textPropEl[0]
italic = textPropEl.getAttribute("fo:font-style")
bold = textPropEl.getAttribute("fo:font-weight")
textProps.setItalic(italic)
textProps.setBold(bold)
if textPropEl.getAttribute("style:font-name") in self.fixedFonts:
textProps.setFixed(True)
return textProps
def extractParagraphProperties (self, style, parent=None):
""" Extracts paragraph properties from a style element. """
paraProps = ParagraphProps()
name = style.getAttribute("style:name")
if name.startswith("Heading_20_"):
level = name[11:]
try:
level = int(level)
paraProps.setHeading(level)
except:
level = 0
if name == "Title":
paraProps.setTitle(True)
paraPropEl = style.getElementsByTagName("style:paragraph-properties")
if paraPropEl:
paraPropEl = paraPropEl[0]
leftMargin = paraPropEl.getAttribute("fo:margin-left")
if leftMargin:
try:
leftMargin = float(leftMargin[:-2])
if leftMargin > 0.01:
paraProps.setIndented(True)
except:
pass
textProps = self.extractTextProperties(style)
if textProps.fixed:
paraProps.setCode(True)
return paraProps
def processStyles(self, styleElements):
""" Runs through "style" elements extracting necessary information.
"""
for style in styleElements:
name = style.getAttribute("style:name")
if name == "Standard": continue
family = style.getAttribute("style:family")
parent = style.getAttribute("style:parent-style-name")
if family == "text":
self.textStyles[name] = self.extractTextProperties(style,
parent)
elif family == "paragraph":
self.paragraphStyles[name] = (
self.extractParagraphProperties(style,
parent))
def processListStyles (self, listStyleElements):
for style in listStyleElements:
name = style.getAttribute("style:name")
prop = ListProperties()
if style.childNodes:
if ( style.childNodes[0].tagName
== "text:list-level-style-number" ):
prop.setOrdered(True)
self.listStyles[name] = prop
def load(self, filepath):
""" Loads an ODT file. """
zip = zipfile.ZipFile(filepath)
styles_doc = xml.dom.minidom.parseString(zip.read("styles.xml"))
self.processFontDeclarations(styles_doc.getElementsByTagName(
"office:font-face-decls")[0])
self.processStyles(styles_doc.getElementsByTagName("style:style"))
self.processListStyles(styles_doc.getElementsByTagName(
"text:list-style"))
self.content = xml.dom.minidom.parseString(zip.read("content.xml"))
self.processFontDeclarations(self.content.getElementsByTagName(
"office:font-face-decls")[0])
self.processStyles(self.content.getElementsByTagName("style:style"))
self.processListStyles(self.content.getElementsByTagName(
"text:list-style"))
def compressCodeBlocks(self, text):
""" Removes extra blank lines from code blocks. """
lines = text.split("\n")
buffer = []
numLines = len(lines)
for i in range(numLines):
if (lines[i].strip() or i == numLines-1 or i == 0 or
not ( lines[i-1].startswith(" ")
and lines[i+1].startswith(" ") ) ):
buffer.append("\n" + lines[i])
return ''.join(buffer)
def listToString (self, listElement, indent = 0):
buffer = []
styleName = listElement.getAttribute("text:style-name")
props = self.listStyles.get(styleName, ListProperties())
i = 0
for item in listElement.childNodes:
buffer.append(" "*indent)
i += 1
if props.ordered:
number = str(i)
number = " " + number + ". "
buffer.append(" 1. ")
else:
buffer.append(" * ")
subitems = [el for el in item.childNodes
if el.tagName in ["text:p", "text:h", "text:list"]]
for subitem in subitems:
if subitem.tagName == "text:list":
buffer.append("\n")
buffer.append(self.listToString(subitem, indent+3))
else:
buffer.append(self.paragraphToString(subitem, indent+3))
buffer.append("\n")
return ''.join(buffer)
def tableToString (self, tableElement):
""" MoinMoin used || to delimit table cells
"""
buffer = []
for item in tableElement.childNodes:
if item.tagName == "table:table-header-rows":
buffer.append(self.tableToString(item))
if item.tagName == "table:table-row":
buffer.append("\n||")
for cell in item.childNodes:
buffer.append(self.paragraphToString(cell))
buffer.append("||")
return ''.join(buffer)
def toString (self):
""" Converts the document to a string. """
body = self.content.getElementsByTagName("office:body")[0]
text = body.childNodes[0]
buffer = []
paragraphs = [el for el in text.childNodes
if el.tagName in ["text:p", "text:h","text:section",
"text:list", "table:table"]]
for paragraph in paragraphs:
if paragraph.tagName == "text:list":
text = self.listToString(paragraph)
elif paragraph.tagName == "text:section":
text = self.textToString(paragraph)
elif paragraph.tagName == "table:table":
text = self.tableToString(paragraph)
else:
text = self.paragraphToString(paragraph)
if text:
buffer.append(text)
if self.footnotes:
buffer.append("----")
for cite, body in self.footnotes:
buffer.append("%s: %s" % (cite, body))
buffer.append("")
return self.compressCodeBlocks('\n\n'.join(buffer))
def textToString(self, element):
buffer = []
for node in element.childNodes:
if node.nodeType == xml.dom.Node.TEXT_NODE:
buffer.append(node.nodeValue)
elif node.nodeType == xml.dom.Node.ELEMENT_NODE:
tag = node.tagName
if tag == "text:note":
cite = (node.getElementsByTagName("text:note-citation")[0]
.childNodes[0].nodeValue)
body = (node.getElementsByTagName("text:note-body")[0]
.childNodes[0])
self.footnotes.append((cite, self.textToString(body)))
buffer.append("^%s^" % cite)
elif tag == "text:s":
try:
num = int(node.getAttribute("text:c"))
buffer.append(" "*num)
except:
buffer.append(" ")
elif tag == "text:tab":
buffer.append(" ")
elif tag == "text:a":
text = self.textToString(node)
link = node.getAttribute("xlink:href")
if link.strip() == text.strip():
buffer.append("[%s] " % link.strip())
else:
buffer.append("[%s %s] " % (link.strip(), text.strip()))
elif tag == "draw:image":
link = node.getAttribute("xlink:href")
if link and link[:2] == './': # Indicates a sub-object, which isn't supported
continue
if link and link[:9] == 'Pictures/':
link = self.baseURL + "/" + link[9:]
buffer.append("%s\n" % link)
elif tag == "text:line-break":
buffer.append("\n")
elif tag in ("draw:text-box", "draw:frame"):
text = self.textToString(node)
buffer.append(text)
elif tag in ("text:p", "text:h"):
text = self.paragraphToString(node)
if text:
buffer.append(text + "\n\n")
elif tag in IGNORED_TAGS:
pass
elif tag in INLINE_TAGS:
text = self.textToString(node)
if not text.strip():
continue # don't apply styles to white space
styleName = node.getAttribute("text:style-name")
style = self.textStyles.get(styleName, TextProps())
if style.fixed:
buffer.append("`" + text + "`")
continue
if style:
if style.italic and style.bold:
mark = "'''''"
elif style.italic:
mark = "''"
elif style.bold:
mark = "'''"
else:
mark = ""
else:
mark = "/" + styleName + "/"
buffer.append("%s%s%s " % (mark, text, mark))
else:
buffer.append(" {" + tag + "} ")
return ''.join(buffer)
def paragraphToString(self, paragraph, indent = 0):
dummyParaProps = ParagraphProps()
style_name = paragraph.getAttribute("text:style-name")
paraProps = self.paragraphStyles.get(style_name, dummyParaProps)
text = self.textToString(paragraph)
#print style_name
if paraProps and not paraProps.code:
text = text.strip()
if paraProps.title:
self.hasTitle = 1
return "= " + text + " =\n"
outlinelevel = paragraph.getAttribute("text:outline-level")
if outlinelevel:
level = int(outlinelevel)
if self.hasTitle: level += 1
if level >= 1:
return "\n" + "=" * level + " " + text + " " + "=" * level + "\n"
elif paraProps.code:
return "{{{\n" + text + "\n}}}\n"
if paraProps.indented:
return self.wrapParagraph(text, indent = indent, blockquote = True)
else:
return self.wrapParagraph(text, indent = indent)
def wrapParagraph(self, text, indent = 0, blockquote=False):
counter = 0
buffer = []
LIMIT = 50
if blockquote:
buffer.append(" ")
return ''.join(buffer) + text
for token in text.split():
if counter > LIMIT - indent:
buffer.append("\n" + " "*indent)
if blockquote:
buffer.append(" ")
counter = 0
buffer.append(token + " ")
counter += len(token)
return ''.join(buffer)
if __name__ == "__main__":
odt = OpenDocumentTextFile(sys.argv[1])
unicode = odt.toString()
out_utf8 = unicode.encode("utf-8")
sys.stdout.write(out_utf8)

View File

@ -0,0 +1,4 @@
Example of script that extracts a table from an SQLite database and produces a spreadsheet.
Issues:
Numbers in the table become strings in the spreadsheet.

View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
import os, sys
import sqlite
from odf.opendocument import OpenDocumentSpreadsheet
from odf.style import Style, TextProperties, ParagraphProperties, TableColumnProperties
from odf.text import P
from odf.table import Table, TableColumn, TableRow, TableCell
if len(sys.argv) != 3:
print "Usage: sqlite-db table"
sys.exit(2)
sqldb = sys.argv[1]
sqltable = sys.argv[2]
textdoc = OpenDocumentSpreadsheet()
# Create a style for the table content. One we can modify
# later in the word processor.
tablecontents = Style(name="Table Contents", family="paragraph")
tablecontents.addElement(ParagraphProperties(numberlines="false", linenumber="0"))
#tablecontents.addElement(TextProperties(fontweight="bold"))
textdoc.styles.addElement(tablecontents)
# Create automatic styles for the column widths.
# We want two different widths, one in inches, the other one in metric.
# ODF Standard section 15.9.1
widthshort = Style(name="Wshort", family="table-column")
widthshort.addElement(TableColumnProperties(columnwidth="1.7cm"))
textdoc.automaticstyles.addElement(widthshort)
widthwide = Style(name="Wwide", family="table-column")
widthwide.addElement(TableColumnProperties(columnwidth="1.5in"))
textdoc.automaticstyles.addElement(widthwide)
# Start the table, and describe the columns
table = Table(name=sqltable)
#table.addElement(TableColumn(numbercolumnsrepeated=4,stylename=widthshort))
#table.addElement(TableColumn(numbercolumnsrepeated=3,stylename=widthwide))
cx = sqlite.connect(sqldb)
cu = cx.cursor()
cu.execute("select * from %s" % sqltable)
for row in cu.fetchall():
tr = TableRow()
table.addElement(tr)
for val in row:
tc = TableCell()
tr.addElement(tc)
if type(val) == type(''):
textval = unicode(val,'utf-8')
else:
textval = str(val)
p = P(stylename=tablecontents,text=textval)
tc.addElement(p)
cx.close()
textdoc.spreadsheet.addElement(table)
textdoc.save(sqltable, True)

View File

@ -0,0 +1,14 @@
all: odf syntaxhighlight.1
txt: syntaxhighlight.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.txt odf
odf:
ln -s ../../odf

View File

@ -0,0 +1,220 @@
.\" Title: syntaxhighlight
.\" Author: S\(/oren Roug
.\" Generator: DocBook XSL Stylesheets v1.74.0 <http://docbook.sf.net/>
.\" Date: 03/08/2009
.\" Manual: User commands
.\" Source: odfpy
.\" Language: English
.\"
.TH "SYNTAXHIGHLIGHT" "1" "03/08/2009" "odfpy" "User commands"
.\" -----------------------------------------------------------------
.\" * (re)Define some macros
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" toupper - uppercase a string (locale-aware)
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de toupper
.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
\\$*
.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH-xref - format a cross-reference to an SH section
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de SH-xref
.ie n \{\
.\}
.toupper \\$*
.el \{\
\\$*
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH - level-one heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SH
.\" put an extra blank line of space above the head in non-TTY output
.if t \{\
.sp 1
.\}
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[an-margin]u
.ti 0
.HTML-TAG ".NH \\n[an-level]"
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
\." make the size of the head bigger
.ps +3
.ft B
.ne (2v + 1u)
.ie n \{\
.\" if n (TTY output), use uppercase
.toupper \\$*
.\}
.el \{\
.nr an-break-flag 0
.\" if not n (not TTY), use normal case (not uppercase)
\\$1
.in \\n[an-margin]u
.ti 0
.\" if not n (not TTY), put a border/line under subheading
.sp -.6
\l'\n(.lu'
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SS - level-two heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SS
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[IN]u
.ti \\n[SN]u
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.ps \\n[PS-SS]u
\." make the size of the head bigger
.ps +2
.ft B
.ne (2v + 1u)
.if \\n[.$] \&\\$*
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BB/BE - put background/screen (filled box) around block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BB
.if t \{\
.sp -.5
.br
.in +2n
.ll -2n
.gcolor red
.di BX
.\}
..
.de EB
.if t \{\
.if "\\$2"adjust-for-leading-newline" \{\
.sp -1
.\}
.br
.di
.in
.ll
.gcolor
.nr BW \\n(.lu-\\n(.i
.nr BH \\n(dn+.5v
.ne \\n(BHu+.5v
.ie "\\$2"adjust-for-leading-newline" \{\
\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.el \{\
\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.in 0
.sp -.5v
.nf
.BX
.in
.sp .5v
.fi
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BM/EM - put colored marker in margin next to block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BM
.if t \{\
.br
.ll -2n
.gcolor red
.di BX
.\}
..
.de EM
.if t \{\
.br
.di
.ll
.gcolor
.nr BH \\n(dn
.ne \\n(BHu
\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[]
.in 0
.nf
.BX
.in
.fi
.\}
..
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "Name"
syntaxhighlight \- Create OpenDocument with syntax highlighted programming code
.SH "Synopsis"
.fam C
.HP \w'\fBsyntaxhighlight\fR\ 'u
\fBsyntaxhighlight\fR [\-e\ \fIencoding\fR] [\-l\ \fIlanguage\fR] [\fIinputfile\fR] [\fIoutputfile\fR]
.fam
.SH "Description"
.PP
The syntaxhighlight program will read a source code file, format it with syntax highlighting and write an OpenDocument text file\&.
.SH "Options"
.PP
\-e \fIencoding\fR
.RS 4
Enter the encoding of the source file\&. Common encodings are: iso\-8859\-1, cp1252, ascii and utf\-8 (default)\&.
.RE
.PP
\-l \fIlanguage\fR
.RS 4
Programming language of the input file\&. If not specified, it is guessed fron the file ending\&. Values can be: Python, HTML, C, C++ and PHP\&.
.RE
.SH "Example"
.sp
.if n \{\
.RS 4
.\}
.fam C
.ps -1
.nf
.if t \{\
.sp -1
.\}
.BB lightgray adjust-for-leading-newline
.sp -1
syntaxhighlight \-e cp1252 \-l Python example\&.py example\&.odt
.EB lightgray adjust-for-leading-newline
.if t \{\
.sp 1
.\}
.fi
.fam
.ps +1
.if n \{\
.RE
.\}
.SH "Author"
.PP
\fBS\(/oren Roug\fR
.RS 4
Original author
.RE

View File

@ -0,0 +1,72 @@
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="syntaxhighlight">
<refentryinfo>
<productname>odfpy</productname>
<author>
<firstname>Søren</firstname>
<surname>Roug</surname>
<contrib>Original author</contrib>
</author>
</refentryinfo>
<refmeta>
<refentrytitle>syntaxhighlight</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>syntaxhighlight</refname>
<refpurpose>Create OpenDocument with syntax highlighted programming code</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>syntaxhighlight</command>
<arg choice="opt">-e <replaceable>encoding</replaceable></arg>
<arg choice="opt">-l <replaceable>language</replaceable></arg>
<arg>
<replaceable>inputfile</replaceable>
</arg>
<arg>
<replaceable>outputfile</replaceable>
</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
The syntaxhighlight program will read a source code file,
format it with syntax highlighting and write an OpenDocument text file.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term>-e <replaceable>encoding</replaceable></term>
<listitem>
<para>
Enter the encoding of the source file. Common encodings are: iso-8859-1,
cp1252, ascii and utf-8 (default).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-l <replaceable>language</replaceable></term>
<listitem>
<para>
Programming language of the input file. If not specified,
it is guessed fron the file ending.
Values can be:
Python, HTML, C, C++ and PHP.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Example</title>
<screen>
syntaxhighlight -e cp1252 -l Python example.py example.odt
</screen>
</refsect1>
</refentry>

View File

@ -0,0 +1,508 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
#
# Syntax Highlighting
# Originally from Peter Clive Wilkinson (http://www.petersblog.org/node/763)
#
import os, sys, re, getopt
from odf.opendocument import OpenDocumentText
from odf.style import FontFace, Style, TextProperties, ParagraphProperties
from odf.text import P, Span, S
class Highlight:
"""
Do syntax highlighting.
"""
courierfont = FontFace(name="Courier", fontfamily="Courier",
fontadornments="Normal", fontfamilygeneric="modern", fontpitch="fixed")
#--- Paragraph style --
programliststyle = Style(name="Program Listing", family="paragraph")
programliststyle.addElement(ParagraphProperties(border="0.002cm solid #000000", margin="0cm", padding="0.2cm"))
programliststyle.addElement(TextProperties(fontname="Courier", fontsize="9pt", language="none", country="none"))
#--- Text styles --
puncstyle = Style(name="Highlighted Punctuation", family="text")
puncstyle.addElement(TextProperties(fontweight="bold")) # Bold
numberstyle = Style(name="Highlighted Number", family="text")
numberstyle.addElement(TextProperties(color="#ff0000")) # Red
keywordstyle = Style(name="Highlighted Keyword", family="text")
keywordstyle.addElement(TextProperties(color="#b218b2", fontweight="bold")) # Blue, bold
variablestyle = Style(name="Highlighted Magic", family="text")
variablestyle.addElement(TextProperties(color="#0000ff")) # Blue
tagstyle = Style(name="Highlighted Tag", family="text")
tagstyle.addElement(TextProperties(color="#800000")) # Darkred
attrstyle = Style(name="Highlighted Tag", family="text")
attrstyle.addElement(TextProperties(color="#008000", fontweight="bold")) # Dark green bold
stringstyle = Style(name="Highlighted String", family="text")
stringstyle.addElement(TextProperties(color="#800000")) # Red
commentstyle = Style(name="Highlighted Comment", family="text")
commentstyle.addElement(TextProperties(color="#0000ff", fontstyle="italic")) # Blue, Italic
preprocstyle = Style(name="Highlighted Preprocessing", family="text")
preprocstyle.addElement(TextProperties(color="#ff00ff", fontstyle="italic")) # Magenta, Italic
def __init__(self, strMode):
"""
Initialise highlighter: strMode = language (PYTHON, C, CPP, PHP, HTML)
"""
self.textdoc = OpenDocumentText()
self.textdoc.fontfacedecls.addElement(self.courierfont)
self.textdoc.styles.addElement(self.programliststyle)
self.textdoc.styles.addElement(self.puncstyle)
self.textdoc.styles.addElement(self.numberstyle)
self.textdoc.styles.addElement(self.keywordstyle)
self.textdoc.styles.addElement(self.variablestyle)
self.textdoc.styles.addElement(self.tagstyle)
self.textdoc.styles.addElement(self.attrstyle)
self.textdoc.styles.addElement(self.stringstyle)
self.textdoc.styles.addElement(self.commentstyle)
self.textdoc.styles.addElement(self.preprocstyle)
self.strSpanStyle = None
self.currPara = P(stylename=self.programliststyle)
self.textdoc.text.addElement(self.currPara)
self.currSpan = None
if strMode == 'CPP':
strMode = 'C'
self.strSuppressTokens = []
elif strMode == 'C':
self.strSuppressTokens = ['CPPKEYWORD']
else:
self.strSuppressTokens = []
self.strMode = strMode
def PythonHighlightToken(self, strTok, oMatch, strStyle):
"""
Callback for python specific highlighting.
"""
#
# Input matches this type.
#
strValue = oMatch.group()
if strTok == 'MULTILINESTRING':
#
# If not inside a multiline string then start one now.
#
self.ChangeStyle(strStyle)
self.WriteContent(strValue)
#
# Remember you are in a string and remember how it was
# started (""" vs ''')
#
self.strMultilineString = oMatch.group(1)
return 'PythonMultilineString'
elif strTok == 'ENDMULTILINESTRING':
#
# Multiline Token found within a multiline string
#
if oMatch.group(1) == self.strMultilineString:
#
# Token is end of multiline so stop here.
#
self.WriteMultiline(strValue)
self.strMultilineString = ''
return 'PYTHON'
self.ChangeStyle(strStyle)
self.WriteContent(strValue)
def CHighlightToken(self, strTok, oMatch, strStyle):
"""
Callback for C specific highlighting.
"""
#
# Input matches this type.
#
strValue = oMatch.group()
#
# Not in multiline mode so change display style as appropriate
# and output the text.
#
self.ChangeStyle(strStyle)
self.WriteContent(strValue)
def PHPHighlightToken(self, strTok, oMatch, strStyle):
"""
Callback for PHP specific highlighting.
"""
#
# Input matches this type.
#
strValue = oMatch.group()
if strTok == 'MULTILINESTRING':
#
# If not inside a multiline string then start one now.
#
self.ChangeStyle(strStyle)
self.WriteContent(strValue)
#
# Remember you are in a string and remember how it was
# started (""" vs ''')
#
self.strMultilineString = oMatch.group(1)
return 'PHPMultilineString'
elif strTok == 'ENDMULTILINESTRING':
#
# Multiline Token found within a multiline string
#
if oMatch.group(1) == self.strMultilineString:
#
# Token is end of multiline so stop here.
#
self.WriteMultiline(strValue)
self.strMultilineString = ''
return 'PHP'
self.ChangeStyle(strStyle)
self.WriteContent(strValue)
if strTok == 'GOTOHTML':
#
# Embedded HTML
#
return 'HTML'
else:
return None
def HTMLHighlightToken(self, strTok, oMatch, strStyle):
"""
Callback for HTML specific highlighting.
"""
#
# Input matches this type.
#
strValue = oMatch.group()
self.ChangeStyle(strStyle)
self.WriteContent(strValue)
if strTok == 'TAG':
#
# Change to mode 1, 'within tag'.
#
return 'HTMLTag'
elif strTok == 'ENDTAG':
#
# Change to mode 1, 'within tag'.
#
return 'HTML'
elif strTok == 'GOTOPHP':
#
# Embedded PHP
#
return 'PHP'
else:
#
# No state change.
#
return None
oStyles = {
'PYTHON': ( PythonHighlightToken,
(
('PUNC', re.compile( r'[-+*!|&^~/%\=<>\[\]{}(),.:]'), puncstyle),
('NUMBER', re.compile( r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'), numberstyle),
('KEYWORD', re.compile( r'(def|class|break|continue|del|exec|finally|pass|' +
r'print|raise|return|try|except|global|assert|lambda|' +
r'yield|for|while|if|elif|else|and|in|is|not|or|import|' +
r'from|True|False)(?![a-zA-Z0-9_])'), keywordstyle),
('MAGIC', re.compile( r'self|None'), variablestyle),
('MULTILINESTRING', re.compile( r'r?u?(\'\'\'|""")'), stringstyle),
('STRING', re.compile( r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'), stringstyle),
('IDENTIFIER', re.compile( r'[a-zA-Z_][a-zA-Z0-9_]*'), None),
('COMMENT', re.compile( r'\#.*'), commentstyle),
('NEWLINE', re.compile( r'\r?\n'), 'NewPara'),
('WHITESPACE', re.compile( r'[ ]+'), 'Keep'),
# if all else fails...
('UNKNOWN', re.compile( r'.'), None)
)),
'PythonMultilineString': ( PythonHighlightToken,
(
('ENDMULTILINESTRING', re.compile( r'.*?("""|\'\'\')', re.DOTALL), stringstyle),
('UNKNOWN', re.compile( r'.'), 'Keep')
)),
'C': ( CHighlightToken,
(
('COMMENT', re.compile( r'//.*\r?\n'), commentstyle),
('MULTILINECOMMENT', re.compile( r'/\*.*?\*/', re.DOTALL), commentstyle),
('PREPROCESSOR', re.compile( r'\s*#.*?[^\\]\s*\n', re.DOTALL), preprocstyle),
('PUNC', re.compile( r'[-+*!&|^~/%\=<>\[\]{}(),.:]'), puncstyle),
('NUMBER', re.compile( r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'),
numberstyle),
('KEYWORD', re.compile( r'(sizeof|int|long|short|char|void|' +
r'signed|unsigned|float|double|' +
r'goto|break|return|continue|asm|' +
r'case|default|if|else|switch|while|for|do|' +
r'struct|union|enum|typedef|' +
r'static|register|auto|volatile|extern|const)(?![a-zA-Z0-9_])'), keywordstyle),
( 'CPPKEYWORD', re.compile( r'(class|private|protected|public|template|new|delete|' +
r'this|friend|using|inline|export|bool|throw|try|catch|' +
r'operator|typeid|virtual)(?![a-zA-Z0-9_])'), keywordstyle),
('STRING', re.compile( r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'), stringstyle),
('IDENTIFIER', re.compile( r'[a-zA-Z_][a-zA-Z0-9_]*'), None),
('NEWLINE', re.compile( r'\r?\n'), 'NewPara'),
('WHITESPACE', re.compile( r'[ ]+'), 'Keep'),
('UNKNOWN', re.compile( r'.'), None)
)),
'PHP': ( PHPHighlightToken,
(
('COMMENT', re.compile( r'//.*\r?\n'), commentstyle),
('MULTILINECOMMENT', re.compile( r'/\*.*?\*/', re.DOTALL), commentstyle),
('MULTILINESTRING', re.compile( r'<<<\s*([a-zA-Z0-9_]+)'), stringstyle),
('GOTOPHP', re.compile( r'<\?php'), stringstyle),
('PUNC', re.compile( r'[-+*!&|^~/%\=<>\[\]{}(),.:]'), puncstyle),
('NUMBER', re.compile( r'0x[0-9a-fA-F]+|[+-]?\d+(\.\d+)?([eE][+-]\d+)?|\d+'),
numberstyle),
('KEYWORD', re.compile( r'(declare|else|enddeclare|endswitch|elseif|endif|if|switch|' +
r'as|do|endfor|endforeach|endwhile|for|foreach|while|' +
r'case|default|switch|function|return|break|continue|exit|' +
r'var|const|boolean|bool|integer|int|real|double|float|string|' +
r'array|object|NULL|extends|implements|instanceof|parent|self|' +
r'include|require|include_once|require_once|new|true|false)(?![a-zA-Z0-9_])'), keywordstyle),
('STRING', re.compile( r'r?u?\'(.*?)(?<!\\)\'|"(.*?)(?<!\\)"'), stringstyle),
('VARIABLE', re.compile( r'\$[a-zA-Z_][a-zA-Z0-9_]*'), variablestyle),
('IDENTIFIER', re.compile( r'[a-zA-Z_][a-zA-Z0-9_]*'), None),
('WHITESPACE', re.compile( r'[ \r\n]+'), 'Keep'),
('GOTOHTML', re.compile( r'\?>'), stringstyle),
('UNKNOWN', re.compile( r'.'), None)
)),
'PHPMultilineString': ( PHPHighlightToken,
(
('ENDMULTILINESTRING', re.compile( r'.*?\n([a-zA-Z0-9_]+)', re.DOTALL), stringstyle),
('UNKNOWN', re.compile( r'.*?(?!\n)'), 'Keep')
)),
'HTML': ( HTMLHighlightToken,
# Mode 0: just look for tags
(
('COMMENT', re.compile( r'<!--[^>]*-->|<!>'), commentstyle),
('XMLCRAP', re.compile( r'<![^>]*>'), preprocstyle),
('SCRIPT', re.compile( r'<script .*?</script>', re.IGNORECASE + re.DOTALL), tagstyle),
('TAG', re.compile( r'</?\s*[a-zA-Z0-9]+'), tagstyle),
('GOTOPHP', re.compile( r'<\?php'), stringstyle),
('NEWLINE', re.compile( r'\r?\n'), 'NewPara'),
('UNKNOWN', re.compile( r'[^<]*'), None)
)),
# Mode 1: within tags,
'HTMLTag': ( HTMLHighlightToken,
(
('ENDTAG', re.compile( r'>'), tagstyle),
('ATTRIBUTE', re.compile( r'[a-zA-Z][a-zA-Z0-9:]*='), attrstyle),
('VALUE', re.compile( r'"[^"]*"'), stringstyle),
('NEWLINE', re.compile( r'\r?\n'), 'NewPara'),
('WHITESPACE', re.compile( r'[ \t\f\v]+'), None),
('UNKNOWN', re.compile( r'.'), None)
))
}
def generatedoc(self, strData):
"""
Syntax highlight some python code.
Returns html version of code.
"""
i = 0
strMode = self.strMode
#
# While input is not exhausted...
#
while i < len(strData):
#
# Compare current position with all possible display types.
#
try:
for strTok, oRE, strStyle in Highlight.oStyles[strMode][1]:
if not strTok in self.strSuppressTokens:
oMatch = oRE.match(strData, i)
if oMatch:
strNewMode = Highlight.oStyles[strMode][0](self, strTok, oMatch, strStyle)
if strNewMode != None:
strMode = strNewMode
i += len(oMatch.group())
break
else:
#
# Token not found so dump out raw text. This doesn't have to be bullet proof.
#
self.ChangeStyle(None)
self.WriteContent(strData[i])
i += 1
except:
raise
#
# Terminate any styles in use.
#
self.ChangeStyle(None)
#
# Expand tabs to 4 spaces.
# Doesn't matter if this number is wrong, the indentation will be butt ugly anyhow.
#
return self.textdoc
def WriteSingleline(self, parent, data):
ls = len(data)
cnt = 0
textstart = 0
i = -1
for i in xrange(ls):
if data[i] == ' ':
if cnt == 0:
# We found the first space. Now print the text before
parent.addText(data[textstart:i])
cnt = 0
textstart = i
cnt = cnt+1
else:
# We didn't see a space
# If there are unprinted spaces, print them now, if there are, then we're at text-start
if cnt > 0:
parent.addText(' ')
if cnt > 1:
parent.addElement(S(c=cnt-1))
if cnt > 0:
cnt = 0
textstart = i
if cnt > 0:
parent.addText(' ')
if cnt > 1:
parent.addElement(S(c=cnt-1))
elif i != -1:
parent.addText(data[textstart:i+1])
def WriteMultiline(self, data):
lines = data.split('\n')
self.currPara.addText(lines[0])
for line in lines[1:]:
self.currPara = P(stylename=self.programliststyle)
self.textdoc.text.addElement(self.currPara)
self.currSpan = Span(stylename=self.strSpanStyle)
self.WriteSingleline(self.currSpan, line)
self.currPara.addElement(self.currSpan)
def WriteContent(self, data):
"""
Write the content, but convert spaces to <text:s> first
"""
# re.compile( r'( )\1+(.+)')
if self.currSpan is None:
self.WriteSingleline(self.currPara, data)
else:
self.WriteSingleline(self.currSpan, data)
def ChangeStyle(self, strStyle):
"""
Generate output to change from existing style to another style only.
"""
#
# Output minimal formatting code: only output anything if the style has
# actually changed.
#
if self.strSpanStyle != strStyle:
if strStyle == 'NewPara':
self.currPara = P(stylename=self.programliststyle)
self.textdoc.text.addElement(self.currPara)
self.currSpan = None
self.strSpanStyle = None
elif strStyle != 'Keep':
if strStyle is None:
self.currSpan = None
else:
self.currSpan = Span(stylename=strStyle)
self.currPara.addElement(self.currSpan)
self.strSpanStyle = strStyle
def usage():
sys.stderr.write("Usage: %s [-l language] [-e encoding] inputfile outputfile\n" % sys.argv[0])
try:
opts, args = getopt.getopt(sys.argv[1:], "l:e:", ["language=", "encoding="])
except getopt.GetoptError:
usage()
sys.exit(2)
language = None
encoding = 'utf-8'
for o, a in opts:
if o in ("-l", "--language"):
language = a.upper()
if o in ("-e", "--encoding"):
encoding = a
if len(args) != 2:
usage()
sys.exit(2)
suffixes = {
'.py': 'PYTHON',
'.xhtml': 'HTML',
'.html': 'HTML',
'.htm': 'HTML',
'.c': 'C',
'.c++': 'CPP',
'.php': 'PHP'
}
inputfile = args[0]
outputfile = args[1]
if language is None:
try:
suffix = inputfile.lower().rindex('.')
language = suffixes[inputfile[suffix:]]
except:
usage()
sys.exit(2)
data = unicode(open(inputfile).read(),encoding)
Highlighted = Highlight(language).generatedoc(data)
Highlighted.save(args[1])

View File

@ -0,0 +1,7 @@
For some reason the script crashes on the make_parser() call in odf2xhtml.py,
but only when called from Trac.
To INSTALL:
Run python setup.py bdist_egg
Then copy the egg to the Trac plugin directory or Python's site-packages

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odfpreview import OdfPreview

View File

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from trac.core import *
from trac.mimeview.api import IHTMLPreviewRenderer
import os
from tempfile import mkstemp
from odf.odf2xhtml import ODF2XHTML
class ODF2XHTMLBody(ODF2XHTML):
def __init__(self):
ODF2XHTML.__init__(self, generate_css=False, embedable=True)
def rewritelink(self, imghref):
imghref = imghref.replace("Pictures/","index_html?pict=")
return imghref
class OdfPreview(Component):
"""Display OpenDocument as HTML."""
implements(IHTMLPreviewRenderer)
def get_quality_ratio(self, mimetype):
self.env.log.debug('Trac checking for %s' % mimetype)
if mimetype in ('application/vnd.oasis.opendocument.text',
'application/vnd.oasis.opendocument.text-template',
'application/vnd.oasis.opendocument.spreadsheet',
'application/vnd.oasis.opendocument.presentation'):
return 7
return 0
def render(self, req, input_type, content, filename=None, url=None):
self.env.log.debug('HTML output for ODF')
odhandler = ODF2XHTMLBody()
hfile, hfilename = mkstemp('tracodf')
try:
if hasattr(content,'read'):
os.write(hfile, content.read())
else:
os.write(hfile, content)
os.close(hfile)
out = odhandler.odf2xhtml(hfilename).encode('us-ascii','xmlcharrefreplace')
except:
self.env.log.error("odf2xhtml failed")
finally:
os.unlink(hfilename)
if out != '':
return out
return "<h1>HTML preview failed</h1>"
# def render(self, req, input_type, content, filename=None, url=None):
# self.env.log.debug('HTML output for ODF')
# hfilename = None
# odhandler = ODF2XHTML()
# if filename is not None:
# infile = filename
# else:
# hfile, hfilename = mkstemp('tracodf')
# if hasattr(content,'read'):
# os.write(hfile, content.read())
# else:
# os.write(hfile, content)
# os.close(hfile)
# infile = hfilename
# out = odhandler.odf2xhtml(infile).encode('us-ascii','xmlcharrefreplace')
# if hfilename is not None:
# os.unlink(hfilename)
# return out

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odftohtml import *

View File

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from trac.core import *
from trac.mimeview.api import IContentConverter
import os
import re
from odf.odf2xhtml import ODF2XHTML
class OdfToHtmlConverter(Component):
"""Convert OpenDocument to HTML."""
implements(IContentConverter)
# IContentConverter methods
def get_supported_conversions(self):
yield ('odt', 'OpenDocument Text', 'odt', 'application/vnd.oasis.opendocument.text', 'text/html', 7)
yield ('ott', 'OpenDocument Text', 'ott', 'application/vnd.oasis.opendocument.text-template', 'text/html', 7)
yield ('ods', 'OpenDocument Spreadsheet', 'ods', 'application/vnd.oasis.opendocument.spreadsheet', 'text/html', 7)
yield ('odp', 'OpenDocument Presentation', 'odp', 'application/vnd.oasis.opendocument.presentation', 'text/html', 7)
def convert_content(self, req, input_type, source, output_type):
odhandler = ODF2XHTML()
out = odhandler.odf2xhtml(source).encode('us-ascii','xmlcharrefreplace')
self.env.log.debug('HTML output for ODF')
return (out, 'text/html')

View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from setuptools import setup
PACKAGE = 'OdfConversion'
VERSION = '0.1'
setup(name='OdfConversion',
version='0.1',
packages=['odfpreview','odftohtml'],
author='Soren Roug',
author_email='soren.roug@eea.europa.eu',
description='A plugin for viewing ODF documents as HTML',
url='http://trac-hacks.org/wiki/OdfConversion',
entry_points={'trac.plugins': ['odfpreview.odfpreview=odfpreview.odfpreview',
'odftohtml.odftohtml=odftohtml.odftohtml']})

14
contrib/xliff/README Normal file
View File

@ -0,0 +1,14 @@
This script will create a OpenDocument Text out of an XLIFF file.
The texts to translate are stored on <user-field-decl> elements.
Send the OpenDocument Text to the translator.
You can then extract the translations later with odfuserfields and in
principle put them back into the original XLIFF file, but this is an
exercise left to the reader.
Other projects on translation:
http://translate.sourceforge.net/wiki/toolkit/odf2xliff
http://translate.sourceforge.net/wiki/developers/projects/odf
http://www.hforge.org/odf-i18n-tests

423
contrib/xliff/global.xlf Normal file
View File

@ -0,0 +1,423 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xliff SYSTEM "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">
<!-- XLIFF Format Copyright © OASIS Open 2001-2003 -->
<xliff version="1.0">
<file
original="global.txt"
product-name="Honoloko genxliff"
product-version="1.0"
datatype="plaintext"
source-language="en"
target-language="da"
date="2005-08-24T21:13:34Z"
>
<header>
</header>
<body>
<trans-unit id="intro">
<source>Welcome to our Island of Honoloko.</source>
<target>Velkommen til vores ø, Honoloko.</target>
</trans-unit>
<trans-unit id="introHealth">
<source>I am the Health Machine.</source>
<target>Jeg er Sundheds-maskinen.</target>
</trans-unit>
<trans-unit id="introEnergy">
<source>I am the Energy Robot.</source>
<target>Jeg er Energirobotten.</target>
</trans-unit>
<trans-unit id="introResources">
<source>I am the Resources Creature.</source>
<target>Jeg er Ressource-dyret.</target>
</trans-unit>
<trans-unit id="introFitness">
<source>I am the Fitness Bunny.</source>
<target>Jeg er Kondikaninen.</target>
</trans-unit>
<trans-unit id="introGroup">
<source>We will help you around Honoloko by giving you tips. Enjoy your time here, but be careful what you decide to do. Your actions will affect the environment and the health of our Island and of the people that live here.</source>
<target>Vi vil hjælpe dig rundt på Honoloko ved at give dig tips. Hav det sjovt her, men pas på, hvad du beslutter dig for at gøre. Dine handlinger vil påvirke miljøet og sundheden for øen og dens indbyggere.</target>
</trans-unit>
<trans-unit id="charChoose1">
<source>Would you like to be a breakdancer or a kung-fu master?</source>
<target>Vil du være breakdancer eller kung-fu mester?</target>
</trans-unit>
<trans-unit id="charChoose2">
<source>Would you like to be a Boy/Girl?</source>
<target>Vil du være dreng eller pige?</target>
</trans-unit>
<trans-unit id="charChoose3">
<source>Choose your name</source>
<target>Vælg dit navn</target>
</trans-unit>
<trans-unit id="instructions1">
<source>Hi</source>
<target>Hej</target>
</trans-unit>
<trans-unit id="instructions2">
<source>You are about to depart for our beautiful Island of Honoloko. Remember, answer the questions and think about the people and environment. Keep an eye on the gameboard so you can see the effect of your decisions.</source>
<target>Du skal snart af sted til vores smukke ø, Honoloko. Husk på at besvare spørgsmålene og at tænke på folk og miljø. Hold øje med spillebrættet, så du kan se dine beslutningers virkninger.</target>
</trans-unit>
<trans-unit id="l2">
<source>Choose which building you would like to build on the board:</source>
<target>Vælg, hvilken bygning du vil bygge på spillebrættet:</target>
</trans-unit>
<trans-unit id="l2c1">
<source>Sewage Plant</source>
<target>Rensningsanlæg</target>
</trans-unit>
<trans-unit id="l2c2">
<source>Luxury Hotel</source>
<target>Luksushotel</target>
</trans-unit>
<trans-unit id="l2c3">
<source>Car Factory</source>
<target>Bilfabrik</target>
</trans-unit>
<trans-unit id="l1">
<source>Choose how you would like to travel around the board:</source>
<target>Vælg hvordan du vil bevæge dig rundt på spillebrættet:</target>
</trans-unit>
<trans-unit id="l1c1">
<source>Car</source>
<target>Bil</target>
</trans-unit>
<trans-unit id="l1c2">
<source>Bus</source>
<target>Bus</target>
</trans-unit>
<trans-unit id="l1c3">
<source>Bike</source>
<target>Cykel</target>
</trans-unit>
<trans-unit id="l3">
<source>Choose how you would like to spend your time:</source>
<target>Vælg hvordan du vil bruge din tid:</target>
</trans-unit>
<trans-unit id="l3c1">
<source>Play</source>
<target>Lege</target>
</trans-unit>
<trans-unit id="l3c2">
<source>Laze</source>
<target>Dovne</target>
</trans-unit>
<trans-unit id="l3c3">
<source>Explore</source>
<target>Udforske</target>
</trans-unit>
<trans-unit id="score90to100">
<source>Well Done! You scored really well. You are obviously a master at breakdance/kung-fu and your coolness has helped you make the right decisions. The healthy, fit, happy people of lucious Honoloko throw a party in your honour. Please come back soon!</source>
<target>Godt gået! Du scorede mange points. Du er åbenbart mester i breakdance/kung-fu, og det har hjulpet dig til at træffe de rigtige beslutninger. De sunde, veltrænede og lykkelige folk på Honoloko holder en fest til ære for dig. Kom snart igen!</target>
</trans-unit>
<trans-unit id="score80to89">
<source>Excellent! You scored really well. Your advanced handling of the resources and environment has made the people of Honoloko very healthy and fit. Keep them happy and come back soon!</source>
<target>Glimrende! Du scorede mange points. Din dygtige håndtering af ressourcerne og miljøet har gjort folkene på Honoloko meget raske og veltrænede. Bliv ved med at gøre dem glade og kom snart igen!</target>
</trans-unit>
<trans-unit id="score70to79">
<source>Great. It seems like you know what you are doing when it comes to handling Honoloko's resources and energy. The island's people are fit and healthy. They want you to come back!</source>
<target>Flot. Du lader til at have styr på at håndtere Honolokos ressourcer og energi. Øens indbyggere er i god form og raske. De vil have, at du kommer tilbage!</target>
</trans-unit>
<trans-unit id="score60to69">
<source>Good. You scored above average, but you need to spend more attention on how you care for the environment and your health. You should practice more by playing again.</source>
<target>Godt. Du scorede flere points end gennemsnittet, men du skal være mere opmærksom på, hvordan du passer på miljøet og dit helbred. Du burde øve dig mere ved at spille spillet igen.</target>
</trans-unit>
<trans-unit id="score50to59">
<source>You scored OK but you did some damage too. You haven't improved our Island either and there is a lot you could do better. The people of Honoloko allow you to leave the Island without paying for the damage you have done. Come back to put it right!</source>
<target>Du klarede dig godt, men du forvoldte også nogen skade. Du har ikke forbedret øen, og der er meget, du kunne gøre bedre. Folkene på Honoloko vil lade dig forlade øen uden at betale for den skade, som du forvoldte. Kom tilbage og ret bod på skaden!</target>
</trans-unit>
<trans-unit id="score40to49">
<source>You scored OK but you did some damage too. You haven't improved our Island either and there is a lot you could do better. The people of Honoloko allow you to leave the Island without paying for the damage you have done. Come back to put it right!</source>
<target>Du klarede dig godt, men du forvoldte også nogen skade. Du har ikke forbedret øen, og der er meget, du kunne gøre bedre. Folkene på Honoloko vil lade dig forlade øen uden at betale for den skade, som du forvoldte. Kom tilbage og ret bod på skaden!</target>
</trans-unit>
<trans-unit id="score30to39">
<source>You scored below average and as a result the island is less healthy and more polluted than when you arrived. People are unhealthy and are wasting resources. You should come back to put it right.</source>
<target>Du scorede mindre end gennemsnittet, og som følge deraf er øen mindre sund og mere forurenet, end da du ankom. Folk er usunde og spilder ressourcer. Du burde komme tilbage og rette bod på skaden.</target>
</trans-unit>
<trans-unit id="score20to29">
<source>You scored pretty badly. You have managed to cause a lot of damage to the health and environment of Honoloko and its people. The people demand that you come back and repair the damage you inflicted on them. Click to play again.</source>
<target>Du scorede temmelig dårligt. Du har forvoldt stor skade på Honoloko og dens indbyggeres helbred og miljø. Folkene forlanger, at du kommer tilbage og udbedrer den skade, du har forvoldt dem. Klik for at spille igen.</target>
</trans-unit>
<trans-unit id="score10to19">
<source>Are you being serious! Is this the best you can do!? You've caused pollution and wasted our resources, and Honoloko is in a complete mess!! You need to change your attitude now. The unhealthy, overweight people of Honoloko won't let you leave the island. You have to stay and clear up the mess! Click to play again.</source>
<target>Tager du det overhovedet alvorligt? Er det virkelig dit bedste? Du har forårsaget forurening og spildt vores ressourcer. Honoloko er i suppedasen! Du må straks ændre din holdning. De usunde, overvægtige folk på Honoloko vil ikke lade dig forlade øen. Du må blive og rydde op i redeligheden! Klik for at spille igen.</target>
</trans-unit>
<trans-unit id="score0to09">
<source>You should be locked away! Armageddon is now upon us. You completely destroyed all the resources on Honoloko and its people are miserably unhealthy. How long do you think will you survive without food or water? Click to play again.</source>
<target>Du burde spærres inde! Dommedag er over os. Du har fuldstændig ødelagt alle ressourcer på Honoloko, og øens indbyggere er jammerligt usunde. Hvor længe tror du, du kan overleve uden mad og drikke? Klik for at spille igen.</target>
</trans-unit>
<trans-unit id="scoretop100">
<source>To enter our high score table and for the opportunity to win a prize enter your email and select your country here. Click here if you are not on line</source>
<target>Indtast din e-mail-adresse og vælg dit land her, hvis du vil på listen over topscorere og vinde en præmie. Klik her, hvis du ikke er tilsluttet internettet.</target>
</trans-unit>
<trans-unit id="scoreTop100Offline">
<source>Here is your code, write it down and when you are next online visit www.honoloko.com/score.php and enter your code.</source>
<target>Her er din adgangskode. Skriv den ned. Besøg www.honoloko.com/score.php næste gang du er på nettet, og indtast din kode.</target>
</trans-unit>
<trans-unit id="enviroScoreRes9to10">
<source>Congratulations, You've helped us use our resources wisely! Come back with your friends.</source>
<target>Tillykke. Du har hjulpet os med at bruge vores ressourcer fornuftigt! Kom tilbage med dine venner.</target>
</trans-unit>
<trans-unit id="enviroScoreRes7to8">
<source>Thanks, you've helped us keep on using our resources quite well!</source>
<target>Tak. Du har hjulpet os med at bruge vores ressourcer ganske godt!</target>
</trans-unit>
<trans-unit id="enviroScoreRes5to6">
<source>Your use of resources is average and can be improved.</source>
<target>Din anvendelse af ressourcer er gennemsnitlig og kan forbedres.</target>
</trans-unit>
<trans-unit id="enviroScoreRes3to4">
<source>Using so much of Honoloko's resources has proved disastrous. Better shape up next time.</source>
<target>Brug af så mange af Honolokos ressourcer har været katastrofalt. Tag dig sammen næste gang.</target>
</trans-unit>
<trans-unit id="enviroScoreRes0to2">
<source>You managed to waste all of Honoloko's natural resources in your short stay.</source>
<target>Det lykkedes dig at spilde alle Honolokos naturressourcer under dit korte ophold.</target>
</trans-unit>
<trans-unit id="enviroScoreEnergy9to10">
<source>Well done, you didn't waste any energy and you helped our environment!</source>
<target>Godt gået! Du spildte ingen energi, og du hjalp miljøet.</target>
</trans-unit>
<trans-unit id="enviroScoreEnergy7to8">
<source>Good work, you managed to use energy sensibly.</source>
<target>Flot! Du forvaltede energien fornuftigt.</target>
</trans-unit>
<trans-unit id="enviroScoreEnergy5to6">
<source>You could have done better, but at least you didn't blow the power station up!</source>
<target>Du kunne have klaret dig bedre, men du sprang i det mindste ikke kraftværket i luften!</target>
</trans-unit>
<trans-unit id="enviroScoreEnergy3to4">
<source>You wasted a huge amount of energy while you were here.</source>
<target>Du spildte en enorm mængde energi, mens du var her.</target>
</trans-unit>
<trans-unit id="enviroScoreEnergy0to2">
<source>You wasted so much energy that you blew up our power station. Thanks a lot!</source>
<target>Du spildte så megen energi, at kraftværket sprængte i luften. Tak skal du have!</target>
</trans-unit>
<trans-unit id="enviroScoreFit9to10">
<source>You are super fit! We all fancy you!</source>
<target>Du er i topform! Vi er alle vilde med dig!</target>
</trans-unit>
<trans-unit id="enviroScoreFit7to8">
<source>You are quite fit. But visit Honoloko again and try to get fitter!</source>
<target>Du er i god form. Besøg Honoloko igen og prøv at forbedre din kondi!</target>
</trans-unit>
<trans-unit id="enviroScoreFit5to6">
<source>You are doing ok, but there's room for improvement.</source>
<target>Du klarer dig fint, men der er plads til forbedring.</target>
</trans-unit>
<trans-unit id="enviroScoreFit3to4">
<source>You're not fit enough. Walking to the fridge and back is not real exercise.</source>
<target>Din kondi er for dårlig. At gå hen til køleskabet og tilbage igen tæller ikke som idræt.</target>
</trans-unit>
<trans-unit id="enviroScoreFit0to2">
<source>You are super lazy. Did you spend the whole day sitting in front of the TV?</source>
<target>Du er superdoven. Sad du foran fjernsynet hele dagen?</target>
</trans-unit>
<trans-unit id="enviroScoreHealth9to10">
<source>You are very healthy. You probably eat well and don't smoke. Keep it up!</source>
<target>Du er ved godt helbred. Du spiser sikkert godt og ryger ikke. Bliv ved med det!</target>
</trans-unit>
<trans-unit id="enviroScoreHealth7to8">
<source>You are in good health. Keep it up.</source>
<target>Du er ved godt helbred. Hold det ved lige.</target>
</trans-unit>
<trans-unit id="enviroScoreHealth5to6">
<source>You don't look well. You could do better</source>
<target>Du ser ikke for sund ud. Det kunne du forbedre.</target>
</trans-unit>
<trans-unit id="enviroScoreHealth3to4">
<source>You are pretty unhealthy. Shape up!</source>
<target>Du er temmelig usund. Se at komme i form!</target>
</trans-unit>
<trans-unit id="enviroScoreHealth0to2">
<source>Are you still breathing?</source>
<target>Trækker du stadig vejret?</target>
</trans-unit>
<trans-unit id="extendedInstructions1">
<source>Our Island of Honoloko is very similar to the real world, where your actions really do make a difference to your surroundings. The effects of your actions are immediately visible.</source>
<target>Vores ø, Honoloko, minder meget om den virkelige verden, hvor dine handlinger har indflydelse på dine omgivelser. Dine handlingers virkninger kan ses med det samme.</target>
</trans-unit>
<trans-unit id="extendedInstructions2">
<source>The aim of the game is to make decisions that most improve the health and environment on Honoloko.</source>
<target>Formålet med spillet er at forbedre helbredet og miljøet på Honoloko.</target>
</trans-unit>
<trans-unit id="extendedInstructions3">
<source>If your decisions score well in Resources or Energy you improve the Environment. When Health or Fitness scores go up or down, you see the Health of the people who live on Honoloko getting better or worse. Remember a better environment means healthier people.</source>
<target>Hvis dine beslutninger giver mange ressource- og energipoints, forbedrer du miljøet. Når pointene for helbred og kondition går op eller ned, ser du, hvordan folks helbred på Honoloko bliver bedre eller værre. Husk på, at et bedre miljø betyder sundere mennesker.</target>
</trans-unit>
<trans-unit id="extendedInstructions4">
<source>Keep an eye on the island, where you will see changes taking place according to your decisions.</source>
<target>Hold øje med øen, hvor du kan se forandringer afhængigt af dine beslutninger.</target>
</trans-unit>
<trans-unit id="extendedInstructions5">
<source>The scores show up in the billboards beside the road, and the colour of the billboard shows how you are doing. Red means you are doing badly in that sector, orange means average, and green means really good.</source>
<target>Dine points kan aflæses på pointtavlerne ved vejsiden, og tavlens farve viser, hvor godt du klarer dig. Rødt betyder, at du klarer dig dårligt i den sektor, orange betyder gennemsnitligt, og grønt betyder rigtig godt.</target>
</trans-unit>
<trans-unit id="extendedInstructions6">
<source>Your total score appears at the end of the game with your scores for each of the four sections.</source>
<target>Dine samlede points og dine points for hver af de fire sektioner kan ses, når spillet er forbi.</target>
</trans-unit>
<trans-unit id="topScoreboard">
<source>Top Scoreboard</source>
<target>Topscorertavle</target>
</trans-unit>
<trans-unit id="top100Scoreboard">
<source>Top 100 Scoreboard</source>
<target>Topscorertavle med de 100 bedste</target>
</trans-unit>
<trans-unit id="topScorers">
<source>These are the top scorers from Honoloko Island. You can win a prize if you get onto this board. If your name is drawn we will contact you so you can claim your prize.</source>
<target>Her er topscorerne fra øen Honoloko. Du kan vinde en præmie, hvis du kommer med på tavlen. Hvis dit navn bliver udtrukket, kontakter vi dig, så du kan få din præmie.</target>
</trans-unit>
<trans-unit id="topScoresFrom">
<source>Top scores from</source>
<target>Topscoring fra</target>
</trans-unit>
<trans-unit id="tellAFriend">
<source>Tell a friend</source>
<target>Fortæl en ven om det</target>
</trans-unit>
<trans-unit id="tellAFriendNotes">
<source>You completed the game. Bring a friend here and see if you can score higher than them!</source>
<target>Du fuldførte spillet. Tag en ven med og se, om du kan få flere points end ham/hende.</target>
</trans-unit>
<trans-unit id="playAgain">
<source>Play Again</source>
<target>Spil igen</target>
</trans-unit>
<trans-unit id="totalScore">
<source>Total Score</source>
<target>Samlet scoring</target>
</trans-unit>
<trans-unit id="volume">
<source>Volume</source>
<target>Lydstyrke</target>
</trans-unit>
<trans-unit id="on">
<source>On</source>
<target>Tænd</target>
</trans-unit>
<trans-unit id="off">
<source>Off</source>
<target>Sluk</target>
</trans-unit>
<trans-unit id="homePage">
<source>Homepage</source>
<target>Hjemmeside</target>
</trans-unit>
<trans-unit id="credits">
<source>Credits</source>
<target>Rulletekst</target>
</trans-unit>
<trans-unit id="inst">
<source>Instructions</source>
<target>Instrukser</target>
</trans-unit>
<trans-unit id="audioControls">
<source>Audio Controls</source>
<target>Lydstyring</target>
</trans-unit>
<trans-unit id="creditsText1">
<source>European Environment Agency</source>
<target>Det Europæiske Miljøagentur</target>
</trans-unit>
<trans-unit id="creditsText2">
<source>Produced by</source>
<target>Produceret af</target>
</trans-unit>
<trans-unit id="creditsText3">
<source>The EEA in co-operation with the WHO Regional Office for Europe</source>
<target>EEA i samarbejde med WHO's regionale kontor for Europa</target>
</trans-unit>
<trans-unit id="cancel">
<source>Cancel</source>
<target>Annullér</target>
</trans-unit>
<trans-unit id="tellAFriendError">
<source>Sorry the email address you gave didn't work properly</source>
<target>E-mail-adressen, du angav, virkede desværre ikke</target>
</trans-unit>
<trans-unit id="tellAFriendSuccess">
<source>Your friend has been asked to come to Honoloko</source>
<target>Din ven er blevet inviteret til Honoloko</target>
</trans-unit>
<trans-unit id="tellAFriendsEmailText">
<source>Friend's email</source>
<target>Vennens e-mail-adresse</target>
</trans-unit>
<trans-unit id="tellAFriendsNameText">
<source>Friend's name</source>
<target>Vennens navn</target>
</trans-unit>
<trans-unit id="yourName">
<source>Your name</source>
<target>Dit navn</target>
</trans-unit>
<trans-unit id="next">
<source>Next</source>
<target>Videre</target>
</trans-unit>
<trans-unit id="newGameButton">
<source>New game</source>
<target>Nyt spil</target>
</trans-unit>
<trans-unit id="scoreboardButton">
<source>Scoreboard</source>
<target>Pointtavle</target>
</trans-unit>
<trans-unit id="tellAFriendButton">
<source>Tell a Friend</source>
<target>Fortæl en ven</target>
</trans-unit>
<trans-unit id="correctLanguage">
<source>Is this the correct language for you?</source>
<target>Er det det rette sprog for dig?</target>
</trans-unit>
<trans-unit id="checkCountryScores">
<source>Click on your country name to view the scores of people that are also from the same country.</source>
<target>Klik på dit land for at se scoringslisten for folk fra samme land.</target>
</trans-unit>
<trans-unit id="checkPosition">
<source>Click on your score to view what your position is.</source>
<target>Klik på din scoring for at se, hvad din stilling er.</target>
</trans-unit>
<trans-unit id="checkRank">
<source>Click on your name to see how you rank in the scoreboard.</source>
<target>Klik på dit navn for at se, hvor du er på pointtavlen.</target>
</trans-unit>
<trans-unit id="feedbackSent">
<source>This has been sent successfully.</source>
<target>Din besked er blevet sendt.</target>
</trans-unit>
<trans-unit id="feedbackWait">
<source>Please wait for confirmation</source>
<target>Vent venligst på bekræftelse</target>
</trans-unit>
<trans-unit id="continue">
<source>Continue</source>
<target>Fortsæt</target>
</trans-unit>
<trans-unit id="country">
<source>Country</source>
<target>Land</target>
</trans-unit>
<trans-unit id="email">
<source>Email address</source>
<target>E-mail-adresse</target>
</trans-unit>
<trans-unit id="characterName">
<source>Your character's name</source>
<target>Din figurs navn</target>
</trans-unit>
<trans-unit id="playerName">
<source>Your real name</source>
<target>Dit virkelige navn</target>
</trans-unit>
<trans-unit id="formError">
<source>Please check your details and make sure you have spelt your email address correctly and have filled in all the sections.</source>
<target>Efterprøv dine detaljer og sørg for, at du har indtastet din e-mail-adresse korrekt og udfyldt alle dele.</target>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,87 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from xliff_parser import HandleXliffParsing
import sys
from odf.opendocument import OpenDocumentText
from odf import style, table
from odf.text import P, UserFieldDecls, UserFieldDecl, UserFieldInput
from odf.namespaces import TABLENS
textdoc = OpenDocumentText()
# Create a style for the table content. One we can modify
# later in the word processor.
tablecontents = style.Style(name="Table Contents", family="paragraph")
tablecontents.addElement(style.ParagraphProperties(numberlines="false", linenumber="0"))
textdoc.styles.addElement(tablecontents)
#tablestyle = style.Style(name="Table style", family="table")
#tablestyle.addElement(style.TableProperties(protected="true"))
#textdoc.automaticstyles.addElement(tablestyle)
# Create automatic styles for the column widths.
widthwide = style.Style(name="Wwide", family="table-column")
widthwide.addElement(style.TableColumnProperties(columnwidth="8cm"))
textdoc.automaticstyles.addElement(widthwide)
tcstyle = style.Style(name="Table Cell", family="table-cell")
tcstyle.addElement(style.TableCellProperties(cellprotect="protected"))
textdoc.automaticstyles.addElement(tcstyle)
parser = HandleXliffParsing()
xliff = file('global.xlf').read()
chandler = parser.parseXLIFFSTring(xliff)
if chandler is None:
print "Unable to parse XLIFF file"
sys.exit(0)
header_info = chandler.getFileTag()
body_info = chandler.getBody() #return a dictionary
uf = UserFieldDecls()
textdoc.text.addElement(uf)
# Add user fields
for id,transunit in body_info.items():
uf.addElement(UserFieldDecl(name=id,valuetype="string", stringvalue=transunit['target']))
# Start the table, and describe the columns
mytable = table.Table(protected="true")
mytable.addElement(table.TableColumn(numbercolumnsrepeated=2,stylename=widthwide))
for id,transunit in body_info.items():
tr = table.TableRow()
mytable.addElement(tr)
tc = table.TableCell(stylename=tcstyle, qattributes={(TABLENS,'protected'):'true'})
tr.addElement(tc)
p = P(stylename=tablecontents,text=transunit['source'])
tc.addElement(p)
tc = table.TableCell(qattributes={(TABLENS,'protected'):'true'})
tr.addElement(tc)
p = P(stylename=tablecontents)
tc.addElement(p)
f = UserFieldInput(name=id,description="Enter translation")
p.addElement(f)
textdoc.text.addElement(mytable)
textdoc.save("translations.odt")

View File

@ -0,0 +1,169 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2000-2004 Juan David Ibáñez Palomar <jdavid@itaapy.com>
# 2003 Roberto Quero, Eduardo Corrales
# 2004 Søren Roug
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# NOTE:
# This parser doesn't understand the ALT-TRANS element.
from xml.sax.handler import ContentHandler
from xml.sax import *
from cStringIO import StringIO
from types import StringType, UnicodeType
#constants
_FILE_ATTRS = ['original', 'source-language', 'datatype', 'date',
'target-language', 'product-name', 'product-version', 'build-num']
_PHASE_ATTRS = ['phase-name', 'process-name', 'tool', 'date', 'contact-name',
'contact-email', 'company-name']
class XLIFFHandler(ContentHandler):
""" This is used to parse the xliff file
"""
def __init__(self):
"""constructor """
self.__currentTag = ''
self.__filetag = []
self.__phase_group = []
self.__source = 0
self.__body = {}
self.__data = []
self.__inside_alttrans = 0
self.__tuid = ''
#functions related with <file> tag
def getFileTag(self):
return self.__filetag
def setFileTag(self, dict):
self.__filetag.extend(dict)
#functions related with <phase-group> tag
def getPhaseGroup(self):
return self.__phase_group
def setPhaseGroup(self, dict):
self.__phase_group.append(dict)
def getBody(self):
return self.__body
def setBody(self, key, value):
self.__body[key] = value
def startElement(self, name, attrs):
self.__currentTag = name
if name == 'alt-trans':
self.__inside_alttrans = 1
# Make the attributes available
# Implicit assumption: There is only one <file> element.
if name == 'file':
tmp = attrs.items()
for i in [elem for elem in attrs.keys() if elem not in _FILE_ATTRS]:
tmp.remove((i, attrs[i]))
self.setFileTag(tmp)
if name == 'phase':
tmp = attrs.items()
for i in [elem for elem in attrs.keys() if elem not in _PHASE_ATTRS]:
tmp.remove((i, attrs[i]))
self.setPhaseGroup(tmp)
if name == 'trans-unit':
self.__tuid = attrs['id']
self.__source = u''
self.__target = u''
self.__note = u''
def endElement(self, name):
if name == 'alt-trans':
self.__inside_alttrans = 0
if name == 'source' and self.__inside_alttrans == 0:
content = u''.join(self.__data).strip()
self.__data = []
self.__source = content
if name == 'target' and self.__inside_alttrans == 0:
content = u''.join(self.__data).strip()
self.__data = []
self.__target = content
if name == 'note' and self.__inside_alttrans == 0:
content = u''.join(self.__data).strip()
self.__data = []
self.__note = content
if name == 'trans-unit':
self.setBody(self.__tuid, {'source':self.__source,
'target':self.__target, 'note':self.__note})
self.__currentTag = ''
def characters(self, content):
currentTag = self.__currentTag
if currentTag in ( 'source', 'target', 'note'):
self.__data.append(content)
class HandleXliffParsing:
""" class for parse xliff files """
def __init__(self):
""" """
pass
def parseXLIFFSTring(self, xml_string):
""" """
chandler = XLIFFHandler()
parser = make_parser()
# Tell the parser to use our handler
parser.setContentHandler(chandler)
# Don't load the DTD from the Internet
parser.setFeature(handler.feature_external_ges, 0)
inpsrc = InputSource()
inpsrc.setByteStream(StringIO(xml_string))
try:
parser.parse(inpsrc)
return chandler
except:
return None
def parseXLIFFFile(self, file):
# Create a parser
parser = make_parser()
chandler = XLIFFHandler()
# Tell the parser to use our handler
parser.setContentHandler(chandler)
# Don't load the DTD from the Internet
parser.setFeature(handler.feature_external_ges, 0)
inputsrc = InputSource()
try:
if type(file) is StringType:
inputsrc.setByteStream(StringIO(file))
else:
filecontent = file.read()
inputsrc.setByteStream(StringIO(filecontent))
parser.parse(inputsrc)
return chandler
except:
return None

22
csv2ods/Makefile Normal file
View File

@ -0,0 +1,22 @@
all: odf csv2ods.1
txt: csv2ods.txt
%.1: %.docbook
xmlto man $<
%.txt: %.docbook
xmlto txt $<
clean:
rm -f *.1 *~ *.txt odf test.csv test2.ods test3.ods
odf:
ln -s ../odf
test: clean odf
@echo 1,2,3,4 > test.csv
@echo 5,6,7,8 >> test.csv
@echo 9,10,11,12 >> test.csv
@python2 csv2ods -i test.csv -o test2.ods
@python3 csv2ods -i test.csv -o test3.ods
@echo created files test.csv, test2.ods and test3.ods

229
csv2ods/csv2ods Executable file
View File

@ -0,0 +1,229 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (C) 2008 Agustin Henze -> agustinhenze at gmail.com
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# Søren Roug
#
# Oct 2014: Georges Khaznadar <georgesk@debian.org>
# - ported to Python3
# - imlemented the missing switch -c / --encoding, with an extra
# feature for POSIX platforms which can guess encoding.
from odf.opendocument import OpenDocumentSpreadsheet
from odf.style import Style, TextProperties, ParagraphProperties, TableColumnProperties
from odf.text import P
from odf.table import Table, TableColumn, TableRow, TableCell
from optparse import OptionParser
import sys,csv,re, os, codecs
if sys.version_info[0]==3: unicode=str
if sys.version_info[0]==2:
class UTF8Recoder:
"""
Iterator that reads an encoded stream and reencodes the input to UTF-8
"""
def __init__(self, f, encoding):
self.reader = codecs.getreader(encoding)(f)
def __iter__(self):
return self
def next(self):
return self.reader.next().encode("utf-8")
class UnicodeReader:
"""
A CSV reader which will iterate over lines in the CSV file "f",
which is encoded in the given encoding.
"""
def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
f = UTF8Recoder(f, encoding)
self.reader = csv.reader(f, dialect=dialect, **kwds)
def next(self):
row = self.reader.next()
return [unicode(s, "utf-8") for s in row]
def __iter__(self):
return self
def csvToOds( pathFileCSV, pathFileODS, tableName='table',
delimiter=',', quoting=csv.QUOTE_MINIMAL,
quotechar = '"', escapechar = None,
skipinitialspace = False, lineterminator = '\r\n',
encoding="utf-8"):
textdoc = OpenDocumentSpreadsheet()
# Create a style for the table content. One we can modify
# later in the word processor.
tablecontents = Style(name="Table Contents", family="paragraph")
tablecontents.addElement(ParagraphProperties(numberlines="false", linenumber="0"))
tablecontents.addElement(TextProperties(fontweight="bold"))
textdoc.styles.addElement(tablecontents)
# Start the table
table = Table( name=tableName )
if sys.version_info[0]==3:
reader = csv.reader(open(pathFileCSV, encoding=encoding),
delimiter=delimiter,
quoting=quoting,
quotechar=quotechar,
escapechar=escapechar,
skipinitialspace=skipinitialspace,
lineterminator=lineterminator)
else:
reader = UnicodeReader(open(pathFileCSV),
encoding=encoding,
delimiter=delimiter,
quoting=quoting,
quotechar=quotechar,
escapechar=escapechar,
skipinitialspace=skipinitialspace,
lineterminator=lineterminator)
fltExp = re.compile('^\s*[-+]?\d+(\.\d+)?\s*$')
for row in reader:
tr = TableRow()
table.addElement(tr)
for val in row:
if fltExp.match(val):
tc = TableCell(valuetype="float", value=val.strip())
else:
tc = TableCell(valuetype="string")
tr.addElement(tc)
p = P(stylename=tablecontents,text=val)
tc.addElement(p)
textdoc.spreadsheet.addElement(table)
textdoc.save( pathFileODS )
if __name__ == "__main__":
usage = "%prog -i file.csv -o file.ods -d"
parser = OptionParser(usage=usage, version="%prog 0.1")
parser.add_option('-i','--input', action='store',
dest='input', help='File input in csv')
parser.add_option('-o','--output', action='store',
dest='output', help='File output in ods')
parser.add_option('-d','--delimiter', action='store',
dest='delimiter', help='specifies a one-character string to use as the field separator. It defaults to ",".')
parser.add_option('-c','--encoding', action='store',
dest='encoding', help='specifies the encoding the file csv. It defaults to utf-8')
parser.add_option('-t','--table', action='store',
dest='tableName', help='The table name in the output file')
parser.add_option('-s','--skipinitialspace',
dest='skipinitialspace', help='''specifies how to interpret whitespace which
immediately follows a delimiter. It defaults to False, which
means that whitespace immediately following a delimiter is part
of the following field.''')
parser.add_option('-l','--lineterminator', action='store',
dest='lineterminator', help='''specifies the character sequence which should
terminate rows.''')
parser.add_option('-q','--quoting', action='store',
dest='quoting', help='''It can take on any of the following module constants:
0 = QUOTE_MINIMAL means only when required, for example, when a field contains either the quotechar or the delimiter
1 = QUOTE_ALL means that quotes are always placed around fields.
2 = QUOTE_NONNUMERIC means that quotes are always placed around fields which do not parse as integers or floating point numbers.
3 = QUOTE_NONE means that quotes are never placed around fields.
It defaults is QUOTE_MINIMAL''')
parser.add_option('-e','--escapechar', action='store',
dest='escapechar', help='''specifies a one-character string used to escape the delimiter when quoting is set to QUOTE_NONE.''')
parser.add_option('-r','--quotechar', action='store',
dest='quotechar', help='''specifies a one-character string to use as the quoting character. It defaults to ".''')
(options, args) = parser.parse_args()
if options.input:
pathFileCSV = options.input
else:
parser.print_help()
exit( 0 )
if options.output:
pathFileODS = options.output
else:
parser.print_help()
exit( 0 )
if options.delimiter:
delimiter = options.delimiter
else:
delimiter = ","
if options.skipinitialspace:
skipinitialspace = True
else:
skipinitialspace=False
if options.lineterminator:
lineterminator = options.lineterminator
else:
lineterminator ="\r\n"
if options.escapechar:
escapechar = options.escapechar
else:
escapechar=None
if options.tableName:
tableName = options.tableName
else:
tableName = "table"
if options.quotechar:
quotechar = options.quotechar
else:
quotechar = "\""
encoding = "utf-8" # default setting
###########################################################
## try to guess the encoding; this is implemented only with
## POSIX platforms. Can it be improved?
output = os.popen('/usr/bin/file ' + pathFileCSV).read()
m=re.match(r'^.*: ([-a-zA-Z0-9]+) text$', output)
if m:
encoding=m.group(1)
if 'ISO-8859' in encoding:
encoding="latin-1"
else:
encoding="utf-8"
############################################################
# when the -c or --coding switch is used, it takes precedence
if options.encoding:
encoding = options.encoding
csvToOds( pathFileCSV=unicode(pathFileCSV),
pathFileODS=unicode(pathFileODS),
delimiter=delimiter, skipinitialspace=skipinitialspace,
escapechar=escapechar,
lineterminator=unicode(lineterminator),
tableName=tableName, quotechar=quotechar,
encoding=encoding)
# Local Variables: ***
# mode: python ***
# End: ***

270
csv2ods/csv2ods.1 Normal file
View File

@ -0,0 +1,270 @@
.\" Title: csv2ods
.\" Author: Agustin Henze <agustinhenze at gmail.com>
.\" Generator: DocBook XSL Stylesheets v1.74.0 <http://docbook.sf.net/>
.\" Date: 01/04/2009
.\" Manual: User commands
.\" Source: odfpy
.\" Language: English
.\"
.TH "CSV2ODS" "1" "01/04/2009" "odfpy" "User commands"
.\" -----------------------------------------------------------------
.\" * (re)Define some macros
.\" -----------------------------------------------------------------
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" toupper - uppercase a string (locale-aware)
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de toupper
.tr aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ
\\$*
.tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH-xref - format a cross-reference to an SH section
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de SH-xref
.ie n \{\
.\}
.toupper \\$*
.el \{\
\\$*
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SH - level-one heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SH
.\" put an extra blank line of space above the head in non-TTY output
.if t \{\
.sp 1
.\}
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[an-margin]u
.ti 0
.HTML-TAG ".NH \\n[an-level]"
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
\." make the size of the head bigger
.ps +3
.ft B
.ne (2v + 1u)
.ie n \{\
.\" if n (TTY output), use uppercase
.toupper \\$*
.\}
.el \{\
.nr an-break-flag 0
.\" if not n (not TTY), use normal case (not uppercase)
\\$1
.in \\n[an-margin]u
.ti 0
.\" if not n (not TTY), put a border/line under subheading
.sp -.6
\l'\n(.lu'
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" SS - level-two heading that works better for non-TTY output
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de1 SS
.sp \\n[PD]u
.nr an-level 1
.set-an-margin
.nr an-prevailing-indent \\n[IN]
.fi
.in \\n[IN]u
.ti \\n[SN]u
.it 1 an-trap
.nr an-no-space-flag 1
.nr an-break-flag 1
.ps \\n[PS-SS]u
\." make the size of the head bigger
.ps +2
.ft B
.ne (2v + 1u)
.if \\n[.$] \&\\$*
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BB/BE - put background/screen (filled box) around block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BB
.if t \{\
.sp -.5
.br
.in +2n
.ll -2n
.gcolor red
.di BX
.\}
..
.de EB
.if t \{\
.if "\\$2"adjust-for-leading-newline" \{\
.sp -1
.\}
.br
.di
.in
.ll
.gcolor
.nr BW \\n(.lu-\\n(.i
.nr BH \\n(dn+.5v
.ne \\n(BHu+.5v
.ie "\\$2"adjust-for-leading-newline" \{\
\M[\\$1]\h'1n'\v'+.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.el \{\
\M[\\$1]\h'1n'\v'-.5v'\D'P \\n(BWu 0 0 \\n(BHu -\\n(BWu 0 0 -\\n(BHu'\M[]
.\}
.in 0
.sp -.5v
.nf
.BX
.in
.sp .5v
.fi
.\}
..
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.\" BM/EM - put colored marker in margin next to block of text
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.de BM
.if t \{\
.br
.ll -2n
.gcolor red
.di BX
.\}
..
.de EM
.if t \{\
.br
.di
.ll
.gcolor
.nr BH \\n(dn
.ne \\n(BHu
\M[\\$1]\D'P -.75n 0 0 \\n(BHu -(\\n[.i]u - \\n(INu - .75n) 0 0 -\\n(BHu'\M[]
.in 0
.nf
.BX
.in
.fi
.\}
..
.\" -----------------------------------------------------------------
.\" * set default formatting
.\" -----------------------------------------------------------------
.\" disable hyphenation
.nh
.\" disable justification (adjust text to left margin only)
.ad l
.\" -----------------------------------------------------------------
.\" * MAIN CONTENT STARTS HERE *
.\" -----------------------------------------------------------------
.SH "Name"
csv2ods \- Create OpenDocument spreadsheet from comma separated values
.SH "Synopsis"
.fam C
.HP \w'\fBcsv2ods\fR\ 'u
\fBcsv2ods\fR \-i\ \fIfile\&.csv\fR \-o\ \fIfile\&.ods\fR
.fam
.SH "Description"
.PP
This program reads a file in CSV format \- table of columns delimited by commas, tabs or any other character\&. It then creates a spreadsheet\&. If a value looks like a number the cell is formatted as a number as well\&.
.SH "Options"
.PP
\-\-version
.RS 4
Show program\'s version number and exit
.RE
.PP
\-h, \-\-help
.RS 4
Show help message and exit
.RE
.PP
\-i \fIINPUT\fR, \-\-input=\fIINPUT\fR
.RS 4
File input in csv\&.
.RE
.PP
\-o \fIOUTPUT\fR, \-\-output=\fIOUTPUT\fR
.RS 4
File output in ods\&.
.RE
.PP
\-d \fIDELIMITER\fR, \-\-delimiter=\fIDELIMITER\fR
.RS 4
Specifies a one\-character string to use as the field separator\&. It defaults to ","\&.
.RE
.PP
\-c \fIENCODING\fR, \-\-encoding=\fIENCODING\fR
.RS 4
Specifies the encoding the file csv\&. It defaults to utf\-8\&.
.RE
.PP
\-t \fITABLENAME\fR, \-\-table=\fITABLENAME\fR
.RS 4
The table name in the output file\&.
.RE
.PP
\-s \fISKIPINITIALSPACE\fR, \-\-skipinitialspace=\fISKIPINITIALSPACE\fR
.RS 4
Specifies how to interpret whitespace which immediately follows a delimiter\&. It defaults to False, which means that whitespace immediately following a delimiter is part of the following field\&.
.RE
.PP
\-l \fILINETERMINATOR\fR, \-\-lineterminator=\fILINETERMINATOR\fR
.RS 4
Specifies the character sequence which should terminate rows\&.
.RE
.PP
\-q \fIQUOTING\fR, \-\-quoting=\fIQUOTING\fR
.RS 4
It can take on any of the following module constants: 0 = QUOTE_MINIMAL means only when required, for example, when a field contains either the quotechar or the delimiter\&. 1 = QUOTE_ALL means that quotes are always placed around fields\&. 2 = QUOTE_NONNUMERIC means that quotes are always placed around fields which do not parse as integers or floating point numbers\&. 3 = QUOTE_NONE means that quotes are never placed around fields\&. It defaults is QUOTE_MINIMAL\&.
.RE
.PP
\-e \fIESCAPECHAR\fR, \-\-escapechar=\fIESCAPECHAR\fR
.RS 4
Specifies a one\-character string used to escape the delimiter when quoting is set to QUOTE_NONE\&.
.RE
.PP
\-r \fIQUOTECHAR\fR, \-\-quotechar=\fIQUOTECHAR\fR
.RS 4
Specifies a one\-character string to use as the quoting character\&. It defaults to "\&.
.RE
.SH "Example"
.sp
.if n \{\
.RS 4
.\}
.fam C
.ps -1
.nf
.if t \{\
.sp -1
.\}
.BB lightgray adjust-for-leading-newline
.sp -1
csv2ods \-i /etc/passwd \-o accounts\&.odt \-d:
.EB lightgray adjust-for-leading-newline
.if t \{\
.sp 1
.\}
.fi
.fam
.ps +1
.if n \{\
.RE
.\}
.SH "Author"
.PP
\fBAgustin Henze\fR <\&agustinhenze at gmail\&.com\&>
.RS 4
Original author of csv\-ods\&.py
.RE

162
csv2ods/csv2ods.docbook Normal file
View File

@ -0,0 +1,162 @@
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<refentry id="csv2ods">
<refentryinfo>
<productname>odfpy</productname>
<author><firstname>Agustin</firstname><surname>Henze</surname>
<contrib>Original author of csv-ods.py</contrib>
<email>agustinhenze at gmail.com</email></author>
</refentryinfo>
<refmeta>
<refentrytitle>csv2ods</refentrytitle>
<manvolnum>1</manvolnum>
<refmiscinfo class="manual">User commands</refmiscinfo>
</refmeta>
<refnamediv>
<refname>csv2ods</refname>
<refpurpose>Create OpenDocument spreadsheet from comma separated values</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>csv2ods</command>
<arg choice="plain">-i <replaceable>file.csv</replaceable></arg>
<arg choice="plain">-o <replaceable>file.ods</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
This program reads a file in CSV format - table of columns delimited by commas,
tabs or any other character. It then creates a spreadsheet. If a value looks like a number
the cell is formatted as a number as well.
</para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term>--version</term>
<listitem>
<para>
Show program's version number and exit
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-h, --help</term>
<listitem>
<para>
Show help message and exit
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-i <replaceable>INPUT</replaceable>, --input=<replaceable>INPUT</replaceable></term>
<listitem>
<para>
File input in csv.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-o <replaceable>OUTPUT</replaceable>, --output=<replaceable>OUTPUT</replaceable></term>
<listitem>
<para>
File output in ods.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-d <replaceable>DELIMITER</replaceable>, --delimiter=<replaceable>DELIMITER</replaceable></term>
<listitem>
<para>
Specifies a one-character string to use as the field
separator. It defaults to ",".
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-c <replaceable>ENCODING</replaceable>, --encoding=<replaceable>ENCODING</replaceable></term>
<listitem>
<para>
Specifies the encoding the file csv. It defaults to utf-8.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-t <replaceable>TABLENAME</replaceable>, --table=<replaceable>TABLENAME</replaceable></term>
<listitem>
<para>
The table name in the output file.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-s <replaceable>SKIPINITIALSPACE</replaceable>, --skipinitialspace=<replaceable>SKIPINITIALSPACE</replaceable></term>
<listitem>
<para>
Specifies how to interpret whitespace which
immediately follows a delimiter. It defaults to
False, which
means that whitespace immediately following a
delimiter is part
of the following field.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-l <replaceable>LINETERMINATOR</replaceable>, --lineterminator=<replaceable>LINETERMINATOR</replaceable></term>
<listitem>
<para>
Specifies the character sequence which should
terminate rows.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-q <replaceable>QUOTING</replaceable>, --quoting=<replaceable>QUOTING</replaceable></term>
<listitem>
<para>
It can take on any of the following module constants:
0 = QUOTE_MINIMAL means only when required, for
example, when a field contains either the quotechar or
the delimiter.
1 = QUOTE_ALL means that quotes are always placed
around fields.
2 = QUOTE_NONNUMERIC means that quotes are always
placed around fields which do not parse as integers or
floating point numbers.
3 = QUOTE_NONE means that quotes are never placed
around fields.
It defaults is QUOTE_MINIMAL.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-e <replaceable>ESCAPECHAR</replaceable>, --escapechar=<replaceable>ESCAPECHAR</replaceable></term>
<listitem>
<para>
Specifies a one-character string used to escape the
delimiter when quoting is set to QUOTE_NONE.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>-r <replaceable>QUOTECHAR</replaceable>, --quotechar=<replaceable>QUOTECHAR</replaceable></term>
<listitem>
<para>
Specifies a one-character string to use as the quoting
character. It defaults to ".
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Example</title>
<screen>
csv2ods -i /etc/passwd -o accounts.odt -d:
</screen>
</refsect1>
</refentry>

85
debian/changelog vendored Normal file
View File

@ -0,0 +1,85 @@
python-odf (1.3.1+dfsg-1) unstable; urgency=medium
* New upstream version, which allowed to remove all previous patches.
* Set HTML_TIMESTAMP = NO (Closes: #777635).
* Added patch which (Closes: #783789).
* Remove doc/ in source, because of uglified JS.
* Updated watch file.
-- W. Martin Borgert <debacle@debian.org> Sat, 25 Jul 2015 13:36:45 +0000
python-odf (1.2.0-6) unstable; urgency=medium
* fixed a lot of issues due to inconsistencies of the code initially
written for Python2, mainly confusions between byte strings
and unicode strings. The new code tries to use byte strings for
file-like streams and unicode strings for in-memory objects.
* added JavaDoc strings to document thoroughly functions and methods,
mainly in odf/opendocument.py
* added a config.dox file to allow one to build a documentation in HTML
and LaTeX, for library developpers and users.
* added many assert() clauses to ensure the types of parameters in
functions and methods
* modified the utilities to make them usable with both Python2 and Python3
* implemented a feature announced in csv2ods manfiles but not previously
active: -c / --encoding switch to take in account the encoding of
the CSV source file.
* added rules to build the developer's documentation and install it in
usr/share/python-odf/API-doc/html
* added a dependency python-odf-doc -> libjs-jquery necessary because
of HTML code output by Doxygen
-- Georges Khaznadar <georgesk@debian.org> Tue, 28 Oct 2014 10:41:32 +0100
python-odf (1.2.0-5) unstable; urgency=medium
* fixed a small mistake: the new Standards-Version is 3.9.6
* recalled that this upload Closes: #766389
-- Georges Khaznadar <georgesk@debian.org> Sun, 26 Oct 2014 22:39:40 +0100
python-odf (1.2.0-3) unstable; urgency=medium
* added myself to Uploaders
* modified the library to work with python3
* upgraded Standards-Version to 3.9.5, debhelper to 9
* added three new binary packages: python3-odf pythn-odf-tools
and python-odf-doc; the last one provides the documentation
for Odfpy's API as an opendocument
* modified the test routine to run tests with both python 2 and 3.
* fixed algorithms which might work with Python2 but were defeated by
Python3's unpredictability for order of iterables like dictionaries.
* modified the source of "binaries" to work with Python3 and checked
their features.
-- Georges Khaznadar <georgesk@debian.org> Tue, 21 Oct 2014 12:04:55 +0200
python-odf (1.2.0-2) unstable; urgency=low
* Build-dep on dh-python (Closes: #764984)
* New policy version, no changes
-- W. Martin Borgert <debacle@debian.org> Sun, 12 Oct 2014 19:59:27 +0000
python-odf (1.2.0-1) unstable; urgency=low
* New upstream version (Closes: #763870)
* Use improved short and long description by upstream
* New policy version, no changes
* Moved packaging to git
-- W. Martin Borgert <debacle@debian.org> Fri, 03 Oct 2014 21:47:40 +0000
python-odf (0.9.6-2) unstable; urgency=low
[Jakub Wilk <jwilk@debian.org> Sun, 05 May 2013 16:03:01 +0200]
* Use canonical URIs for Vcs-* fields.
* New policy version, no changes. Upload to unstable.
-- W. Martin Borgert <debacle@debian.org> Thu, 09 May 2013 12:57:04 +0000
python-odf (0.9.6-1) experimental; urgency=low
* Initial release. (Closes: #484584)
-- Thomas Bechtold <thomasbechtold@jpberlin.de> Tue, 11 Dec 2012 17:15:17 +0100

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

79
debian/control vendored Normal file
View File

@ -0,0 +1,79 @@
Source: python-odf
Section: python
Priority: optional
Maintainer: Debian Python Modules Team <python-modules-team@lists.alioth.debian.org>
Uploaders: Thomas Bechtold <thomasbechtold@jpberlin.de>,
W. Martin Borgert <debacle@debian.org>,
Georges Khaznadar <georgesk@debian.org>
Standards-Version: 3.9.6
Build-Depends: debhelper (>= 9), python-all, python3-all, xmlto,
python-setuptools, dh-python, doxygen, doxypy, graphviz
Homepage: https://github.com/eea/odfpy
Vcs-Git: git://anonscm.debian.org/python-modules/packages/python-odf.git
Vcs-Browser: http://anonscm.debian.org/gitweb/?p=python-modules/packages/python-odf.git
Package: python-odf
Architecture: all
Recommends: python-odf-doc, python-odf-tools
Depends: ${misc:Depends}, ${python:Depends}
Description: Python API and tools to manipulate OpenDocument files
Odfpy is a library to read and write OpenDocument v. 1.1 files.
The main focus has been to prevent the programmer from creating invalid
documents. It has checks that raise an exception if the programmer adds
an invalid element, adds an attribute unknown to the grammar, forgets to
add a required attribute or adds text to an element that doesn't allow it.
.
These checks and the API itself were generated from the RelaxNG
schema, and then hand-edited. Therefore the API is complete and can
handle all ODF constructions.
Package: python-odf-tools
Architecture: all
Conflicts: python-odf (<< 1.2.0-3)
Depends: ${misc:Depends}, ${python:Depends}, python3-odf
Description: Python API and tools to manipulate OpenDocument files
Odfpy is a library to read and write OpenDocument v. 1.1 files.
The main focus has been to prevent the programmer from creating invalid
documents. It has checks that raise an exception if the programmer adds
an invalid element, adds an attribute unknown to the grammar, forgets to
add a required attribute or adds text to an element that doesn't allow it.
.
These checks and the API itself were generated from the RelaxNG
schema, and then hand-edited. Therefore the API is complete and can
handle all ODF constructions.
Package: python3-odf
Architecture: all
Recommends: python-odf-doc, python-odf-tools
Depends: ${misc:Depends}, ${python:Depends}, python3
Description: Python API and tools to manipulate OpenDocument files
Odfpy is a library to read and write OpenDocument v. 1.1 files.
The main focus has been to prevent the programmer from creating invalid
documents. It has checks that raise an exception if the programmer adds
an invalid element, adds an attribute unknown to the grammar, forgets to
add a required attribute or adds text to an element that doesn't allow it.
.
These checks and the API itself were generated from the RelaxNG
schema, and then hand-edited. Therefore the API is complete and can
handle all ODF constructions.
.
In addition to the API, there are a few scripts:
.
- csv2ods - Create OpenDocument spreadsheet from comma separated values
- mailodf - Email ODF file as HTML archive
- odf2xhtml - Convert ODF to (X)HTML
- odf2mht - Convert ODF to HTML archive
- odf2xml - Create OpenDocument XML file from OD? package
- odfimgimport - Import external images
- odflint - Check ODF file for problems
- odfmeta - List or change the metadata of an ODF file
- odfoutline - Show outline of OpenDocument
- odfuserfield - List or change the user-field declarations in an ODF file
- xml2odf - Create OD? package from OpenDocument in XML form
Package: python-odf-doc
Architecture: all
Section: doc
Depends: ${misc:Depends}, libjs-jquery
Description: documentation and examples for python-odf and python3-odf
Odfpy is a library to read and write OpenDocument v. 1.1 files.

105
debian/copyright vendored Normal file
View File

@ -0,0 +1,105 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: odfpy
Source: https://joinup.ec.europa.eu/software/odfpy/home
Files: *
Copyright: 2006-2009 Søren Roug, European Environment Agency
License: GPL-2+ and Apache-2
Files: csv2ods/*
Copyright: 2008 Agustin Henze <agustinhenze@gmail.com>
License: GPL-2+ and Apache-2
Files: examples/easylists.py
Copyright: 2008 J. David Eisenberg
License: GPL-2+
Files: examples/ods-currency.py
Copyright: 2009 Brad Ralph
License: GPL-2+ and Apache-2
Files: contrib/gutenberg/*
Copyright: 2007 Søren Roug, European Environment Agency
License: LGPL-2.1+
Files: contrib/xliff/*
Copyright: 2000-2004 Juan David Ibáñez Palomar
2003 Roberto Quero, Eduardo Corrales
2004 Søren Roug
License: GPL-2+
Files: odf/*
Copyright: 2006-2009 Søren Roug, European Environment Agency
License: LGPL-2.1+
Files: odf/easyliststyle.py odf/teletype.py
Copyright: 2008 J. David Eisenberg
License: GPL-2+
Files: odf/__init__.py odf/userfield.py
Copyright: 2006-2009 Søren Roug, European Environment Agency
License: GPL-2+ and Apache-2
Files: odf/thumbnail.py
Copyright: The Document Foundation
License: CC-BY-SA-3.0
License: CC-BY-SA-3.0
You are free:
to Share (to copy, distribute and transmit the work) and
to Remix (to adapt the work) under the following conditions:
.
Attribution — You must attribute the work in the manner specified by the
author or licensor (but not in any way that suggests that they endorse you
or your use of the work).
.
Share Alike — If you alter, transform, or build upon this work, you may
distribute the resulting work only under the same, similar or a compatible
license.
.
For more information, see http://creativecommons.org/licenses/by-sa/3.0/
License: Apache-2
On Debian systems, the full text of the Apache
License version 2 can be found in the file
`/usr/share/common-licenses/Apache-2.0'.
License: LGPL-2.1+
This package is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
.
You should have received a copy of the GNU Lesser General Public
License along with this package; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
.
On Debian systems, the complete text of the GNU Lesser General
Public License can be found in `/usr/share/common-licenses/LGPL-2'.
License: GPL-2+
This program is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.
.
This program is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied
warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more
details.
.
You should have received a copy of the GNU General Public
License along with this package; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301 USA
.
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
`/usr/share/common-licenses/GPL-2'.

19
debian/patches/fix_chinese_ods.patch vendored Normal file
View File

@ -0,0 +1,19 @@
Description: Fixes loading of Chinese ODS file
Author: W. Martin Borgert <debacle@debian.org>
Origin: vendor
Bug: https://github.com/eea/odfpy/issues/19
Bug-Debian: https://bugs.debian.org/783789
Last-Update: 2015-07-25
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- a/odf/attrconverters.py
+++ b/odf/attrconverters.py
@@ -103,7 +103,7 @@
def __save_prefix(attribute, arg, element):
prefix = arg.split(':',1)[0]
if prefix == arg:
- return str(arg)
+ return arg
namespace = element.get_knownns(prefix)
if namespace is None:
#raise ValueError( "'%s' is an unknown prefix" % str(prefix))

19
debian/patches/no_html_timestamp.patch vendored Normal file
View File

@ -0,0 +1,19 @@
Description: unset HTML_TIMESTAMP for reproducible builds
Author: W. Martin Borgert <debacle@debian.org>
Origin: vendor
Bug: https://github.com/eea/odfpy/issues/20
Bug-Debian: https://bugs.debian.org/777635
Last-Update: 2015-07-25
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- a/config.dox
+++ b/config.dox
@@ -1127,7 +1127,7 @@
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_TIMESTAMP = YES
+HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the

2
debian/patches/series vendored Normal file
View File

@ -0,0 +1,2 @@
no_html_timestamp.patch
fix_chinese_ods.patch

3
debian/python-odf-doc.install vendored Normal file
View File

@ -0,0 +1,3 @@
api-for-odfpy.odt usr/share/python-odf
examples usr/share/python-odf
doc/html usr/share/python-odf/API-doc

2
debian/python-odf-tools.install vendored Normal file
View File

@ -0,0 +1,2 @@
debian/python3-odfpy/usr/share usr
debian/python3-odfpy/usr/bin usr

1
debian/python-odf.install vendored Normal file
View File

@ -0,0 +1 @@
debian/python-odfpy/usr/lib usr

1
debian/python3-odf.install vendored Normal file
View File

@ -0,0 +1 @@
debian/python3-odfpy/usr/lib usr

45
debian/rules vendored Executable file
View File

@ -0,0 +1,45 @@
#!/usr/bin/make -f
#DH_VERBOSE=1
export PYBUILD_NAME=odfpy
PYTHON2=$(shell pyversions -vr)
%:
dh $@ --buildsystem=pybuild --with python2 --with python3
ifeq (,$(filter nocheck,$(DEB_BUILD_OPTIONS)))
test-python%:
cd tests; \
PYTHONPATH=../`python$* -c "import sys; print ':'.join(sys.path)"`; \
for F in `ls test*.py` ; do \
PYTHONPATH=$$PYTHONPATH python$* $$F; \
done
override_dh_auto_test: $(PYTHON2:%=test-python%)
endif
UTILITIES = csv2ods mailodf odf2xhtml odf2mht odf2xml odfimgimport \
odflint odfmeta odfoutline odfuserfield xml2odf
override_dh_clean:
# clean directories used for utilities
for D in $(UTILITIES); do make -C $$D clean; done
# remove build space
rm -rf build
# remove generated symlinks
find . -name odf -type l | xargs rm -f
# remove documentation made by Doxygen
rm -rf doc
dh_clean
override_dh_auto_build:
for D in $(UTILITIES); do make -C $$D; done
doxygen config.dox
dh_auto_build
override_dh_install:
dh_install
# remove an embedded JS library
f=debian/python-odf-doc/usr/share/python-odf/API-doc/html/jquery.js; \
[ -e $$f ] && rm $$f && ln -s /usr/share/javascript/jquery/jquery.js $$f

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (quilt)

3
debian/source/include-binaries vendored Normal file
View File

@ -0,0 +1,3 @@
# example file to run tests: an odf file with
# an image as a hyperlink
odfimgimport/textWithImages.odt

3
debian/watch vendored Normal file
View File

@ -0,0 +1,3 @@
version=3
opts=dversionmangle=s/\+(debian|dfsg|ds|deb)(\.\d+)?$// \
https://github.com/eea/odfpy/releases /eea/odfpy/archive/release-(.+)\.tar\.gz

148
examples/barchart.py Normal file
View File

@ -0,0 +1,148 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
# This is an example of an OpenDocument Chart.
#
# Opendocument charts are usually not found in the wild. They are intended to be
# subobojects of e.g. spreadsheets. But the KDE application called kchart
# (http://www.koffice.org/kchart/) is able to read and write ODC files.
#
# Kchart is able to understand a document without <chart:series>, whereas
# OOo misinterprets the label rows and columns. So if you embed the
# spectre-balance.odc file in an OOo Writer document, expect to see some
# oddities.
from odf.opendocument import OpenDocumentChart
from odf import chart, style, table, text
# import a support class from the examples directory
from datatable import DataTable
class BarChart(object):
def __init__(self):
self.charttype = 'chart:bar'
self.subtype = 'normal' # 'percentage', 'stacked' or 'normal'
self.threedimensional = "true"
self.x_axis = "X"
self.y_axis = "Y"
self.values = (1,2,3)
self.title = None
self.subtitle = None
def __call__(self, doc):
chartstyle = style.Style(name="chartstyle", family="chart")
chartstyle.addElement( style.GraphicProperties(stroke="none", fillcolor="#ffffff"))
doc.automaticstyles.addElement(chartstyle)
mychart = chart.Chart( width="576pt", height="504pt", stylename=chartstyle, attributes={'class':self.charttype})
doc.chart.addElement(mychart)
# Title
if self.title:
titlestyle = style.Style(name="titlestyle", family="chart")
titlestyle.addElement( style.GraphicProperties(stroke="none", fill="none"))
titlestyle.addElement( style.TextProperties(fontfamily="'Nimbus Sans L'",
fontfamilygeneric="swiss", fontpitch="variable", fontsize="13pt"))
doc.automaticstyles.addElement(titlestyle)
mytitle = chart.Title(x="385pt", y="27pt", stylename=titlestyle)
mytitle.addElement( text.P(text=self.title))
mychart.addElement(mytitle)
# Subtitle
if self.subtitle:
subtitlestyle = style.Style(name="subtitlestyle", family="chart")
subtitlestyle.addElement( style.GraphicProperties(stroke="none", fill="none"))
subtitlestyle.addElement( style.TextProperties(fontfamily="'Nimbus Sans L'",
fontfamilygeneric="swiss", fontpitch="variable", fontsize="10pt"))
doc.automaticstyles.addElement(subtitlestyle)
subtitle = chart.Subtitle(x="0pt", y="123pt", stylename=subtitlestyle)
subtitle.addElement( text.P(text= self.subtitle))
mychart.addElement(subtitle)
# Legend
legendstyle = style.Style(name="legendstyle", family="chart")
legendstyle.addElement( style.GraphicProperties(fill="none"))
legendstyle.addElement( style.TextProperties(fontfamily="'Nimbus Sans L'",
fontfamilygeneric="swiss", fontpitch="variable", fontsize="6pt"))
doc.automaticstyles.addElement(legendstyle)
mylegend = chart.Legend(legendposition="end", legendalign="center", stylename=legendstyle)
mychart.addElement(mylegend)
# Plot area
plotstyle = style.Style(name="plotstyle", family="chart")
if self.subtype == "stacked": percentage="false"; stacked="true"
elif self.subtype == "percentage": percentage="true"; stacked="false"
else: percentage="false"; stacked="false"
plotstyle.addElement( style.ChartProperties(seriessource="columns",
percentage=percentage, stacked=stacked,
threedimensional=self.threedimensional))
doc.automaticstyles.addElement(plotstyle)
plotarea = chart.PlotArea(datasourcehaslabels=self.datasourcehaslabels, stylename=plotstyle)
mychart.addElement(plotarea)
# Style for the X,Y axes
axisstyle = style.Style(name="axisstyle", family="chart")
axisstyle.addElement( style.ChartProperties(displaylabel="true"))
doc.automaticstyles.addElement(axisstyle)
# Title for the X axis
xaxis = chart.Axis(dimension="x", name="primary-x", stylename=axisstyle)
plotarea.addElement(xaxis)
xt = chart.Title()
xaxis.addElement(xt)
xt.addElement(text.P(text=self.x_axis))
# Title for the Y axis
yaxis = chart.Axis(dimension="y", name="primary-y", stylename=axisstyle)
plotarea.addElement(yaxis)
yt = chart.Title()
yaxis.addElement(yt)
yt.addElement(text.P(text=self.y_axis))
# Data area
datatable = DataTable( self.values )
datatable.datasourcehaslabels = self.datasourcehaslabels
mychart.addElement(datatable())
if __name__ == "__main__":
# Create the document
doc = OpenDocumentChart()
mychart = BarChart()
mychart.title = "SPECTRE"
mychart.subtitle = "SPecial Executive for Counter-intelligence, Terrorism, Revenge and Extortion"
mychart.x_axis = u"Divisions"
mychart.y_axis = u"€ (thousand)"
# These represent the data. Six rows in three columns
mychart.values = (
('', 'Expense', 'Revenue'),
('Counterfeit', 1000, 1500),
('Murder', 1100, 1150),
('Prostitution', 3200, 2350),
('Blackmail', 1100, 1150),
('Larceny', 1000, 1750)
)
mychart.datasourcehaslabels = "both"
mychart(doc)
doc.save("spectre-balance", True)

Binary file not shown.

94
examples/datatable.py Normal file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (C) 2007 Søren Roug, European Environment Agency
#
# This is free software. You may redistribute it under the terms
# of the Apache license and the GNU General Public License Version
# 2 or at your option any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Contributor(s):
#
from odf import table, text
def valuetype(val):
valuetype="string"
if isinstance(val,str): valuetype="string"
if isinstance(val,int): valuetype="float"
if isinstance(val,float): valuetype="float"
if isinstance(val,bool): valuetype="boolean"
return valuetype
class DataTable(object):
def __init__(self, values=()):
self.values = values
self.datasourcehaslabels = "none"
def _set_values(self, value):
if isinstance(value, list) or isinstance(value, tuple):
self.__dict__['values'] = value
firstrow = value[0]
if isinstance(firstrow, list) or isinstance(firstrow, tuple):
self.numcols = len(firstrow)
else:
self.numcols = 1
else:
raise ValueError, "Value must be list or tuple"
def __setattr__(self, name, value):
if name == 'values':
self._set_values(value)
else:
self.__dict__[name] = value
def __call__(self):
datatable = table.Table(name="local-table")
if self.datasourcehaslabels in ('row','both'):
t = table.TableHeaderColumns()
t.addElement(table.TableColumn())
datatable.addElement(t)
t = table.TableColumns()
if self.datasourcehaslabels in ('row','both'):
t.addElement(table.TableColumn(numbercolumnsrepeated=str(self.numcols-1)))
else:
t.addElement(table.TableColumn(numbercolumnsrepeated=str(self.numcols)))
datatable.addElement(t)
if self.datasourcehaslabels in ('column','both'):
t = table.TableHeaderRows()
datatable.addElement(t)
tr = table.TableRow()
t.addElement(tr)
content = self.values[0]
for val in content:
tc = table.TableCell(valuetype=valuetype(val))
tr.addElement(tc)
tc.addElement(text.P(text=str(val)))
t = table.TableRows()
datatable.addElement(t)
rownum = 0
for content in self.values:
if rownum == 0 and self.datasourcehaslabels in ('column','both'):
rownum += 1
continue
tr = table.TableRow()
t.addElement(tr)
for val in content:
tc = table.TableCell(valuetype=valuetype(val), value=val)
tr.addElement(tc)
tc.addElement(text.P(text=str(val)))
rownum += 1
return datatable

122
examples/easylists.py Normal file
View File

@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
#
# Show the easyliststyle.py module
# Copyright (C) 2008 J. David Eisenberg
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from odf import easyliststyle
from odf.opendocument import OpenDocumentText
from odf.style import Style, TextProperties
from odf.text import P, List, ListItem
"""
This program shows the easyliststyle.py module.
It creates a file named "easylist_odfpy.odt"
with a bulleted list, a numbered list, and a
mixed list.
"""
bulletListSpec = '*,>,#,%'
mixedListSpec = u'1.!\u273f!a)'
numberListSpecArray = ('I', '1:', 'a')
itemList = (
"Cats",
">Domestic Shorthair",
">Domestic Longhair",
">Purebred",
">>Russian Blue",
">>Siamese",
">>>Seal Point",
">>>Flame Point",
"Dogs",
">Retrievers",
">>Golden Retriever",
">>Labrador Retriever",
">Poodles",
">>Toy Poodle",
">>Standard Poodle"
)
def createList(itemList, indentDelim, styleName):
listArray = []
listItem = ListItem()
level = 0
lastLevel = 0
for levCount in range(0,10):
listArray.append(None)
listArray[0] = List()
for item in itemList:
level = 0;
while (level < len(item) and item[level] == indentDelim):
level +=1
item = item[level:]
if (level > lastLevel): # open the sub-levels
for levCount in range(lastLevel+1, level+1):
listArray[levCount] = List()
elif (level < lastLevel): # close off the intervening lists
for levCount in range(lastLevel, level, -1):
listArray[levCount-1].childNodes[-1].addElement(listArray[levCount])
# now that we are at the proper level, add the item.
listArray[level].setAttribute( 'stylename', styleName );
listItem = ListItem()
para = P(text=item);
listItem.addElement(para);
listArray[level].addElement(listItem);
lastLevel = level;
# close off any remaining open lists
for levCount in range(lastLevel, 0, -1):
listArray[levCount-1].childNodes[-1].addElement(listArray[levCount])
return listArray[0]
textdoc = OpenDocumentText()
s = textdoc.styles
listStyle = easyliststyle.styleFromString('bullet1', bulletListSpec,
',', '0.6cm', easyliststyle.SHOW_ONE_LEVEL)
s.addElement(listStyle)
listElement = createList(itemList, '>', 'bullet1')
textdoc.text.addElement(listElement)
para = P(text="-----------------------");
textdoc.text.addElement(para)
listStyle = easyliststyle.styleFromList('num1', numberListSpecArray,
'0.25in', easyliststyle.SHOW_ALL_LEVELS)
s.addElement(listStyle)
listElement = createList(itemList, '>', 'num1')
textdoc.text.addElement(listElement)
para = P(text="-----------------------");
textdoc.text.addElement(para)
listStyle = easyliststyle.styleFromString('mix1', mixedListSpec,
'!', '0.8cm', easyliststyle.SHOW_ONE_LEVEL)
s.addElement(listStyle)
listElement = createList(itemList, '>', 'mix1')
textdoc.text.addElement(listElement)
textdoc.save("easylist_odfpy.odt")
# vim: set expandtab sw=4 :

Some files were not shown because too many files have changed in this diff Show More