From b0b66fcae954c26aa99799c5f36b5c098d311eb3 Mon Sep 17 00:00:00 2001 From: henrikjust <henrikjust@f0f2a975-2e09-46c8-9428-3b39399b9f3c> Date: Fri, 20 Feb 2009 09:37:06 +0000 Subject: [PATCH] Initial import git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@5 f0f2a975-2e09-46c8-9428-3b39399b9f3c --- source/COPYING.TXT | 460 ++ source/distro/COPYING.TXT | 460 ++ source/distro/History.txt | 121 + source/distro/Readme.txt | 18 + source/distro/changelog.txt | 325 ++ source/distro/doc/user-manual.odt | Bin 0 -> 215915 bytes source/distro/doc/w2llogo.png | Bin 0 -> 5608 bytes source/distro/latex/obsolete/writer.sty | 83 + source/distro/latex/ooomath.sty | 83 + .../samples/config/google-docs-config.xml | 113 + source/distro/samples/xhtml/sample-xhtml.odt | Bin 0 -> 12597 bytes source/distro/w2l | 22 + source/distro/w2l.bat | 19 + source/distro/xslt/pmathml.xsl | 612 +++ source/distro/xslt/pmathmlcss.xsl | 872 ++++ source/idl/readme-idl.txt | 30 + .../writer2latex/XW2LStarMathConverter.idl | 22 + .../writer2latex/XW2LStarMathConverter.class | Bin 0 -> 557 bytes source/idl/writer2latex/writer2latex.rdb | Bin 0 -> 8192 bytes source/idl/writer2xhtml/XBatchConverter.idl | 52 + .../da/writer2xhtml/XBatchConverter.class | Bin 0 -> 564 bytes .../da/writer2xhtml/XBatchHandler.class | Bin 0 -> 734 bytes source/idl/writer2xhtml/writer2xhtml.rdb | Bin 0 -> 16384 bytes .../w2lcommon/filter/ByteArrayXStream.java | 188 + .../w2lcommon/filter/ExportFilterBase.java | 504 +++ .../w2lcommon/filter/FilterDataParser.java | 271 ++ .../filter/GraphicConverterImpl.java | 67 + .../filter/GraphicConverterImpl1.java | 194 + .../filter/GraphicConverterImpl2.java | 267 ++ .../w2lcommon/filter/OptionsDialogBase.java | 547 +++ .../da/comp/w2lcommon/helper/DialogBase.java | 503 +++ .../da/comp/w2lcommon/helper/MessageBox.java | 105 + .../comp/w2lcommon/helper/PropertyHelper.java | 78 + .../comp/writer2latex/LaTeXOptionsDialog.java | 351 ++ .../da/comp/writer2latex/W2LExportFilter.java | 56 + .../da/comp/writer2latex/W2LRegistration.java | 103 + .../writer2latex/W2LStarMathConverter.java | 125 + .../da/comp/writer2xhtml/BatchConverter.java | 541 +++ .../writer2xhtml/BatchHandlerAdapter.java | 72 + .../da/comp/writer2xhtml/W2XExportFilter.java | 56 + .../da/comp/writer2xhtml/W2XRegistration.java | 119 + .../comp/writer2xhtml/XhtmlOptionsDialog.java | 216 + .../writer2xhtml/XhtmlOptionsDialogCalc.java | 174 + .../writer2xhtml/XhtmlOptionsDialogXsl.java | 52 + source/java/writer2latex/Application.java | 382 ++ .../java/writer2latex/BatchHandlerImpl.java | 94 + .../java/writer2latex/api/BatchConverter.java | 96 + .../java/writer2latex/api/BatchHandler.java | 77 + source/java/writer2latex/api/Config.java | 99 + source/java/writer2latex/api/Converter.java | 99 + .../writer2latex/api/ConverterFactory.java | 126 + .../writer2latex/api/ConverterResult.java | 59 + .../writer2latex/api/GraphicConverter.java | 58 + .../java/writer2latex/api/IndexPageEntry.java | 141 + source/java/writer2latex/api/MIMETypes.java | 55 + source/java/writer2latex/api/OutputFile.java | 53 + source/java/writer2latex/api/Package.html | 13 + .../writer2latex/api/StarMathConverter.java | 61 + .../writer2latex/base/BatchConverterBase.java | 182 + .../java/writer2latex/base/BooleanOption.java | 44 + source/java/writer2latex/base/ConfigBase.java | 170 + .../java/writer2latex/base/ConverterBase.java | 117 + .../java/writer2latex/base/IntegerOption.java | 40 + source/java/writer2latex/base/Option.java | 45 + source/java/writer2latex/base/Package.html | 17 + .../writer2latex/bibtex/BibTeXDocument.java | 238 + .../java/writer2latex/bibtex/Converter.java | 91 + source/java/writer2latex/bibtex/Package.html | 14 + .../java/writer2latex/latex/BibConverter.java | 157 + .../writer2latex/latex/BlockConverter.java | 378 ++ .../writer2latex/latex/CaptionConverter.java | 163 + .../latex/CharStyleConverter.java | 507 +++ .../writer2latex/latex/ColorConverter.java | 202 + .../latex/ContentHandlingOption.java | 43 + .../writer2latex/latex/ConverterHelper.java | 49 + .../writer2latex/latex/ConverterPalette.java | 303 ++ .../writer2latex/latex/DrawConverter.java | 496 +++ .../writer2latex/latex/FieldConverter.java | 672 +++ .../writer2latex/latex/HeadingConverter.java | 357 ++ .../writer2latex/latex/IndexConverter.java | 242 ++ .../writer2latex/latex/InlineConverter.java | 636 +++ .../java/writer2latex/latex/LaTeXConfig.java | 543 +++ .../writer2latex/latex/LaTeXDocument.java | 152 + .../latex/LaTeXDocumentPortion.java | 214 + .../latex/ListStyleConverter.java | 384 ++ .../writer2latex/latex/MathmlConverter.java | 232 + .../writer2latex/latex/NoteConverter.java | 391 ++ source/java/writer2latex/latex/Package.html | 16 + .../latex/PageStyleConverter.java | 596 +++ .../java/writer2latex/latex/ParConverter.java | 546 +++ .../writer2latex/latex/SectionConverter.java | 135 + .../writer2latex/latex/StarMathConverter.java | 1618 +++++++ .../writer2latex/latex/StyleConverter.java | 51 + .../writer2latex/latex/TableConverter.java | 366 ++ .../writer2latex/latex/TableFormatter.java | 451 ++ .../java/writer2latex/latex/config/clean.xml | 77 + .../writer2latex/latex/config/default.xml | 8 + .../writer2latex/latex/config/pdfprint.xml | 43 + .../writer2latex/latex/config/pdfscreen.xml | 50 + .../writer2latex/latex/config/ultraclean.xml | 77 + .../writer2latex/latex/i18n/ClassicI18n.java | 697 +++ source/java/writer2latex/latex/i18n/I18n.java | 118 + .../java/writer2latex/latex/i18n/Package.html | 16 + .../latex/i18n/ReplacementTrie.java | 56 + .../latex/i18n/ReplacementTrieNode.java | 127 + .../latex/i18n/UnicodeCharacter.java | 52 + .../writer2latex/latex/i18n/UnicodeRow.java | 43 + .../latex/i18n/UnicodeStringParser.java | 79 + .../writer2latex/latex/i18n/UnicodeTable.java | 169 + .../latex/i18n/UnicodeTableHandler.java | 166 + .../writer2latex/latex/i18n/XeTeXI18n.java | 120 + .../java/writer2latex/latex/i18n/symbols.xml | 3824 +++++++++++++++++ .../writer2latex/latex/util/BeforeAfter.java | 75 + .../java/writer2latex/latex/util/Context.java | 310 ++ .../writer2latex/latex/util/HeadingMap.java | 69 + source/java/writer2latex/latex/util/Info.java | 76 + .../java/writer2latex/latex/util/Package.html | 11 + .../writer2latex/latex/util/StyleMap.java | 99 + .../writer2latex/latex/util/StyleMapItem.java | 36 + source/java/writer2latex/office/BibMark.java | 146 + source/java/writer2latex/office/CellView.java | 40 + .../writer2latex/office/ControlReader.java | 151 + .../writer2latex/office/FontDeclaration.java | 61 + .../java/writer2latex/office/FormReader.java | 79 + .../java/writer2latex/office/FormsReader.java | 114 + .../java/writer2latex/office/ImageLoader.java | 194 + .../java/writer2latex/office/IndexMark.java | 97 + .../java/writer2latex/office/ListCounter.java | 126 + .../java/writer2latex/office/ListStyle.java | 128 + .../java/writer2latex/office/LoftReader.java | 135 + .../java/writer2latex/office/MIMETypes.java | 159 + .../java/writer2latex/office/MasterPage.java | 66 + source/java/writer2latex/office/MetaData.java | 169 + .../writer2latex/office/OfficeReader.java | 1149 +++++ .../java/writer2latex/office/OfficeStyle.java | 66 + .../office/OfficeStyleFamily.java | 137 + source/java/writer2latex/office/Package.html | 13 + .../java/writer2latex/office/PageLayout.java | 91 + .../java/writer2latex/office/PropertySet.java | 98 + .../java/writer2latex/office/SVMReader.java | 232 + .../office/StyleWithProperties.java | 298 ++ .../java/writer2latex/office/TableLine.java | 66 + .../java/writer2latex/office/TableRange.java | 122 + .../writer2latex/office/TableRangeParser.java | 131 + .../java/writer2latex/office/TableReader.java | 395 ++ .../java/writer2latex/office/TableView.java | 196 + .../java/writer2latex/office/TocReader.java | 187 + .../java/writer2latex/office/XMLString.java | 447 ++ source/java/writer2latex/util/Base64.java | 1811 ++++++++ source/java/writer2latex/util/CSVList.java | 69 + .../util/ExportNameCollection.java | 102 + source/java/writer2latex/util/Misc.java | 356 ++ source/java/writer2latex/util/Package.html | 11 + .../writer2latex/util/SimpleInputBuffer.java | 106 + .../xhtml/BatchConverterImpl.java | 231 + .../xhtml/CellStyleConverter.java | 104 + source/java/writer2latex/xhtml/Converter.java | 628 +++ .../writer2latex/xhtml/ConverterHelper.java | 92 + .../writer2latex/xhtml/DrawConverter.java | 939 ++++ .../xhtml/FrameStyleConverter.java | 299 ++ source/java/writer2latex/xhtml/L10n.java | 154 + .../writer2latex/xhtml/LinkDescriptor.java | 40 + .../xhtml/ListStyleConverter.java | 157 + .../writer2latex/xhtml/MathConverter.java | 238 + source/java/writer2latex/xhtml/Package.html | 15 + .../xhtml/PageStyleConverter.java | 137 + .../writer2latex/xhtml/ParStyleConverter.java | 195 + .../xhtml/PresentationStyleConverter.java | 157 + .../writer2latex/xhtml/RowStyleConverter.java | 80 + .../xhtml/SectionStyleConverter.java | 84 + .../writer2latex/xhtml/StyleConverter.java | 175 + .../xhtml/StyleConverterHelper.java | 121 + source/java/writer2latex/xhtml/StyleInfo.java | 41 + .../StyleWithPropertiesConverterHelper.java | 144 + .../writer2latex/xhtml/TableConverter.java | 453 ++ .../xhtml/TableStyleConverter.java | 114 + .../writer2latex/xhtml/TextConverter.java | 1811 ++++++++ .../xhtml/TextStyleConverter.java | 432 ++ .../writer2latex/xhtml/Xhtml10Converter.java | 35 + .../java/writer2latex/xhtml/XhtmlConfig.java | 255 ++ .../writer2latex/xhtml/XhtmlDocument.java | 782 ++++ .../writer2latex/xhtml/XhtmlFormatOption.java | 43 + .../xhtml/XhtmlMathMLConverter.java | 35 + .../xhtml/XhtmlMathMLXSLConverter.java | 35 + .../writer2latex/xhtml/XhtmlStyleMap.java | 69 + .../writer2latex/xhtml/config/cleanxhtml.xml | 66 + .../xmerge/BinaryGraphicsDocument.java | 165 + .../java/writer2latex/xmerge/ConvertData.java | 179 + .../java/writer2latex/xmerge/DOMDocument.java | 400 ++ source/java/writer2latex/xmerge/Document.java | 86 + .../xmerge/EmbeddedBinaryObject.java | 143 + .../writer2latex/xmerge/EmbeddedObject.java | 129 + .../xmerge/EmbeddedXMLObject.java | 312 ++ .../writer2latex/xmerge/NewDOMDocument.java | 160 + .../writer2latex/xmerge/OfficeConstants.java | 455 ++ .../writer2latex/xmerge/OfficeDocument.java | 1287 ++++++ .../xmerge/OfficeDocumentException.java | 145 + .../java/writer2latex/xmerge/OfficeZip.java | 473 ++ source/java/writer2latex/xmerge/Package.html | 15 + source/oxt/writer2latex/META-INF/manifest.xml | 33 + source/oxt/writer2latex/Options.xcs | 59 + source/oxt/writer2latex/Options.xcu | 90 + .../W2LDialogs/DialogStrings_da_DK.properties | 111 + .../W2LDialogs/DialogStrings_de_DE.properties | 111 + .../W2LDialogs/DialogStrings_en_US.default | 0 .../W2LDialogs/DialogStrings_en_US.properties | 111 + .../W2LDialogs/DialogStrings_fr_FR.properties | 111 + .../W2LDialogs/DialogStrings_ru_RU.properties | 111 + .../W2LDialogs/DialogStrings_uk_UA.properties | 111 + .../writer2latex/W2LDialogs/LaTeXOptions.xdl | 108 + .../oxt/writer2latex/W2LDialogs/Module1.xba | 7 + source/oxt/writer2latex/W2LDialogs/dialog.xlb | 5 + source/oxt/writer2latex/W2LDialogs/script.xlb | 5 + source/oxt/writer2latex/desc_da.txt | 1 + source/oxt/writer2latex/desc_en.txt | 1 + source/oxt/writer2latex/desc_fr.txt | 1 + source/oxt/writer2latex/desc_ru.txt | 1 + source/oxt/writer2latex/desc_uk.txt | 1 + source/oxt/writer2latex/description.xml | 30 + source/oxt/writer2latex/w2l_filters.xcu | 45 + source/oxt/writer2latex/w2l_types.xcu | 32 + source/oxt/writer2xhtml/META-INF/manifest.xml | 32 + source/oxt/writer2xhtml/Options.xcs | 72 + source/oxt/writer2xhtml/Options.xcu | 110 + .../W2XDialogs/DialogStrings_da_DK.properties | 131 + .../W2XDialogs/DialogStrings_de_DE.properties | 131 + .../W2XDialogs/DialogStrings_en_US.default | 0 .../W2XDialogs/DialogStrings_en_US.properties | 131 + .../W2XDialogs/DialogStrings_fr_FR.properties | 131 + .../W2XDialogs/DialogStrings_ru_RU.properties | 131 + .../W2XDialogs/DialogStrings_uk_UA.properties | 131 + .../oxt/writer2xhtml/W2XDialogs/Module1.xba | 7 + .../writer2xhtml/W2XDialogs/XhtmlOptions.xdl | 69 + .../W2XDialogs/XhtmlOptionsCalc.xdl | 38 + source/oxt/writer2xhtml/W2XDialogs/dialog.xlb | 6 + source/oxt/writer2xhtml/W2XDialogs/script.xlb | 5 + source/oxt/writer2xhtml/desc_da.txt | 1 + source/oxt/writer2xhtml/desc_en.txt | 1 + source/oxt/writer2xhtml/desc_fr.txt | 1 + source/oxt/writer2xhtml/desc_ru.txt | 1 + source/oxt/writer2xhtml/desc_uk.txt | 1 + source/oxt/writer2xhtml/description.xml | 29 + source/oxt/writer2xhtml/w2x_filters.xcu | 64 + source/oxt/writer2xhtml/w2x_types.xcu | 44 + .../xhtml-config-sample/META-INF/manifest.xml | 8 + source/oxt/xhtml-config-sample/Options.xcu | 45 + source/oxt/xhtml-config-sample/Paths.xcu | 11 + .../config/sampleconfig.xml | 52 + .../config/sampletemplate.xhtml | 50 + .../oxt/xhtml-config-sample/description.xml | 6 + .../xhtml-sample-config/XHTML template.ott | Bin 0 -> 7237 bytes source/readme-source.txt | 68 + 252 files changed, 49000 insertions(+) create mode 100644 source/COPYING.TXT create mode 100644 source/distro/COPYING.TXT create mode 100644 source/distro/History.txt create mode 100644 source/distro/Readme.txt create mode 100644 source/distro/changelog.txt create mode 100644 source/distro/doc/user-manual.odt create mode 100644 source/distro/doc/w2llogo.png create mode 100644 source/distro/latex/obsolete/writer.sty create mode 100644 source/distro/latex/ooomath.sty create mode 100644 source/distro/samples/config/google-docs-config.xml create mode 100644 source/distro/samples/xhtml/sample-xhtml.odt create mode 100644 source/distro/w2l create mode 100644 source/distro/w2l.bat create mode 100644 source/distro/xslt/pmathml.xsl create mode 100644 source/distro/xslt/pmathmlcss.xsl create mode 100644 source/idl/readme-idl.txt create mode 100644 source/idl/writer2latex/XW2LStarMathConverter.idl create mode 100644 source/idl/writer2latex/org/openoffice/da/writer2latex/XW2LStarMathConverter.class create mode 100644 source/idl/writer2latex/writer2latex.rdb create mode 100644 source/idl/writer2xhtml/XBatchConverter.idl create mode 100644 source/idl/writer2xhtml/org/openoffice/da/writer2xhtml/XBatchConverter.class create mode 100644 source/idl/writer2xhtml/org/openoffice/da/writer2xhtml/XBatchHandler.class create mode 100644 source/idl/writer2xhtml/writer2xhtml.rdb create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/filter/ByteArrayXStream.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/filter/ExportFilterBase.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/filter/FilterDataParser.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl1.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl2.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/filter/OptionsDialogBase.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/helper/DialogBase.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/helper/MessageBox.java create mode 100644 source/java/org/openoffice/da/comp/w2lcommon/helper/PropertyHelper.java create mode 100644 source/java/org/openoffice/da/comp/writer2latex/LaTeXOptionsDialog.java create mode 100644 source/java/org/openoffice/da/comp/writer2latex/W2LExportFilter.java create mode 100644 source/java/org/openoffice/da/comp/writer2latex/W2LRegistration.java create mode 100644 source/java/org/openoffice/da/comp/writer2latex/W2LStarMathConverter.java create mode 100644 source/java/org/openoffice/da/comp/writer2xhtml/BatchConverter.java create mode 100644 source/java/org/openoffice/da/comp/writer2xhtml/BatchHandlerAdapter.java create mode 100644 source/java/org/openoffice/da/comp/writer2xhtml/W2XExportFilter.java create mode 100644 source/java/org/openoffice/da/comp/writer2xhtml/W2XRegistration.java create mode 100644 source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialog.java create mode 100644 source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogCalc.java create mode 100644 source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogXsl.java create mode 100644 source/java/writer2latex/Application.java create mode 100644 source/java/writer2latex/BatchHandlerImpl.java create mode 100644 source/java/writer2latex/api/BatchConverter.java create mode 100644 source/java/writer2latex/api/BatchHandler.java create mode 100644 source/java/writer2latex/api/Config.java create mode 100644 source/java/writer2latex/api/Converter.java create mode 100644 source/java/writer2latex/api/ConverterFactory.java create mode 100644 source/java/writer2latex/api/ConverterResult.java create mode 100644 source/java/writer2latex/api/GraphicConverter.java create mode 100644 source/java/writer2latex/api/IndexPageEntry.java create mode 100644 source/java/writer2latex/api/MIMETypes.java create mode 100644 source/java/writer2latex/api/OutputFile.java create mode 100644 source/java/writer2latex/api/Package.html create mode 100644 source/java/writer2latex/api/StarMathConverter.java create mode 100644 source/java/writer2latex/base/BatchConverterBase.java create mode 100644 source/java/writer2latex/base/BooleanOption.java create mode 100644 source/java/writer2latex/base/ConfigBase.java create mode 100644 source/java/writer2latex/base/ConverterBase.java create mode 100644 source/java/writer2latex/base/IntegerOption.java create mode 100644 source/java/writer2latex/base/Option.java create mode 100644 source/java/writer2latex/base/Package.html create mode 100644 source/java/writer2latex/bibtex/BibTeXDocument.java create mode 100644 source/java/writer2latex/bibtex/Converter.java create mode 100644 source/java/writer2latex/bibtex/Package.html create mode 100644 source/java/writer2latex/latex/BibConverter.java create mode 100644 source/java/writer2latex/latex/BlockConverter.java create mode 100644 source/java/writer2latex/latex/CaptionConverter.java create mode 100644 source/java/writer2latex/latex/CharStyleConverter.java create mode 100644 source/java/writer2latex/latex/ColorConverter.java create mode 100644 source/java/writer2latex/latex/ContentHandlingOption.java create mode 100644 source/java/writer2latex/latex/ConverterHelper.java create mode 100644 source/java/writer2latex/latex/ConverterPalette.java create mode 100644 source/java/writer2latex/latex/DrawConverter.java create mode 100644 source/java/writer2latex/latex/FieldConverter.java create mode 100644 source/java/writer2latex/latex/HeadingConverter.java create mode 100644 source/java/writer2latex/latex/IndexConverter.java create mode 100644 source/java/writer2latex/latex/InlineConverter.java create mode 100644 source/java/writer2latex/latex/LaTeXConfig.java create mode 100644 source/java/writer2latex/latex/LaTeXDocument.java create mode 100644 source/java/writer2latex/latex/LaTeXDocumentPortion.java create mode 100644 source/java/writer2latex/latex/ListStyleConverter.java create mode 100644 source/java/writer2latex/latex/MathmlConverter.java create mode 100644 source/java/writer2latex/latex/NoteConverter.java create mode 100644 source/java/writer2latex/latex/Package.html create mode 100644 source/java/writer2latex/latex/PageStyleConverter.java create mode 100644 source/java/writer2latex/latex/ParConverter.java create mode 100644 source/java/writer2latex/latex/SectionConverter.java create mode 100644 source/java/writer2latex/latex/StarMathConverter.java create mode 100644 source/java/writer2latex/latex/StyleConverter.java create mode 100644 source/java/writer2latex/latex/TableConverter.java create mode 100644 source/java/writer2latex/latex/TableFormatter.java create mode 100644 source/java/writer2latex/latex/config/clean.xml create mode 100644 source/java/writer2latex/latex/config/default.xml create mode 100644 source/java/writer2latex/latex/config/pdfprint.xml create mode 100644 source/java/writer2latex/latex/config/pdfscreen.xml create mode 100644 source/java/writer2latex/latex/config/ultraclean.xml create mode 100644 source/java/writer2latex/latex/i18n/ClassicI18n.java create mode 100644 source/java/writer2latex/latex/i18n/I18n.java create mode 100644 source/java/writer2latex/latex/i18n/Package.html create mode 100644 source/java/writer2latex/latex/i18n/ReplacementTrie.java create mode 100644 source/java/writer2latex/latex/i18n/ReplacementTrieNode.java create mode 100644 source/java/writer2latex/latex/i18n/UnicodeCharacter.java create mode 100644 source/java/writer2latex/latex/i18n/UnicodeRow.java create mode 100644 source/java/writer2latex/latex/i18n/UnicodeStringParser.java create mode 100644 source/java/writer2latex/latex/i18n/UnicodeTable.java create mode 100644 source/java/writer2latex/latex/i18n/UnicodeTableHandler.java create mode 100644 source/java/writer2latex/latex/i18n/XeTeXI18n.java create mode 100644 source/java/writer2latex/latex/i18n/symbols.xml create mode 100644 source/java/writer2latex/latex/util/BeforeAfter.java create mode 100644 source/java/writer2latex/latex/util/Context.java create mode 100644 source/java/writer2latex/latex/util/HeadingMap.java create mode 100644 source/java/writer2latex/latex/util/Info.java create mode 100644 source/java/writer2latex/latex/util/Package.html create mode 100644 source/java/writer2latex/latex/util/StyleMap.java create mode 100644 source/java/writer2latex/latex/util/StyleMapItem.java create mode 100644 source/java/writer2latex/office/BibMark.java create mode 100644 source/java/writer2latex/office/CellView.java create mode 100644 source/java/writer2latex/office/ControlReader.java create mode 100644 source/java/writer2latex/office/FontDeclaration.java create mode 100644 source/java/writer2latex/office/FormReader.java create mode 100644 source/java/writer2latex/office/FormsReader.java create mode 100644 source/java/writer2latex/office/ImageLoader.java create mode 100644 source/java/writer2latex/office/IndexMark.java create mode 100644 source/java/writer2latex/office/ListCounter.java create mode 100644 source/java/writer2latex/office/ListStyle.java create mode 100644 source/java/writer2latex/office/LoftReader.java create mode 100644 source/java/writer2latex/office/MIMETypes.java create mode 100644 source/java/writer2latex/office/MasterPage.java create mode 100644 source/java/writer2latex/office/MetaData.java create mode 100644 source/java/writer2latex/office/OfficeReader.java create mode 100644 source/java/writer2latex/office/OfficeStyle.java create mode 100644 source/java/writer2latex/office/OfficeStyleFamily.java create mode 100644 source/java/writer2latex/office/Package.html create mode 100644 source/java/writer2latex/office/PageLayout.java create mode 100644 source/java/writer2latex/office/PropertySet.java create mode 100644 source/java/writer2latex/office/SVMReader.java create mode 100644 source/java/writer2latex/office/StyleWithProperties.java create mode 100644 source/java/writer2latex/office/TableLine.java create mode 100644 source/java/writer2latex/office/TableRange.java create mode 100644 source/java/writer2latex/office/TableRangeParser.java create mode 100644 source/java/writer2latex/office/TableReader.java create mode 100644 source/java/writer2latex/office/TableView.java create mode 100644 source/java/writer2latex/office/TocReader.java create mode 100644 source/java/writer2latex/office/XMLString.java create mode 100644 source/java/writer2latex/util/Base64.java create mode 100644 source/java/writer2latex/util/CSVList.java create mode 100644 source/java/writer2latex/util/ExportNameCollection.java create mode 100644 source/java/writer2latex/util/Misc.java create mode 100644 source/java/writer2latex/util/Package.html create mode 100644 source/java/writer2latex/util/SimpleInputBuffer.java create mode 100644 source/java/writer2latex/xhtml/BatchConverterImpl.java create mode 100644 source/java/writer2latex/xhtml/CellStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/Converter.java create mode 100644 source/java/writer2latex/xhtml/ConverterHelper.java create mode 100644 source/java/writer2latex/xhtml/DrawConverter.java create mode 100644 source/java/writer2latex/xhtml/FrameStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/L10n.java create mode 100644 source/java/writer2latex/xhtml/LinkDescriptor.java create mode 100644 source/java/writer2latex/xhtml/ListStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/MathConverter.java create mode 100644 source/java/writer2latex/xhtml/Package.html create mode 100644 source/java/writer2latex/xhtml/PageStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/ParStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/PresentationStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/RowStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/SectionStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/StyleConverter.java create mode 100644 source/java/writer2latex/xhtml/StyleConverterHelper.java create mode 100644 source/java/writer2latex/xhtml/StyleInfo.java create mode 100644 source/java/writer2latex/xhtml/StyleWithPropertiesConverterHelper.java create mode 100644 source/java/writer2latex/xhtml/TableConverter.java create mode 100644 source/java/writer2latex/xhtml/TableStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/TextConverter.java create mode 100644 source/java/writer2latex/xhtml/TextStyleConverter.java create mode 100644 source/java/writer2latex/xhtml/Xhtml10Converter.java create mode 100644 source/java/writer2latex/xhtml/XhtmlConfig.java create mode 100644 source/java/writer2latex/xhtml/XhtmlDocument.java create mode 100644 source/java/writer2latex/xhtml/XhtmlFormatOption.java create mode 100644 source/java/writer2latex/xhtml/XhtmlMathMLConverter.java create mode 100644 source/java/writer2latex/xhtml/XhtmlMathMLXSLConverter.java create mode 100644 source/java/writer2latex/xhtml/XhtmlStyleMap.java create mode 100644 source/java/writer2latex/xhtml/config/cleanxhtml.xml create mode 100644 source/java/writer2latex/xmerge/BinaryGraphicsDocument.java create mode 100644 source/java/writer2latex/xmerge/ConvertData.java create mode 100644 source/java/writer2latex/xmerge/DOMDocument.java create mode 100644 source/java/writer2latex/xmerge/Document.java create mode 100644 source/java/writer2latex/xmerge/EmbeddedBinaryObject.java create mode 100644 source/java/writer2latex/xmerge/EmbeddedObject.java create mode 100644 source/java/writer2latex/xmerge/EmbeddedXMLObject.java create mode 100644 source/java/writer2latex/xmerge/NewDOMDocument.java create mode 100644 source/java/writer2latex/xmerge/OfficeConstants.java create mode 100644 source/java/writer2latex/xmerge/OfficeDocument.java create mode 100644 source/java/writer2latex/xmerge/OfficeDocumentException.java create mode 100644 source/java/writer2latex/xmerge/OfficeZip.java create mode 100644 source/java/writer2latex/xmerge/Package.html create mode 100644 source/oxt/writer2latex/META-INF/manifest.xml create mode 100644 source/oxt/writer2latex/Options.xcs create mode 100644 source/oxt/writer2latex/Options.xcu create mode 100644 source/oxt/writer2latex/W2LDialogs/DialogStrings_da_DK.properties create mode 100644 source/oxt/writer2latex/W2LDialogs/DialogStrings_de_DE.properties create mode 100644 source/oxt/writer2latex/W2LDialogs/DialogStrings_en_US.default create mode 100644 source/oxt/writer2latex/W2LDialogs/DialogStrings_en_US.properties create mode 100644 source/oxt/writer2latex/W2LDialogs/DialogStrings_fr_FR.properties create mode 100644 source/oxt/writer2latex/W2LDialogs/DialogStrings_ru_RU.properties create mode 100644 source/oxt/writer2latex/W2LDialogs/DialogStrings_uk_UA.properties create mode 100644 source/oxt/writer2latex/W2LDialogs/LaTeXOptions.xdl create mode 100644 source/oxt/writer2latex/W2LDialogs/Module1.xba create mode 100644 source/oxt/writer2latex/W2LDialogs/dialog.xlb create mode 100644 source/oxt/writer2latex/W2LDialogs/script.xlb create mode 100644 source/oxt/writer2latex/desc_da.txt create mode 100644 source/oxt/writer2latex/desc_en.txt create mode 100644 source/oxt/writer2latex/desc_fr.txt create mode 100644 source/oxt/writer2latex/desc_ru.txt create mode 100644 source/oxt/writer2latex/desc_uk.txt create mode 100644 source/oxt/writer2latex/description.xml create mode 100644 source/oxt/writer2latex/w2l_filters.xcu create mode 100644 source/oxt/writer2latex/w2l_types.xcu create mode 100644 source/oxt/writer2xhtml/META-INF/manifest.xml create mode 100644 source/oxt/writer2xhtml/Options.xcs create mode 100644 source/oxt/writer2xhtml/Options.xcu create mode 100644 source/oxt/writer2xhtml/W2XDialogs/DialogStrings_da_DK.properties create mode 100644 source/oxt/writer2xhtml/W2XDialogs/DialogStrings_de_DE.properties create mode 100644 source/oxt/writer2xhtml/W2XDialogs/DialogStrings_en_US.default create mode 100644 source/oxt/writer2xhtml/W2XDialogs/DialogStrings_en_US.properties create mode 100644 source/oxt/writer2xhtml/W2XDialogs/DialogStrings_fr_FR.properties create mode 100644 source/oxt/writer2xhtml/W2XDialogs/DialogStrings_ru_RU.properties create mode 100644 source/oxt/writer2xhtml/W2XDialogs/DialogStrings_uk_UA.properties create mode 100644 source/oxt/writer2xhtml/W2XDialogs/Module1.xba create mode 100644 source/oxt/writer2xhtml/W2XDialogs/XhtmlOptions.xdl create mode 100644 source/oxt/writer2xhtml/W2XDialogs/XhtmlOptionsCalc.xdl create mode 100644 source/oxt/writer2xhtml/W2XDialogs/dialog.xlb create mode 100644 source/oxt/writer2xhtml/W2XDialogs/script.xlb create mode 100644 source/oxt/writer2xhtml/desc_da.txt create mode 100644 source/oxt/writer2xhtml/desc_en.txt create mode 100644 source/oxt/writer2xhtml/desc_fr.txt create mode 100644 source/oxt/writer2xhtml/desc_ru.txt create mode 100644 source/oxt/writer2xhtml/desc_uk.txt create mode 100644 source/oxt/writer2xhtml/description.xml create mode 100644 source/oxt/writer2xhtml/w2x_filters.xcu create mode 100644 source/oxt/writer2xhtml/w2x_types.xcu create mode 100644 source/oxt/xhtml-config-sample/META-INF/manifest.xml create mode 100644 source/oxt/xhtml-config-sample/Options.xcu create mode 100644 source/oxt/xhtml-config-sample/Paths.xcu create mode 100644 source/oxt/xhtml-config-sample/config/sampleconfig.xml create mode 100644 source/oxt/xhtml-config-sample/config/sampletemplate.xhtml create mode 100644 source/oxt/xhtml-config-sample/description.xml create mode 100644 source/oxt/xhtml-config-sample/template/xhtml-sample-config/XHTML template.ott create mode 100644 source/readme-source.txt diff --git a/source/COPYING.TXT b/source/COPYING.TXT new file mode 100644 index 0000000..d18617e --- /dev/null +++ b/source/COPYING.TXT @@ -0,0 +1,460 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + diff --git a/source/distro/COPYING.TXT b/source/distro/COPYING.TXT new file mode 100644 index 0000000..101dfa2 --- /dev/null +++ b/source/distro/COPYING.TXT @@ -0,0 +1,460 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + diff --git a/source/distro/History.txt b/source/distro/History.txt new file mode 100644 index 0000000..e80ec69 --- /dev/null +++ b/source/distro/History.txt @@ -0,0 +1,121 @@ +Version history for Writer2LaTeX +================================ + +Version 0.9.4 (February 2009) + -Filters: + - Changed to oxt format and separated into Writer2LaTeX and Writer2xhtml extensions + - Added (localized) dialogs to the filters + - LaTeX: + - Export vector graphics to pdf for use with pdfTeX. + - Support equations generated by OOoLaTeX. + - XHTML: + - Improved navigation features for splitted documents + - Do not export hidden or filtered rows and columns. Apply print ranges. + - Improved batch conversion + - General: + - Export images to a subdirectory. + +Version 0.5 (June 2007) + - LaTeX: + - support fancyhdr.sty + - support geometry.sty + - support supertabular.sty + - support tabulary.sty + - support colortbl.sty + - support l, c, r columns in tables + - support floating figures and tables + - some graphics improvements + - custom tabstop handling + - flexible handling of notes (ignore, comment, marginpar, pdf annotation) + - string replace (eg. LaTeX -> \LaTeX) + - XHTML: + - convert an entire directory with index page(s) + - convert using a template document + - support html forms + - custom tabstop handling + - support continued lists + - improved export of table formatting + - support background images + - improved table of contents + - General: + - support OpenDocument + - now installable as uno package (extension) + - no longer using the XMerge framework + +Version 0.4 (July 2005) + - LaTeX: + - improved readability of output (wrap long lines, do not include unused + labels and hypertargets) + - support for more languages + - writer.sty is replaced by the optional package ooomath.sty + - color.sty is now optional + - new options to ignore hard page and line breaks + - new option to ignore double spaces + - new option to specify that the document is *not* multilingual + - support for cp1251 (Windows cyrillic) and latin2 inputenc + - improved style maps for paragraphs (avoid line break, verbatim content) + - cleaned up configuration of formatting + - improved export of list formatting + - support for other documentclasses than article + - support for lastpage.sty + - support for eurosym.sty + - support for tipa.sty + - support for various 8-bit fonts: Wingdings, Windows Symbol + - support for OpenSymbol + - XHTML: + - converts units to px (default) + - Calc2xhtml added + - improved support for table formatting; table formatting is now always + exported + - (partial) bidi support + - support for sections (margins and background color) + - new option to scale column widths + - new options to ignore double spaces, hard line breaks and empty paragraphs + - General + - Added ant build file (provided by Michael Niedermair) + - Now possible to specify output path+file name on command line + - removed usage of document specific configuration files (*-config.xml) + - new option to create user configuration automatically + +Version 0.3.2 (February 2004) + - LaTeX: + - support for custom math symbols (%symbol) + - improved handling of paragraph formatting + - support for table borders + - support for alphabetical index + - support for pdf meta data + - XHTML: + - support for three different XHTML variants + - long documents can be split in several XHTML documents + - support for custom style sheets + - more flexible handling of fomatting + - support for most XHTML elements (including blockquote, dl, hr) + - support for alphabetical index + - support for meta data + +Version 0.3 (October 2003) + - support for BibTeX + - support for greek and russian text + - support for more languages + - support for latin1, cp1250, iso-8859-7, koi8-r and utf8 (Dominique + Unruh's ucs.sty) inputencodings + - support for symbol fonts: pifont.sty, wasysym.sty, ifsym.sty and + bbding.sty + - support for ulem.sty (underline/cross out) + - export of footnotes configuration, list label styles, heading + styles and hard paragraph formatting can be disabled + - custom export of list styles + - bugfixes + +Version 0.2 (March 2003) + - support for tables + - support for graphics + - support for outline numbering and list styles + - support for page styles + - support for sections (columns) + - custom export of text and paragraph styles + - integration with xmerge (enables use as export filter) + - export to XHTML + MathML + +Version 0.1 (September 2002) + - first simple version \ No newline at end of file diff --git a/source/distro/Readme.txt b/source/distro/Readme.txt new file mode 100644 index 0000000..f87afdd --- /dev/null +++ b/source/distro/Readme.txt @@ -0,0 +1,18 @@ +Writer2LaTeX version 0.9.4 +========================== + +This is the distribution of Writer2LaTeX version 0.9.4 + +Latest version can be found at the web site + http://writer2latex.sourceforge.net + +You can read about installation and usage of Writer2LaTeX +in the user guide, which is included in the +directory doc. + +Bugs and feature requests should be reported to + henrikjust (at) openoffice.org + + +February 2009 +Henrik Just diff --git a/source/distro/changelog.txt b/source/distro/changelog.txt new file mode 100644 index 0000000..25b4bcf --- /dev/null +++ b/source/distro/changelog.txt @@ -0,0 +1,325 @@ +Changelog for Writer2LaTeX version 0.5 -> 1.0 + +---------- version 1.0 beta ---------- + +[w2l] Bugfix: Fixed crash for tables with fewer columns than rows + +---------- version 0.9.4 ----------- + +[w2l] Added sample LaTeX configuration for Google Docs from Elefterios Stamatogiannakis + +[w2x] Added MathML named entites table from Bruno Mascret + +[all] Bugfix: Locked controls are now updated correctly when the dialog is displayed + +[w2l] New backed xetex (initial prototype) + +[c2x] Bugfix: Corrected wrong calculation of column number for columns with two letters + (affected apply_print_ranges=true) + +[c2x] Bugfix: Ensure that we always export a document, even if there are no defined + print ranges (affected apply_print_ranges=true) + +[c2x] Bugfix: Now also exports uplink for spreadsheets + +[w2l] Bugfix: No longer incorrectly assume math mode when exporting plain text + +[all] Added support for the new Writer notes/annotations (which now can contain several + formatted paragraphs rather than a single, plain text paragraph) + +[all] Added russian and ukrainian translations from Volodymyr Volkov + +[all] Bugfix: Removed PreferredFilter propery from type definitions (makes sense + only for import filters) + +[all] Changed unix startscript: By default assume that w2l and writer2latex are + in the same directory (thus using dirname $0), and assume that the java executable + is either in the PATH or the location is defined by JAVA_HOME + (which these assumptions manual editing of the script is usually not required) + +[all] Added french translation from Jacques Lamoine + +[w2l] The option simple_table_limit now refers to the *total* width of the table + measured in characters rather than the width of individual cells + +[w2x] Bugfix: The position and dimensions of a frame is now calculated correctly + (previously the size of margins, borders and padding was not taken into account) + +[w2l] The inputencoding utf8 now uses the LaTeX standard rather than ucs.sty + (which only supports latin and cyrillic alphabets) + +[w2l] New style map family "text-attribute" to map text formatting to LaTeX code + currently only the values "bold", "italic", "small-caps", "subscript" and + "superscript" are supported, and the verbatim attribute is not supported + +---------- version 0.9.3 ----------- + +[all] All source files are now US ASCII; added encoding of source files to build file + +[all] Added more File-based methods to the API + +[w2l] The paper sizes defined by standard LaTeX document classes are now + identified and added as global options (a4paper, a5paper, b5paper, + letterpaper, legalpaper, executivepaper, including landscape variants) + +[w2x] Bugfix: Now only exports the name of a text box if it actually exists! + +[w2l] Removed option "pagecolor" from \hypersetup (this option has been removed + from the hyperref package) + +[all] Filter bugfix: The new option name "ConfigURL" is now used in the dialogs + +[all] Added (partial) german localization from Martin Friedrich + +[w2x] New option use_named_entities: When set to true, named character entities + as defined in xhtml 1.0 are used (currently only those within ISO latin 1) + +[w2x] Characters not supported by the encoding are now exported as numeric + character entities + +[w2x] Bugfix: Internal hyperlinks to tables, sections, frames and graphics now + works correctly + +[w2x] Bugfix: Subtables are no longer exported with an id attribute + +[all] Bugfix: table:number-rows-repeated is now treated correctly + +[c2x] New option: apply_print_ranges + +---------- version 0.9.2 ----------- + +[c2x] No longer exports <tbody> for tables without <thead> + +[all] Renamed "class" to "family" in style maps to avoid confusion + with css classes (old name is still accepted) + +[all] Filter: Renamed template and config options to the more + descriptive TemplateURL and ConfigURL + +[all] API: Added convenience method convert(File) to Converterer + +[all] API: Added convenience method write(File) to ConverterResult + +[all] New option: save_images_in_subdir + +[all] Use Robert Harders public domain Base64 decoder (replacing + the non-standard decoder in sun.misc) + +[all] MIMETypes used in the API are now defined in writer2latex.api.MIMETypes + +[all] Renamed the option keep_image_size to original_image_size (the meaning of + this option was confusing). The old name is still recognized. + +[all] OOo now saves SVM files with a MIME type in the package format. + This is now recognized by w2l, which implies that GraphicConverter + implementations can use SVM files from a package + +[w2x] New uno service org.openoffice.da.writer2xhtml.BatchConverter + provides batch conversion via uno. Interaction with the user can be implemented + using the interface org.openoffice.da.writer2xhtml.XBatchHandler + +[w2x] Batch processing from the command line can now be cancelled (using the Enter key) + +[all] API: Created new API for batch conversion + +[w2l] Bugfix: Tables were in rare cases exported with invalid row alignment + (null character) + +[w2x] Filter ui bugfix: Default value for RepeatLevels changed from + 6 to 5 (6 was not valid) + +[w2x] Filter: The xhtml export now uses it's own custom configuration + (writer2xhtml.xml, also stored in the user configuration of OOo) + +[w2l] Tabstops are now exported as \ \ , which usually is closer to + the expectations of the user (and is not ignored at the beginning of a par.) + +[w2x] Empty paragraphs are now (again!) exported with a + (otherwise they are ignored by the browser) + +[w2x] Xhtml specific options no longer starts with the prefix xhtml_ + (is still accepted for backwards compatibility) + +[all] Filter: Added display name and description to the extensions + +[w2x] BatchConverter: Localized alternative text for icons + +[w2b] Bugfix: Replace ";" with "and" also in in editor field + +[w2x] Added new options xslt_path to give the path to the XSL + transformation from w3c (also in the ui) + +[all] Filter: Optimized the size of the extensions by excluding code not + relevant for the format(s) supported by the extension + +[all] Separated LaTeX and xhtml code. As a consequence, it is possible to build + and run Writer2LaTeX with support for only one format + +[c2x] New options display_hiddens_rows_cols and display_filtered_rows_cols. If + set to false (default), rows and columns that are hidden or filtered are not + exported + +[all] Filter: Translated dialogs to Danish + +[all] Filter: Raised requirement in description.xml to OOo 2.2 + (needs support for localized dialogs) + +[w2l] Added ui option "Support additional symbols". This sets all the + options use_pifont, use_ifsym, use_wasysym, use_eurosym and use_tipa. + The pseudo-option additional_symbols can be used in the LockedOptions list + +[all] Bugfix: The class w2l.xmerge.DOMDocument did not throw IOException when + trying to read a non-xml document + +[w2x] Bugfix: The convert method did not restart document enumeration + (relevant if it's called several times for the same converter instance) + +[w2x] API: Created new API for batch conversion, which is also moved + from Application to a separate class + +[all] API: The API now uses a interface to the configuration rather than using + the class writer2latex.util.Config + +[w2x] Style of header/footer in index pages (batch conversion) aligned with + document header/footer + +[all] Added package descriptions to the javadoc + +[w2l] Always export non-breaking space as ~ even if inputenc supports it + directly - non-breaking spaces are usually not easy to identify in text editors + +---------- version 0.9.1 ----------- + +[w2x] New option: xhtml_add_bom (default false), if set to true + a BOM will be added at the start of generated xhtml files + +[w2x] Fixed typo in filter name: XHTML 1.0 + MathML 2.0 should be + XHTML 1.1 + MathML 2.0 + +[w2l] Added support for the latin language + +[w2l] Removed article.xml from w2l - it now belongs to Writer4LaTeX + +[all] The distribution now includes a sample configuration + package: xhtml-config-sample.oxt + +[all] Filter: Split the filter component into two extensions: + writer2latex.oxt for LaTeX and BibTeX, writer2xhtml.oxt for xhtml + The build file now produces optimized jars for each application, and the + standalone jar does not contain any uno code + +[w2l] Added (imcomplete) support for some of the Euclid fonts from Design Science + +[w2b] Replace ";" with "and" in author field (multiple authors) + +[w2l] Added support for formulas inserted by OOoLaTeX + +[w2x] Now adds mini-toc to the element with id "panel" if it exists in the xhtml + template + +[w2l] API: Added new api for standalone StarMath converter + Writer2LaTeX api: writer2latex.api.StarMathConverter + Uno service: org.openoffice.da.writer2latex.StarMathConverter + +[all] Filter: Added dialogs to all filters (w2l, w2x and c2x) + +[w2l] Vector graphics (except wmf) is now exported as pdf if backend is pdftex + +[all] API: In the GraphicConverter interface, the method + supportsFormat has been replaced with the method supportsConversion + +[w2l] Added support for *numbered* display equations: If paragraph + contains exactly one equation and exactly one sequence field (and otherwise + only whitespace and brackets), it treated as a numbered equation + +[w2l] I18n: If several characters in a row are converted using math mode, they + are now combined (ie. ${\alpha}{\beta}$ rather than ${\alpha}$${\beta}$) + +[all] Extended FilterData to support the property AutoCreate. If set to true, + the configuration file will be created if it does not exist + +[w2l] Even if formatting=IGNORE_ALL, we should still obey character style maps + from the configuration + +[w2x] Added german translation of navigation pane (w2l.util.L10n) + +[all] Bugfix: A paragraph with a bibliographic reference as the only + content was considered empty (and the reference was lost) + +[w2l] Use url decoding when creating file names for linked sections + (in order to obtain more readable file names) + +[w2l] Avoid redundant \mathit in StarMathConverter in some rare + cases (such as greek letters pasted into Math) + +[w2l] Support formulas for sequence fields (only the most common + cases oow:Name, oow:Name+Number, oow:Name-Number and oow:Number) + +[c2x] New options xhtml_display_hidden_sheets, xhtml_use_title_as_heading and + xhtml_use_sheet_names_as_headings + +[o2x] New option xhtml_notes. If true notes (annotations) are exported to xhtml + comments, if false they are ignored + +[c2x] Convert notes (annotations) + +[w2l] Heading formatting: The prefix and suffix to the counter should not be + included in \thesection, but rather in the formatting. This means that the + prefix and suffix no longer will be exported if formatting=CONVERT_BASIC + +[w2l] Added support for chapter references to reference marks and bookmarks + (if the marks are contained in a heading) + +[w2l] New option external_bibtex_files gives a list of BibTeX files. If nonempty, + these files will be used instead of converting the actual bibliography marks + +[w2l] New option metadata. If true, export metadata as \author, + \date and pdf metadata, as well as pdf metadata (if use_hyperref is true) + +[all] Filters/API: Changed filternames to the more obvious + org.openoffice.da.writer2latex etc. + +[w2l] Updated clean.xml and ultraclean.xml. Both configurations + now exports predefined Writer styles (like article.xml) + +[w2x] New option: xhtml_repeat_levels defines the number of heading + levels to repeat when splitting a file (0 means no levels are repeated) + +[w2x] Allow xhtml templates without header and footer elements + +[w2x] Bugfix (w2l.office.ListCounter): OpenDocument does not use + numberings like 1.0.1 if a list level is missing, but rather 1.1.1 + +[all] Command line application now uses internal standard configurations + (*ultraclean.xml etc.) + +[all] Command line application now accepts several configuration + files (by using the -config option more than once) + +[w2x] Use text style for label when applying numbering to heading + +[w2x] Added support for other list styes applied to headings + +[w2x] Added support for text:restart-numbering, text:is-list-header on headings + +[w2x] No longer add hr and [] to links in navigation (the template and style + sheet should take care of this) + +[all] New filter configuration concept: Configuration packages containing an OOo + template, a w2l configuration and registry information to bind these together: + When a configuration package is installed in OOo, the configuration is added + to the filter ui, and is preselected for documents based on the OOo template + +[all] Repackaging: Distribution files are included in source, build + file is extended to create the distribution; source distribution is separated + +[all] Changed to new extension format (.oxt) + +[all] Filter: Added messagebox to display the error in case an + exception happens during conversion + +[all] Moved default configurations to jar; the FilterData property + can refer to these using the special url *filename.xml + +[all] The filter now reads the FilterData property, which can be + used to specify options, configuration fil (url) and xhtml template (url) \ No newline at end of file diff --git a/source/distro/doc/user-manual.odt b/source/distro/doc/user-manual.odt new file mode 100644 index 0000000000000000000000000000000000000000..2efa35c1f4c627076118ac2bf06e86daa6625664 GIT binary patch literal 215915 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 O`*2^2QUMu$_kRF{3;Im} literal 0 HcmV?d00001 diff --git a/source/distro/doc/w2llogo.png b/source/distro/doc/w2llogo.png new file mode 100644 index 0000000000000000000000000000000000000000..990bc7c1d6be919e2b764fb669152c32b50f61b2 GIT binary patch literal 5608 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 CuGK#P literal 0 HcmV?d00001 diff --git a/source/distro/latex/obsolete/writer.sty b/source/distro/latex/obsolete/writer.sty new file mode 100644 index 0000000..ed8e7cd --- /dev/null +++ b/source/distro/latex/obsolete/writer.sty @@ -0,0 +1,83 @@ +%% File: ooomath.sty, Copyright (C) 2002-2004 Henrik Just +%% +%% This file may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.2 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.2 or later is part of all distributions of LaTeX +%% version 1999/12/01 or later. +%% +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{ooomath}[2004/08/08 v1.0 - stylefile used by Writer2LaTeX] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Some math mode macros defining additional symbols +% \ddotsup is taken from "The comprehensive LaTeX symbol list" +\newcommand\defeq{\stackrel{\mathrm{def}}{=}} +\newcommand\lambdabar{\mathchar'26\mkern-10mu\lambda} +\newcommand\ddotsup{\mathinner{\mkern1mu\raise\p@\vbox{\kern7\p@\hbox{.}}\mkern2mu\raise4\p@\hbox{.}\mkern2mu\raise7\p@ +\hbox{.}\mkern1mu}} +% Fallback definitions for math mode macros defined in other packages +\providecommand\multimapdotbothA{\bullet\kern-0.4em-\kern-0.4em\circ} +\providecommand\multimapdotbothB{\circ\kern-0.4em-\kern-0.4em\bullet} +\providecommand\oiint{\oint} % should be area integral... +\providecommand\oiiint{\oint} % should be volume integral... +\providecommand\llbracket{[} % should be double... +\providecommand\rrbracket{]} % should be double... +% More math mode macros +\newcommand\wideslash[2]{{}^{#1}/_{#2}} % too small +\newcommand\widebslash[2]{{}_{#1}\backslash^{#2}} % too small +\newcommand\normalsubformula[1]{\text{$#1$}} +\newcommand\boldsubformula[1]{\text{\mathversion{bold}$#1$}} +% Multiscripts, based on leftidx.sty by Harald Harders +\newlength{\idxmathdepth} +\newlength{\idxmathtotal} +\newlength{\idxmathwidth} +\newlength{\idxraiseme} +% +\newcommand{\idxdheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\displaystyle#1\)}% +\protect\settodepth{\idxmathdepth}{\(\displaystyle#1\)}% +\protect\settowidth{\idxmathwidth}{\(\displaystyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +\newcommand{\idxtheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\textstyle #1\)}% +\protect\settodepth{\idxmathdepth}{\(\textstyle #1\)}% +\protect\settowidth{\idxmathwidth}{\(\textstyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +\newcommand{\idxsheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\scriptstyle #1\)}% +\protect\settodepth{\idxmathdepth}{\(\scriptstyle #1\)}% +\protect\settowidth{\idxmathwidth}{\(\scriptstyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +\newcommand{\idxssheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\scriptscriptstyle #1\)}% +\protect\settodepth{\idxmathdepth}{\(\scriptscriptstyle #1\)}% +\protect\settowidth{\idxmathwidth}{\(\scriptscriptstyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +% \multiscripts{<prescripts>}{<under>}{<over>}{<formula>}{<scripts>} +\newcommand\multiscripts[5]{% +\mathchoice% +{\idxdheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5}% +{\idxtheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5}% +{\idxsheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5}% +{\idxssheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5} +}% +\newcommand\mathoverstrike[1]{% +\mathchoice% +{\idxdheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +{\idxtheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +{\idxsheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +{\idxssheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +} +\endinput + diff --git a/source/distro/latex/ooomath.sty b/source/distro/latex/ooomath.sty new file mode 100644 index 0000000..9944903 --- /dev/null +++ b/source/distro/latex/ooomath.sty @@ -0,0 +1,83 @@ +%% File: ooomath.sty, Copyright (C) 2002-2004 Henrik Just +%% +%% This file may be distributed and/or modified under the +%% conditions of the LaTeX Project Public License, either version 1.2 +%% of this license or (at your option) any later version. +%% The latest version of this license is in +%% http://www.latex-project.org/lppl.txt +%% and version 1.2 or later is part of all distributions of LaTeX +%% version 1999/12/01 or later. +%% +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{ooomath}[2004/12/27 v1.01 - stylefile used by Writer2LaTeX] + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Some math mode macros defining additional symbols +% \ddotsup is taken from "The comprehensive LaTeX symbol list" +\newcommand\defeq{\stackrel{\mathrm{def}}{=}} +\newcommand\lambdabar{\mathchar'26\mkern-10mu\lambda} +\newcommand\ddotsup{\mathinner{\mkern1mu\raise\p@\vbox{\kern7\p@\hbox{.}}\mkern2mu\raise4\p@\hbox{.}\mkern2mu\raise7\p@ +\hbox{.}\mkern1mu}} +% Fallback definitions for math mode macros defined in other packages +\providecommand\multimapdotbothA{\bullet\kern-0.4em-\kern-0.4em\circ} +\providecommand\multimapdotbothB{\circ\kern-0.4em-\kern-0.4em\bullet} +\providecommand\oiint{\oint} % should be area integral... +\providecommand\oiiint{\oint} % should be volume integral... +\providecommand\llbracket{[} % should be double... +\providecommand\rrbracket{]} % should be double... +% More math mode macros +\newcommand\wideslash[2]{{}^{#1}/_{#2}} % too small +\newcommand\widebslash[2]{{}_{#1}\backslash^{#2}} % too small +\newcommand\normalsubformula[1]{\text{\mathversion{normal}$#1$}} +\newcommand\boldsubformula[1]{\text{\mathversion{bold}$#1$}} +% Multiscripts, based on leftidx.sty by Harald Harders +\newlength{\idxmathdepth} +\newlength{\idxmathtotal} +\newlength{\idxmathwidth} +\newlength{\idxraiseme} +% +\newcommand{\idxdheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\displaystyle#1\)}% +\protect\settodepth{\idxmathdepth}{\(\displaystyle#1\)}% +\protect\settowidth{\idxmathwidth}{\(\displaystyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +\newcommand{\idxtheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\textstyle #1\)}% +\protect\settodepth{\idxmathdepth}{\(\textstyle #1\)}% +\protect\settowidth{\idxmathwidth}{\(\textstyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +\newcommand{\idxsheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\scriptstyle #1\)}% +\protect\settodepth{\idxmathdepth}{\(\scriptstyle #1\)}% +\protect\settowidth{\idxmathwidth}{\(\scriptstyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +\newcommand{\idxssheight}[1]{% +\protect\settoheight{\idxmathtotal}{\(\scriptscriptstyle #1\)}% +\protect\settodepth{\idxmathdepth}{\(\scriptscriptstyle #1\)}% +\protect\settowidth{\idxmathwidth}{\(\scriptscriptstyle#1\)}% +\protect\addtolength{\idxmathtotal}{\idxmathdepth}% +\protect\setlength{\idxraiseme}{\idxmathtotal/2-\idxmathdepth}} +% \multiscripts{<prescripts>}{<under>}{<over>}{<formula>}{<scripts>} +\newcommand\multiscripts[5]{% +\mathchoice% +{\idxdheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5}% +{\idxtheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5}% +{\idxsheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5}% +{\idxssheight{#4}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#1\underset{#2} +{\overset{#3}{#4}}\rule[-\idxmathdepth]{0mm}{\idxmathtotal}#5} +}% +\newcommand\mathoverstrike[1]{% +\mathchoice% +{\idxdheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +{\idxtheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +{\idxsheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +{\idxssheight{#1}\rlap{\rule[\idxraiseme]{\idxmathwidth}{0.4pt}}{#1}}% +} +\endinput + diff --git a/source/distro/samples/config/google-docs-config.xml b/source/distro/samples/config/google-docs-config.xml new file mode 100644 index 0000000..f509a70 --- /dev/null +++ b/source/distro/samples/config/google-docs-config.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- gdocs.xml + This is a sample configuration file for Writer2LaTeX. + The options are set to produce the cleanest possible + LaTeX file from an Google Docs document. + The configuration is provided by Elefterios Stamatogiannakis + --> + + +<config> + <option name="documentclass" value="article" /> + <option name="backend" value="generic" /> + <option name="inputencoding" value="ascii" /> + <option name="multilingual" value="false" /> + <option name="use_ooomath" value="false" /> + <option name="use_color" value="false" /> + <option name="use_colortbl" value="false" /> + <option name="use_geometry" value="false" /> + <option name="use_fancyhdr" value="false" /> + <option name="use_hyperref" value="false" /> + <option name="use_caption" value="true" /> + <option name="use_endnotes" value="false" /> + <option name="use_bibtex" value="true" /> + <option name="bibtex_style" value="plain" /> + <option name="formatting" value="ignore_most" /> + <option name="page_formatting" value="ignore_all" /> + <option name="ignore_empty_paragraphs" value="true" /> + <option name="ignore_hard_page_breaks" value="true" /> + <option name="ignore_hard_line_breaks" value="true" /> + <option name="ignore_double_spaces" value="true" /> + <option name="use_supertabular" value="false" /> + <option name="simple_table_limit" value="10000" /> + <option name="wrap_lines_after" value="0" /> + <option name="debug" value="false" /> + <heading-map max-level="5"> + <heading-level-map writer-level="1" name="section" level="1" /> + <heading-level-map writer-level="2" name="subsection" level="2" /> + <heading-level-map writer-level="3" name="subsubsection" level="3" /> + <heading-level-map writer-level="4" name="paragraph" level="4" /> + <heading-level-map writer-level="5" name="subparagraph" level="5" /> + </heading-map> + <custom-preamble /> + + <!-- Style maps: These rules defines how styles in OOo are mapped to LaTeX code. + A number of predefined Writer styles are converted --> + + <!-- "Title" is mapped to \maketitle. If the user chooses to export meta data, + the author and date will be inserted automatically --> + <style-map name="Title" class="paragraph" before="\title{" after="} \maketitle" line-break="false" /> + + <!-- "Quotations" is mapped to a quotation environment --> + <style-map name="Quotations" family="paragraph-block" next="Quotations" before="\begin{quotation}" after="\end{quotation}" /> + <style-map name="Quotations" family="paragraph" before="" after="" /> + + <!-- Preformatted Text is mapped to a verbatim environment + Note the attribute verbatim, which instructs OOo to output the content + verbatim (characters not available in the inputencoding will be replaced + by question marks; other content will be lost). --> + <style-map name="Preformatted Text" family="paragraph-block" next="Preformatted Text" before="\begin{verbatim}" after="\end{verbatim}" /> + <style-map name="Preformatted Text" family="paragraph" before="" after="" verbatim="true" /> + + <!-- "Horizontal line" is mapped to a \hrule --> + <style-map name="Horizontal Line" family="paragraph" before="" after=" \begin{center}\hrule\end{center}" /> + + <!-- "Emphasis" text style is mapped to \emph --> + <style-map name="Emphasis" family="text" before="\emph{" after="}" /> + + <!-- Italics text style is mapped to \emph --> + <style-map class="text-attribute" name="italic" before="\emph{" after="}" /> + + <!-- Underlined text style is mapped to latex verbatim output --> + <style-map class="text-attribute" name="underline" before="" after="" verbatim="true"/> + + <!-- Superscript text style is mapped to \textsuperscript --> + <style-map class="text-attribute" name="superscript" before="\textsuperscript{" after="}" /> + + <!-- Subscript text style is mapped to clean latex --> + <style-map class="text-attribute" name="subscript" before="$_{\textrm{" after="}}$" /> + + <!-- "Strong Emphasis" text style is mapped to \textbf --> + <style-map name="Strong Emphasis" family="text" before="\textbf{" after="}"/> + + <!-- "Teletype" text style is mapped to \texttt --> + <style-map name="Teletype" family="text" before="\texttt{" after="}" /> + + <!-- "List Heading" and "List Contents" are mapped to a description environment --> + <style-map name="List Heading" family="paragraph-block" next="List Heading;List Contents" before="\begin{description}" after="\end{description}"/> + <style-map name="List Heading" family="paragraph" before="\item[" after="]" line-break="false" /> + <style-map name="List Contents" family="paragraph" before="" after="" /> + + <!-- Various characters are mapped to more human friendly latex entities--> + <string-replace input=""" latex-code=""" /> + <string-replace input="<" latex-code="<" /> + <string-replace input=">" latex-code=">" /> + <string-replace input="‘" latex-code="`" /> + <string-replace input="’" latex-code="'" /> + <string-replace input="‚" latex-code="'" /> + <string-replace input="“" latex-code="``" /> + <string-replace input="”" latex-code="''" /> + <string-replace input="„" latex-code=""" /> + <string-replace input="" latex-code=""" /> + <string-replace input="" latex-code="''" /> + <string-replace input="" latex-code="'" /> + <string-replace input="" latex-code="``" /> + <string-replace input="" latex-code="'" /> + <string-replace input="'" latex-code="'" /> + + <!-- Some mappings to help with citations. --> + <string-replace input="<<<" latex-code="\cite{" /> + <string-replace input=">>>" latex-code="}" /> + +</config> + diff --git a/source/distro/samples/xhtml/sample-xhtml.odt b/source/distro/samples/xhtml/sample-xhtml.odt new file mode 100644 index 0000000000000000000000000000000000000000..72a2be1af015d797dc4c45d6c4fe426450db3aa2 GIT binary patch literal 12597 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 BZiD~; literal 0 HcmV?d00001 diff --git a/source/distro/w2l b/source/distro/w2l new file mode 100644 index 0000000..43713be --- /dev/null +++ b/source/distro/w2l @@ -0,0 +1,22 @@ +# *Very* simple script to run Writer2LaTeX (unix version) +# Created by Henrik Just, october 2003 +# Modified december 2003 as suggested by Juan Julian Merelo Guervos +# Modified may 2004 as suggested by J. Wolfgang Kaltz +# Last modified december 2008 + +# If writer2latex.jar is not in the same directory as the script, please +# edit the following line to contain the full path to Writer2LaTeX: +W2LPATH=`dirname $0` + +# If the required JVM is not in your path, or the path is not set by JAVA_HOME, +# please edit the following line to contain the full path and file name +MYJAVAEXE="java" + +if [ "$JAVA_HOME" = "" ] +then +JAVAEXE="$MYJAVAEXE" +else +JAVAEXE="$JAVA_HOME/bin/java" +fi + +$JAVAEXE -jar "$W2LPATH/writer2latex.jar" "$@" \ No newline at end of file diff --git a/source/distro/w2l.bat b/source/distro/w2l.bat new file mode 100644 index 0000000..9bd4355 --- /dev/null +++ b/source/distro/w2l.bat @@ -0,0 +1,19 @@ +@echo off +rem *Very* simple batch file to run Writer2LaTeX +rem Created by Henrik Just, october 2003 +rem Modfied december 2003 as suggested by Juan Julian Merelo Guervos +rem Last modified july 2007 + +rem Please edit the following line to contain the full path to Writer2LaTeX: + +set W2LPATH="c:\writer2latex09" + +rem If the Java executable is not in your path, please edit the following +rem line to contain the full path and file name + +set JAVAEXE="java" + +%JAVAEXE% -jar %W2LPATH%\writer2latex.jar %1 %2 %3 %4 %5 %6 %7 %8 %9 + +set W2LPATH= +set JAVAEXE= diff --git a/source/distro/xslt/pmathml.xsl b/source/distro/xslt/pmathml.xsl new file mode 100644 index 0000000..97012f1 --- /dev/null +++ b/source/distro/xslt/pmathml.xsl @@ -0,0 +1,612 @@ +<xsl:stylesheet + version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:mml="http://www.w3.org/1998/Math/MathML" + xmlns:h="http://www.w3.org/1999/xhtml" + xmlns="http://www.w3.org/1999/xhtml" + xmlns:msxsl="urn:schemas-microsoft-com:xslt" + xmlns:fns="http://www.w3.org/2002/Math/preference" + xmlns:doc="http://www.dcarlisle.demon.co.uk/xsldoc" + xmlns:ie5="http://www.w3.org/TR/WD-xsl" + exclude-result-prefixes="h ie5 fns msxsl fns doc" + extension-element-prefixes="msxsl fns doc" +> + +<!-- +$Id: pmathml.xsl,v 1.8 2003/06/23 14:46:44 davidc Exp $ + +Copyright David Carlisle 2001, 2002. + +Use and distribution of this code are permitted under the terms of the <a +href="http://www.w3.org/Consortium/Legal/copyright-software-19980720" +>W3C Software Notice and License</a>. +--> + +<!-- MathPlayer mpdialog code for contributed by + Jack Dignan and Robert Miner, both of Design Science. +--> + +<xsl:output method="xml" omit-xml-declaration="yes" /> + +<ie5:if doc:id="iehack" test="."> + <ie5:eval no-entities="t">'<!--'</ie5:eval> +</ie5:if> + + +<fns:x name="mathplayer" o="MathPlayer.Factory.1"> +<object id="mmlFactory" + classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987"> +</object> +<?import namespace="mml" implementation="#mmlFactory"?> +</fns:x> + +<fns:x name="techexplorer" o="techexplorer.AxTchExpCtrl.1"> +<object id="mmlFactory" classid="clsid:0E76D59A-C088-11D4-9920-002035EFB1A4"> +</object> +<?import namespace="mml" implementation="#mmlFactory"?> +</fns:x> + + +<!-- SCRIPT not script due to weird mozilla bug +http://bugzilla.mozilla.org/show_bug.cgi?id=158457 +--> + +<fns:x name="css" o="Microsoft.FreeThreadedXMLDOM"> +<SCRIPT for="window" event="onload"> +var xsl = new ActiveXObject("Microsoft.FreeThreadedXMLDOM"); +xsl.async = false; +xsl.validateOnParse = false; +xsl.load("pmathmlcss.xsl"); +var xslTemplate = new ActiveXObject("MSXML2.XSLTemplate.3.0"); +xslTemplate.stylesheet=xsl.documentElement; +var xslProc = xslTemplate.createProcessor(); +xslProc.input = document.XMLDocument; +xslProc.transform(); +var str = xslProc.output; +<!-- work around bug in IE6 under Win XP, RM 6/5/2002 --> +var repl = "replace"; +if (window.navigator.appVersion.match(/Windows NT 5.1/)) { repl = ""; } +var newDoc = document.open("text/html", repl); +newDoc.write(str); +</SCRIPT> +</fns:x> + + +<h:p> +in mpdialog mode, we just write out some JavaScript to display +dialog to the reader asking whether they want to install MathPlayer +Depending on the response we get, we then instantiate an XSL processor +and reprocess the doc, passing $secondpass according to the +reader response. +</h:p> +<h:p>Using d-o-e is fairly horrible, but this code is only for IE +anyway, and we need to force HTML semantics in this case.</h:p> + +<xsl:variable name="mpdialog"> +var cookieName = "MathPlayerInstall="; +function MPInstall(){ + var showDialog=true; + var c = document.cookie; + var i = c.indexOf(cookieName); + if (i >= 0) { + if ( c.substr(i + cookieName.length, 1) >= 2) { showDialog=false; } + } + if (showDialog) { + MPDialog(); + c = document.cookie; + i = c.indexOf(cookieName); + } + if (i >= 0) return c.substr(i + cookieName.length, 1); + else return null; +} + +function MPDialog() { + var vArgs=""; + var sFeatures="dialogWidth:410px;dialogHeight:190px;help:off;status:no"; + var text = ""; + text += "javascript:document.write('" + text += '<script>' + text += 'function fnClose(v) { ' + text += 'var exp = new Date();' + text += 'var thirtyDays = exp.getTime() + (30 * 24 * 60 * 60 * 1000);' + text += 'exp.setTime(thirtyDays);' + text += 'var cookieProps = ";expires=" + exp.toGMTString();' + text += 'if (document.forms[0].dontask.checked) v+=2;' + text += 'document.cookie="' + cookieName + '"+v+cookieProps;' + text += 'window.close();' + text += '}' + text += '</' + 'script>' + text += '<head><title>Install MathPlayer?</title></head>' + text += '<body bgcolor="#D4D0C8"><form>' + text += '<table cellpadding=10 style="font-family:Arial;font-size:10pt" border=0 width=100%>' + text += '<tr><td align=left>This page requires Design Science\\\'s MathPlayer&trade;.<br>' + text += 'Do you want to download and install MathPlayer?</td></tr>'; + text += '<tr><td align=center><input type="checkbox" name="dontask">' + text += 'Don\\\'t ask me again</td></tr>' + text += '<tr><td align=center><input id=yes type="button" value=" Yes "' + text += ' onClick="fnClose(1)">&nbsp;&nbsp;&nbsp;' + text += '<input type="button" value=" No " onClick="fnClose(0)"></td></tr>' + text += '</table></form>'; + text += '</body>' + text += "')" + window.showModalDialog( text , vArgs, sFeatures ); +} + +function WaitDialog() { + var vArgs=""; + var sFeatures="dialogWidth:510px;dialogHeight:150px;help:off;status:no"; + var text = ""; + text += "javascript:document.write('" + text += '<script>' + text += 'window.onload=fnLoad;' + text += 'function fnLoad() {document.forms[0].yes.focus();}' + text += 'function fnClose(v) { ' + text += 'window.returnValue=v;' + text += 'window.close();' + text += '}' + text += '</' + 'script>' + text += '<head><title>Wait for Installation?</title></head>' + text += '<body bgcolor="#D4D0C8" onload="fnLoad()"><form><' + text += 'table cellpadding=10 style="font-family:Arial;font-size:10pt" border=0 width=100%>' + text += '<tr><td align=left>Click OK once MathPlayer is installed ' + text += 'to refresh the page.<br>' + text += 'Click Cancel to view the page immediately without MathPlayer.</td></tr>'; + text += '<tr><td align=center><input id=yes type="button" ' + text += 'value=" OK " onClick="fnClose(1)">&nbsp;&nbsp;&nbsp;' + text += '<input type="button" value="Cancel" onClick="fnClose(0)"></td></tr>' + text += '</table></form>'; + text += '</body>' + text += "')" + return window.showModalDialog( text , vArgs, sFeatures ); +} + +var result = MPInstall(); + +var action = "fallthrough"; +if (result == 1 || result == 3) { + window.open("http://www.dessci.com/webmath/mathplayer"); + var wait = WaitDialog(); + if ( wait == 1) { + action = "install"; + document.location.reload(); + + } +} +if (action == "fallthrough") { +var xsl = new ActiveXObject("Microsoft.FreeThreadedXMLDOM"); +xsl.async = false; +xsl.validateOnParse = false; +xsl.load("pmathmlcss.xsl"); +var xslTemplate = new ActiveXObject("MSXML2.XSLTemplate.3.0"); +xslTemplate.stylesheet=xsl.documentElement; +var xslProc = xslTemplate.createProcessor(); +xslProc.input = document.XMLDocument; + +xslProc.transform(); +var str = xslProc.output; +<!-- work around bug in IE6 under Win XP, RM 6/5/2002 --> +var repl = "replace"; +if (window.navigator.appVersion.match(/Windows NT 5.1/)) { repl = ""; } +var newDoc = document.open("text/html", repl); +newDoc.write(str); +document.close(); +} +</xsl:variable> + +<fns:x name="mathplayer-dl" >mathplayer-dl</fns:x> + +<fns:x name="techexplorer-plugin" >techexplorer-plugin</fns:x> + +<xsl:variable name="root" select="/"/> + + + +<xsl:param name="activex"> + <xsl:choose> + <xsl:when test="/*/@fns:renderer='techexplorer-plugin'">techexplorer-plugin</xsl:when> + <xsl:when test="system-property('xsl:vendor')!='Microsoft'"/> + <xsl:otherwise> +<xsl:variable name="docpref" select="document('')/*/fns:x[@name=$root/*/@fns:renderer][1]"/> + <xsl:choose> + <xsl:when test="$docpref='mathplayer-dl'">mathplayer-dl</xsl:when> + <xsl:when test="$docpref and fns:isinstalled(string($docpref/@o))='true'"> + <xsl:copy-of select="$docpref/node()"/> + </xsl:when> + <xsl:otherwise> + <xsl:copy-of select="(document('')/*/fns:x[fns:isinstalled(string(@o))='true'])[1]/node()"/> + </xsl:otherwise> + </xsl:choose> + </xsl:otherwise> + </xsl:choose> +</xsl:param> + +<h:div doc:ref="iehack"> +<h:h3>IE5 hacks</h:h3> +<h:p>This code will be ignored by an XSLT engine as a top level +element in a foreign namespace. It will be executed by an IE5XSL +engine and insert <!-- into the output stream, ie the start of a +comment. This will comment out all the XSLT code which will be copied +to the output. A similar clause below will close this comment, it is +then followed by the IE5XSL templates to be executed.</h:p> +<h:p>This trick is due to Jonathan Marsh of Microsoft, and used in +<h:a href="http://www.w3.org/TR/2001/WD-query-datamodel-20010607/xmlspec-ie-dm.xsl">the stylesheet for +the XPath 2 data model draft</h:a>.</h:p> +</h:div> + +<h:h2>XSLT stylesheet</h:h2> +<h:h3>MSXSL script block</h:h3> + +<h:p>The following script block implements an extension function that +tests whether a specified ActiveX component is known to the client. +This is used below to test for the existence of MathML rendering +components.</h:p> +<msxsl:script language="JScript" implements-prefix="fns"> + function isinstalled(ax) + { + try { + var ActiveX = new ActiveXObject(ax); + return "true"; + } catch (e) { + return "false"; + } +} +</msxsl:script> + +<h:p>The main bulk of this stylesheet is an identity transformation so...</h:p> +<xsl:template match="*|comment()"> +<xsl:copy> +<xsl:copy-of select="@*"/> +<xsl:apply-templates/> +</xsl:copy> +</xsl:template> + + + +<h:p>XHTML elements are copied sans prefix (XHTML is default namespace +here, so these elements will still be in XHTML namespace</h:p> +<xsl:template match="h:*"> +<xsl:element name="{local-name(.)}"> + <xsl:copy-of select="@*"/> +<xsl:apply-templates/> +</xsl:element> +</xsl:template> + +<h:p>IE's treatment of XHTML as HTML needs a little help here...</h:p> +<xsl:template match="h:br|h:hr"> +<xsl:choose> +<xsl:when test="system-property('xsl:vendor')='Microsoft'"> + <xsl:value-of disable-output-escaping="yes" select="concat('<',local-name(.))"/> + <xsl:apply-templates mode="verb" select="@*"/> + <xsl:text disable-output-escaping="yes">></xsl:text> +</xsl:when> +<xsl:otherwise> +<xsl:element name="{local-name(.)}"> + <xsl:copy-of select="@*"/> +<xsl:apply-templates/> +</xsl:element> +</xsl:otherwise> +</xsl:choose> +</xsl:template> + +<h:p>This just ensures the mathml prefix declaration isn't copied from +the source at this stage, so that the system will use the mml prefix +coming from this stylesheet</h:p> +<xsl:template match="h:html|html"> +<html> +<xsl:copy-of select="@*[not(namespace-uri(.)='http://www.w3.org/2002/Math/preference')]"/> +<xsl:apply-templates/> +</html> +</xsl:template> + +<h:p>We modify the head element to add code to specify a Microsoft +"Behaviour" if the behaviour component is known to the system.</h:p> +<h:span doc:ref="mp">Test for MathPlayer (Design Science)</h:span> +<h:span doc:ref="te">Test for Techexplorer (IBM)</h:span> +<h:span doc:ref="ms"><h:div>Test for Microsoft. In this case we just +output a small HTML file that executes a script that will re-process +the source docuument with a different stylesheet. Doing things this +way avoids the need to xsl:import the second stylesheet, which would +very much increase the processing overhead of running this +stylesheet.</h:div></h:span> +<h:span doc:ref="other">Further tests (eg for netscape/mozilla) could +be added here if necessary</h:span> +<xsl:template match="h:head|head"> +<head> + +<!-- new if for IE frames bug --> +<xsl:if test="system-property('xsl:vendor')='Microsoft'"> +<xsl:if test="name(msxsl:node-set($activex)/*)=''"> +<object id="mmlFactory" + classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987"> +</object> +<xsl:processing-instruction name="import"> + namespace="mml" implementation="#mmlFactory" +</xsl:processing-instruction> +</xsl:if> +</xsl:if> + +<xsl:choose> +<xsl:when doc:id="mp" test="$activex='mathplayer-dl'"> + <xsl:if test="fns:isinstalled('MathPlayer.Factory.1')='false'"> + <SCRIPT for="window" event="onload"> + <xsl:value-of select="$mpdialog" disable-output-escaping="yes"/> + </SCRIPT> + </xsl:if> + <xsl:copy-of select="document('')/*/fns:x[@name='mathplayer']"/> +</xsl:when> +<xsl:when doc:id="mp" test="not($activex='techexplorer-plugin') and system-property('xsl:vendor')='Microsoft'"> + <xsl:copy-of select="$activex"/> +</xsl:when> +<xsl:otherwise doc:id="other"> +</xsl:otherwise> +</xsl:choose> + <xsl:apply-templates/> +</head> +</xsl:template> + + +<xsl:template match="mml:math" priority="22"> +<xsl:choose> +<xsl:when test="$activex='techexplorer-plugin'"> +<embed type="text/mathml" height="75" width="300"> +<xsl:attribute name="mmldata"> +<xsl:apply-templates mode="verb" select="."/> +</xsl:attribute> +</embed> +</xsl:when> +<xsl:otherwise> +<xsl:element name="mml:{local-name(.)}"> + <xsl:copy-of select="@*"/> +<xsl:apply-templates/> +</xsl:element> +</xsl:otherwise> +</xsl:choose> +</xsl:template> + + +<!-- squash annotation elements --> + + + +<h:p>Somewhat bizarrely in an otherwise namespace aware system, +Microsoft behaviours are defined to trigger off the +<h:em>prefix</h:em> not the <h:em>Namespace</h:em>. In the code above +we associated a MathML rendering behaviour (if one was found) with the +prefix <h:code>mml:</h:code> so here we ensure that this is the prefix +that actually gets used in the output.</h:p> +<xsl:template match="mml:*"> +<xsl:element name="mml:{local-name(.)}"> + <xsl:copy-of select="@*"/> +<xsl:apply-templates/> +</xsl:element> +</xsl:template> + +<h:p>Copy semantics element through in IE (so mathplayer gets to see +mathplayer annotations, otherwise use first child or a presentation annotation.</h:p> +<xsl:template match="mml:semantics"> +<xsl:choose> + <xsl:when test="system-property('xsl:vendor')='Microsoft'"> + <xsl:element name="mml:{local-name(.)}"> + <xsl:copy-of select="@*"/> + <xsl:apply-templates/> + </xsl:element> + </xsl:when> + <xsl:when test="mml:annotation-xml[@encoding='MathML-Presentation']"> + <xsl:apply-templates select="mml:annotation-xml[@encoding='MathML-Presentation']/node()"/> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates select="*[1]"/> + </xsl:otherwise> +</xsl:choose> +</xsl:template> + +<!-- a version of my old verb.xsl --> + +<!-- non empty elements and other nodes. --> +<xsl:template mode="verb" match="*[*]|*[text()]|*[comment()]|*[processing-instruction()]"> + <xsl:value-of select="concat('<',local-name(.))"/> + <xsl:apply-templates mode="verb" select="@*"/> + <xsl:text>></xsl:text> + <xsl:apply-templates mode="verb"/> + <xsl:value-of select="concat('</',local-name(.),'>')"/> +</xsl:template> + +<!-- empty elements --> +<xsl:template mode="verb" match="*"> + <xsl:value-of select="concat('<',local-name(.))"/> + <xsl:apply-templates mode="verb" select="@*"/> + <xsl:text>/></xsl:text> +</xsl:template> + +<!-- attributes + Output always surrounds attribute value by " + so we need to make sure no literal " appear in the value --> +<xsl:template mode="verb" match="@*"> + <xsl:value-of select="concat(' ',local-name(.),'=')"/> + <xsl:text>"</xsl:text> + <xsl:call-template name="string-replace"> + <xsl:with-param name="from" select="'"'"/> + <xsl:with-param name="to" select="'&quot;'"/> + <xsl:with-param name="string" select="."/> + </xsl:call-template> + <xsl:text>"</xsl:text> +</xsl:template> + +<!-- pis --> +<xsl:template mode="verb" match="processing-instruction()"/> + +<!-- only works if parser passes on comment nodes --> +<xsl:template mode="verb" match="comment()"/> + + +<!-- text elements + need to replace & and < by entity references--> +<xsl:template mode="verb" match="text()"> + <a name="{generate-id(.)}"/> + <xsl:call-template name="string-replace"> + <xsl:with-param name="to" select="'&gt;'"/> + <xsl:with-param name="from" select="'>'"/> + <xsl:with-param name="string"> + <xsl:call-template name="string-replace"> + <xsl:with-param name="to" select="'&lt;'"/> + <xsl:with-param name="from" select="'<'"/> + <xsl:with-param name="string"> + <xsl:call-template name="string-replace"> + <xsl:with-param name="to" select="'&amp;'"/> + <xsl:with-param name="from" select="'&'"/> + <xsl:with-param name="string" select="."/> + </xsl:call-template> + </xsl:with-param> + </xsl:call-template> + </xsl:with-param> + </xsl:call-template> +</xsl:template> + + +<!-- end verb mode --> + +<!-- replace all occurences of the character(s) `from' + by the string `to' in the string `string'.--> +<xsl:template name="string-replace" > + <xsl:param name="string"/> + <xsl:param name="from"/> + <xsl:param name="to"/> + <xsl:choose> + <xsl:when test="contains($string,$from)"> + <xsl:value-of select="substring-before($string,$from)"/> + <xsl:value-of select="$to"/> + <xsl:call-template name="string-replace"> + <xsl:with-param name="string" select="substring-after($string,$from)"/> + <xsl:with-param name="from" select="$from"/> + <xsl:with-param name="to" select="$to"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$string"/> + </xsl:otherwise> + </xsl:choose> +</xsl:template> + + +<!-- end of verb.xsl --> + + + +<h:h2>IE5XSL stylesheet</h:h2> +<h:p>In a rare fit of sympathy for users of +<h:em>the-language-known-as-XSL-in-IE5</h:em> this file incorporates a +version of the above code designed to work in the Microsoft dialect. +This is needed otherwise users of a MathML rendering behaviour would +have to make a choice whether they wanted to use this stylesheet +(keeping their source documents conforming XHTML+MathML) or to use +the explicit Microsoft Object code, which is less portable, but would +work in at least IE5.5.</h:p> + +<h:p>This entire section of code, down to the end of the stylesheet is +contained within this ie5:if. Thus XSLT sees it as a top level element +from a foreign namespace and silently ignores it. IE5XSL sees it as +"if true" and so executes the code.</h:p> + + +<h:p doc:ref="closecomment">First close the comment started at the beginning. This ensures +that the bulk of the XSLT code, while being copied to the result tree +by the IE5XSL engine, will not be rendered in the browser.</h:p> + +<h:span doc:ref="eval">Lacking attribute value templates in +xsl:element, and the local-name() function, we resort to constructing +the start and end tags in strings in javascript, then using +no-entities attribute which is the IE5XSL equivalent of disable-output-encoding</h:span> +<ie5:if test="."> + +<ie5:eval doc:id="closecomment" no-entities="t">'-->'</ie5:eval> + +<ie5:apply-templates select="."> + + +<ie5:script> + function mpisinstalled() + { + try { + var ActiveX = new ActiveXObject("MathPlayer.Factory.1"); + return "true"; + } catch (e) { + return "false"; + } +} +</ie5:script> + +<ie5:template match="/"> +<ie5:apply-templates/> +</ie5:template> + +<ie5:template match="head|h:head"/> + +<ie5:template match="text()"> +<ie5:value-of select="."/> +</ie5:template> + +<ie5:template match="*|@*"> +<ie5:copy> +<ie5:apply-templates select="*|text()|@*"/> +</ie5:copy> +</ie5:template> + + +<ie5:template match="mml:*"> +<ie5:eval no-entities="t" doc:id="eval">'<mml:' + this.nodeName.substring(this.nodeName.indexOf(":")+1)</ie5:eval> +<ie5:for-each select="@*"> +<ie5:eval no-entities="t">' ' + this.nodeName</ie5:eval>="<ie5:value-of select="."/>" +</ie5:for-each> +<ie5:eval no-entities="t">'>'</ie5:eval> +<ie5:apply-templates select="*|text()"/> +<ie5:eval no-entities="t">'</mml:' + this.nodeName.substring(this.nodeName.indexOf(":")+1) + '>'</ie5:eval> +</ie5:template> + + +<ie5:template match="mml:math"> +<ie5:if expr="mpisinstalled()=='false'"> +<embed type="text/mathml" height="75" width="300"> +<ie5:attribute name="mmldata"> +<ie5:eval doc:id="eval" no-entities="t">'<math>'</ie5:eval> +<ie5:apply-templates/> +<ie5:eval doc:id="eval" no-entities="t">'</math>'</ie5:eval> +</ie5:attribute> +</embed> +</ie5:if> +<ie5:if expr="mpisinstalled()=='true'"> +<ie5:eval doc:id="eval" no-entities="t">'<mml:' + this.nodeName.substring(this.nodeName.indexOf(":")+1)</ie5:eval> +<ie5:for-each select="@*"> +<ie5:eval no-entities="t">' ' + this.nodeName</ie5:eval>="<ie5:value-of select="."/>" +</ie5:for-each> +<ie5:eval no-entities="t">'>'</ie5:eval> +<ie5:apply-templates select="*|text()"/> +<ie5:eval no-entities="t">'</mml:' + this.nodeName.substring(this.nodeName.indexOf(":")+1) + '>'</ie5:eval> +</ie5:if> +</ie5:template> + +<ie5:template match="html|h:html"> +<html xmlns:mml="http://www.w3.org/1998/Math/MathML"> +<head> +<ie5:if expr="mpisinstalled()=='true'"> +<object id="mmlFactory" + classid="clsid:32F66A20-7614-11D4-BD11-00104BD3F987"> +</object> +<ie5:pi name="IMPORT"> + namespace="mml" implementation="#mmlFactory" +</ie5:pi> +</ie5:if> +<ie5:apply-templates select="h:head/*|head/*"/> +</head> +<body> +<ie5:apply-templates select="body|h:body"/> +</body> +</html> +</ie5:template> + +</ie5:apply-templates> + + +</ie5:if> + + +</xsl:stylesheet> diff --git a/source/distro/xslt/pmathmlcss.xsl b/source/distro/xslt/pmathmlcss.xsl new file mode 100644 index 0000000..27e518e --- /dev/null +++ b/source/distro/xslt/pmathmlcss.xsl @@ -0,0 +1,872 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- +Presentation MathML Stylesheet +--> + +<!-- +$Id: pmathmlcss.xsl,v 1.1 2002/03/20 12:20:57 mf Exp $ + +Copyright David Carlisle 2001, 2002. + +Use and distribution of this code are permitted under the terms of the <a +href="http://www.w3.org/Consortium/Legal/copyright-software-19980720" +>W3C Software Notice and License</a>. +--> + +<xsl:stylesheet + version="1.0" + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:h="http://www.w3.org/1999/xhtml" + xmlns:m="http://www.w3.org/1998/Math/MathML" + xmlns:doc="http://www.dcarlisle.demon.co.uk/xsldoc" + xmlns:x="data:,x" + exclude-result-prefixes="x h doc" +> + +<h:h2>Dictionary</h:h2> + +<h:p> The following elements in the x: namespace form an +implementation of an "Operator Dictionary" for this MathML +Implementation. In the case of stretch operators, the element +specifies the symbol parts via the latin-1 equivalent character based +on the encoding in the symbol font. It is a clear "failure to comply +to the spec" that using latin 1 characters (or numeric character +references) in the latin 1 range access glyphs in teh symbol font via +font position, however most of these character parts are not in +Unicode (until 3.2), so there is no standard way to access these characters.</h:p> + +<x:x x="{" m="0em" stretch="true" top="�" middle="�" extend="�" bottom="�">{</x:x> +<x:x x="}" m="0em" stretch="true" top="�" middle="�" extend="�" bottom="�">{</x:x> + +<x:x x="(" m="0em" stretch="true" top="�" middle="�" extend="�" bottom="�">(</x:x> +<x:x x=")" m="0em" stretch="true" top="�" middle="�" extend="�" bottom="�">)</x:x> + +<x:x x="[" m="0em" stretch="true" top="�" middle="�" extend="�" bottom="�">[</x:x> +<x:x x="]" m="0em" stretch="true" top="�" middle="�" extend="�" bottom="�">]</x:x> + +<x:x x="〚" m="0em" stretch="true" top="��" middle="��" extend="��" bottom="��">[[</x:x> +<x:x x="〛" m="0em" stretch="true" top="��" middle="��" extend="��" bottom="��">]]</x:x> + +<x:x x="|" m="0em" stretch="true" top="�" middle="�" extend="�" bottom="�">|</x:x> +<x:x x="||" m="0em" stretch="true" top="��" middle="��" extend="��" bottom="��">||</x:x> + +<x:x x="⁡" m="0em"></x:x><!-- applyfunction --> +<x:x x="⁢" m="0em"></x:x><!-- invisibletimes --> +<x:x x="-">−</x:x> +<x:x x="≃"><span style="position: +relative; top: +.1em;">−</span><span style="position: +relative; left: -.55em; top: -.2em; margin: 0em;">~</span></x:x> +<x:x x="︸" m="0em">_v_</x:x> + + +<h:p>Remove these for now, as XML parser in IE6 is broken and doesn't +accept plane 1 characters.</h:p> +<!-- +<x:x x="𝔸" v="doublestruck">A</x:x> +<x:x x="𝔹" v="doublestruck">B</x:x> +<x:x x="ℂ" v="doublestruck">C</x:x> +<x:x x="𝔻" v="doublestruck">D</x:x> + +<x:x x="𝕒" v="doublestruck">a</x:x> +<x:x x="𝕓" v="doublestruck">b</x:x> +<x:x x="𝕔" v="doublestruck">c</x:x> +<x:x x="𝕕" v="doublestruck">d</x:x> + +<x:x x="𝔄" v="fraktur">A</x:x> +<x:x x="𝔅" v="fraktur">B</x:x> +<x:x x="ℭ" v="fraktur">C</x:x> +<x:x x="𝔇" v="fraktur">D</x:x> + +<x:x x="𝔞" v="fraktur">a</x:x> +<x:x x="𝔟" v="fraktur">b</x:x> +<x:x x="𝔠" v="fraktur">c</x:x> +<x:x x="𝔡" v="fraktur">d</x:x> +--> + +<h:p>Grab all of the above into a variable.</h:p> +<xsl:variable name="opdict" select="document('')/*/x:x"/> + +<h:h2>HTML elements</h:h2> + +<h:p> +XHTML elements get passed straight through, sans namespace prefix. +</h:p> +<xsl:template match="h:*"> +<xsl:element name="{local-name(.)}"> +<xsl:copy-of select="@*"/> +<xsl:apply-templates/> +</xsl:element> +</xsl:template> + + + + +<h:p> +Template for the head element copies the original content, aand in +addition adds a script element and CSS style element that implement +the core of the MathML renderer. +</h:p> + +<h:p doc:ref="malign"> +The malign function first finds the left most item in the aligngroup, and +then modifies the left margin of each item to make them +align. (Currently only left alignment is supported.) +</h:p> + +<h:p doc:ref="mrowStretch"> +The mrowStretch function implements stretchy brackets. It is called +repeatedly, once for each mo child,after a span corresponding to an +mrow. The arguments are the id of teh span and the characters to use +for the parts of the stretch operator. +constructed fence. The +</h:p> + +<h:p doc:ref="css"> +Inline CSS style block handles all font and size specification for the +various MathML operators. +</h:p> + +<xsl:template match="h:head"> + +<xsl:element name="{local-name(.)}"> +<xsl:copy-of select="@*"/> +<xsl:apply-templates/> + +<script> + +<xsl:text doc:id="malign"> +function malign (l) +{ +var m = 0; +for ( i = 0; i < l.length ; i++) +{ + m = Math.max(m,l[i].offsetLeft); +} +for ( i = 0; i < l.length ; i++) +{ + l[i].style.marginLeft=m - l[i].offsetLeft; +} +} +</xsl:text> + +<xsl:text doc:id="mrowStretch"> +function mrowStretch (opid,opt,ope,opm,opb){ +opH = opid.offsetHeight; +var opH; +var i; +var es; +if (mrowH > opH * 2) { +m= "<font size='+1' face='symbol'>" + opm + "</font><br/>" ; +if ((mrowH < opH * 3) &&(opm == ope) ) m=""; +es=""; +for ( i = 3; i <= mrowH / (2*opH) ; i += 1) es += "<font size='+1' face='symbol'>" + ope + "</font><br/>" ; +opid.innerHTML="<table class='lr'><tr><td><font size='+1' face='symbol'>" + + opt + "</font><br/>" + + es + + m + + es + + "<font size='+1' face='symbol'>" + opb + "</font></td></tr></table>"; +} +} +</xsl:text> + +<xsl:text doc:id="msubsup"> +function msubsup (bs,bbs,x,b,p){ +<!-- +p.style.setExpression("top",bs +" .offsetTop - " + (p.offsetHeight/2 +(bbs.offsetHeight - Math.max(bbs.offsetHeight, b.offsetHeight + p.offsetHeight)*.5))); +--> +p.style.setExpression("top",bs +" .offsetTop -" + (p.offsetHeight/2)); +b.style.setExpression("top",bs + ".offsetTop + " + (bbs.offsetHeight - b.offsetHeight*.5)); +x.style.setExpression("marginLeft",Math.max(p.offsetWidth,b.offsetWidth)); + document.recalc(true); +} +</xsl:text> + +<!-- +function msubsupzz (bs,x,b,p){ +p.style.setExpression("top",bs +" .offsetTop - " + bs + +"p.offsetHeight/2 +(" + bs + ".offsetHeight - Math.max(" + bs + ".offsetHeight, (" + bs + "b.offsetHeight + " + bs + "p.offsetHeight)*.5))"); +b.style.setExpression("top",bs + ".offsetTop + " + bs + ".offsetHeight - " + bs + "b.offsetHeight/2"); +x.style.setExpression("marginLeft","Math.max(" + bs +"p.offsetWidth," ++ bs +"b.offsetWidth)"); +} +--> + +<xsl:text doc:id="msup"> +function msup (bs,x,p){ +p.style.setExpression("top",bs +" .offsetTop -" + (p.offsetHeight/2)); +x.style.setExpression("marginLeft", bs +"p.offsetWidth"); +x.style.setExpression("height", bs + ".offsetHeight + " + p.offsetHeight); +document.recalc(true); +} +</xsl:text> + +<xsl:text doc:id="msub"> +function msub (bs,x,p){ +p.style.setExpression("top",bs +" .offsetTop +" + (p.offsetHeight/2)); +x.style.setExpression("marginLeft", bs +"p.offsetWidth"); +x.style.setExpression("height", bs + ".offsetHeight + " + p.offsetHeight); +document.recalc(true); +} +</xsl:text> + +<xsl:text doc:id="toggle"> +function toggle (x) { +for ( i = 0 ; i < x.childNodes.length ; i++) { +if (x.childNodes.item(i).style.display=='inline') { + x.childNodes.item(i).style.display='none'; +if ( i+1 == x.childNodes.length) { +x.childNodes.item(0).style.display='inline'; +} else { +x.childNodes.item(i+1).style.display='inline'; +}; +break; +} +} +} +</xsl:text> +</script> + + +<style> +<xsl:text doc:id="css"> + +.msubsup { +<!--background-color: red;--> +font-size: 80%; +position: absolute; +} + +.munderover { +display: inline; +vertical-align: middle; +} + +.lr { +display: inline; +vertical-align: middle; +} + +.mi { +font-style: serif; +} + + +.mspace{ +display: inline; +} +.mtext { +font-style: serif; +} +.ms { +font-style: monospace; +} + +.mi1 { +font-style: italic; +} + +.doublestruck { +font-family: castellar, algerian,niagara engraved; +} +.mo { +padding-right: .3em; +padding-left: .3em; +} + +.mn { +} + +.msqrt { +border-style: solid; +border-color: black; +border-width: .1em 0pt 0pt .1em; +padding-left: .2em; +margin-left: 0em; +margin-top: .2em; +display: inline; +} + +.actuarial { +border-style: solid; +border-color: black; +border-width: .1em .1em 0pt 0pt ; +padding-right: .2em; +margin-right: 0em; +margin-top: .2em; +display: inline; +} + +.ssa { + position:relative; top:+0.5ex; +width: 0pt; +color: red; +} + + +.mover { +margin: 0pt; +padding: 0pt; +display: inline; +vertical-align: middle; +text-align: center; +} + +.mtable { +display: inline; +vertical-align: middle; +} + + +.mfrac { +text-align: center; +display:inline; +vertical-align: middle; +} + +.mfraca { +vertical-align: bottom; +} + +.mfracaa { +border-width: 0em 0em .2ex 0em ; border-style: solid; + border-color: black; +} + + +.mfracb { +vertical-align: top; +} + +.merror{ +background-color: white ; +border-style: solid; +border-color: #FF0000; +color: #FF0000; +} +.mphantom{ +visibility: hidden; +} +</xsl:text> +</style> +</xsl:element> +</xsl:template> + +<h:p> +Unimplemented MathML elements get copied literally, in red, mainly as +a debugging aid. +</h:p> +<xsl:template match="m:*"> +<span style="color: red;"><<xsl:value-of select="local-name(.)"/>></span> +<xsl:apply-templates/> +<span style="color: red;"></<xsl:value-of select="local-name(.)"/>></span> +</xsl:template> + +<h:p> +mi: set default font based on string length, otherwise behaviour based +on entries in the operator dictionary if one exists, or content is +copied through to the output unchanged. +</h:p> +<xsl:template match="m:mi"> +<span class="mi"> +<xsl:if test="1=string-length(normalize-space(.))"> +<xsl:attribute name="class">mi1</xsl:attribute> +</xsl:if> +<xsl:apply-templates select="@mathvariant"/> + <xsl:variable name="x" select="normalize-space(.)"/> + <xsl:choose> + <xsl:when test="$opdict[@x=$x and @v]"> + <xsl:attribute name="class"><xsl:value-of select="$opdict[@x=$x]/@v"/></xsl:attribute> + <xsl:value-of select="$opdict[@x=$x and @v]"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$x"/> + </xsl:otherwise> + </xsl:choose> +</span> +</xsl:template> + +<h:p> +Handling of mathvariant attribute. +The choice of font families here (currently) avoids math-specific +fonts but does use several fionts coming with windows 9.x and/or +office 2000. +</h:p> +<xsl:template match="@mathvariant[.='bold']"> +<xsl:attribute name="style">font-weight: bold; font-style: upright</xsl:attribute> +</xsl:template> +<xsl:template match="@mathvariant[.='bold-italic']"> +<xsl:attribute name="style">font-style: upright; font-weight: bold; font-style: italic;</xsl:attribute> +</xsl:template> +<xsl:template match="@mathvariant[.='italic']"> +<xsl:attribute name="style">font-style: italic; </xsl:attribute> +</xsl:template> +<xsl:template match="@mathvariant[.='monospace']"> +<xsl:attribute name="style">font-family: monospace; </xsl:attribute> +</xsl:template> + +<xsl:template match="@mathvariant[.='sans-serif']"> +<xsl:attribute name="style">font-family: sans-serif; </xsl:attribute> +</xsl:template> +<xsl:template match="@mathvariant[.='bold-sans-serif']"> +<xsl:attribute name="style">font-family: sans-serif; font-weight: bold; </xsl:attribute> +</xsl:template> + +<xsl:template match="@mathvariant[.='fraktur']"> +<xsl:attribute name="style">font-family: old english text mt</xsl:attribute> +<xsl:attribute name="class"></xsl:attribute> +</xsl:template> + +<xsl:template match="@mathvariant[.='double-struck']"> +<xsl:attribute name="class">doublestruck</xsl:attribute> +</xsl:template> + + +<xsl:template match="@mathvariant[.='script']"> +<xsl:attribute name="style">font-family: brush script mt italic</xsl:attribute> +<xsl:attribute name="class"></xsl:attribute> +</xsl:template> + + +<h:p>mo: Generate a unique ID so that a script at the end of any +surrounding mrow may replace the conent by a suitably stretched +operator if need be.</h:p> +<xsl:template match="m:mo"> +<span id="{generate-id()}" class="mo"> + <xsl:apply-templates/> +</span> +</xsl:template> + +<h:p>mn: a simple span</h:p> +<xsl:template match="m:mn"> +<span class="mn"> + <xsl:apply-templates/> +</span> +</xsl:template> + +<h:p>munder: currently only supports underline, with a bottom border</h:p> +<xsl:template match="m:munder"> +<span class="munder"> +<xsl:if test="normalize-space(*[2])='̲'"> + <xsl:attribute + name="style">border-width: 0pt 0pt .1em 0pt; border-style: solid;"</xsl:attribute> +</xsl:if> + <span><xsl:apply-templates select="*[1]"/></span> +</span> +</xsl:template> + +<h:p>mover: currently only supports overline, with a top border</h:p> +<xsl:template match="m:mover"> +<span class="munder"> +<xsl:if test="normalize-space(*[2])='¯'"> + <xsl:attribute + name="style">border-width: .1em 0pt 0pt 0pt; border-style: solid;"</xsl:attribute> +</xsl:if> + <span><xsl:apply-templates select="*[1]"/></span> +</span> +</xsl:template> + +<h:p>munderover: </h:p> +<xsl:template match="m:munderover"> +<table class="munderover"> +<tr><td><xsl:apply-templates select="*[3]"/></td></tr> +<tr><td><xsl:apply-templates select="*[1]"/></td></tr> +<tr><td><xsl:apply-templates select="*[2]"/></td></tr> +</table> +</xsl:template> + +<h:p>mtext: a simple span</h:p> +<xsl:template match="m:mtext"> +<span class="mtext"> + <xsl:value-of select="normalize-space(.)"/> +</span> +</xsl:template> + +<h:p>mstyle: not many attributes currently supported</h:p> +<xsl:template match="m:mstyle"> +<span> +<xsl:attribute name="style"> + <xsl:if test="@color">color: <xsl:value-of select="@color"/>; </xsl:if> + <xsl:if test="@background">background-color: <xsl:value-of select="@background"/>; </xsl:if> +</xsl:attribute> + <xsl:apply-templates/> +</span> +</xsl:template> + + +<h:p>mglyph: Uses disable output escaping to construct a numeric +character reference. Uses IE's non conforming behaviour of using this +number to access the font encoding rather than unicode.</h:p> +<xsl:template match="m:mglyph"> +<font face="{@fontfamily}"><xsl:value-of +disable-output-escaping="yes" select="'&#'"/> +<xsl:value-of select="@index"/>;<xsl:text/> +</font> +</xsl:template> + +<h:p>ms: a simple span with left and right character added to the content.</h:p> +<xsl:template match="m:ms"> +<span class="ms"> + <xsl:value-of select="@lquote"/><xsl:if test="not(@lquote)">"</xsl:if> + <xsl:value-of select="normalize-space(.)"/> + <xsl:value-of select="@rquote"/><xsl:if test="not(@rquote)">"</xsl:if> +</span> +</xsl:template> + + +<xsl:template match="m:math"> + <xsl:call-template name="mrow"/> +</xsl:template> + + + +<xsl:template match="m:mfenced"> +<xsl:variable name="l"> + <xsl:choose> + <xsl:when test="@open"><xsl:value-of select="@open"/></xsl:when> + <xsl:otherwise>(</xsl:otherwise> + </xsl:choose> +</xsl:variable> +<xsl:variable name="r"> + <xsl:choose> + <xsl:when test="@close"><xsl:value-of select="@close"/></xsl:when> + <xsl:otherwise>)</xsl:otherwise> + </xsl:choose> +</xsl:variable> +<xsl:variable name="s"> + <xsl:choose> + <xsl:when test="@sep"> + <xsl:call-template name="text"> + <xsl:with-param name="x" select="@sep"/> + </xsl:call-template> + </xsl:when> + <xsl:otherwise>,</xsl:otherwise> + </xsl:choose> +</xsl:variable> +<span id="{generate-id()}L"><xsl:value-of select="$l"/></span> +<span id="{generate-id()}M"> +<xsl:for-each select="*"> +<xsl:apply-templates select="."/> +<xsl:if test="position() != last()"><span id="{generate-id()}X{position()}"><xsl:value-of select="$s"/></span></xsl:if> +</xsl:for-each> +</span> +<span id="{generate-id()}R"><xsl:value-of select="$r"/></span> +<script> + +<xsl:if test="$s=$opdict[@stretch='true']/@x"> +<xsl:for-each select="*[position()<last()]"> +<xsl:variable name="opdictentry" select="$opdict[@x=$s]"/> +mrowStretch(<xsl:value-of select="concat(generate-id(),'X',position())"/>,"<xsl:value-of +select="$opdictentry/@top"/>","<xsl:value-of +select="$opdictentry/@extend"/>","<xsl:value-of +select="$opdictentry/@middle"/>","<xsl:value-of +select="$opdictentry/@bottom"/>");</xsl:for-each> +</xsl:if> + +<xsl:variable name="opdictentry" select="$opdict[@x=$l]"/> +var mrowH = <xsl:value-of select="generate-id()"/>M.offsetHeight; +mrowStretch(<xsl:value-of select="generate-id()"/>L,"<xsl:value-of +select="$opdictentry/@top"/>","<xsl:value-of +select="$opdictentry/@extend"/>","<xsl:value-of +select="$opdictentry/@middle"/>","<xsl:value-of +select="$opdictentry/@bottom"/>");<xsl:text/> + +<xsl:variable name="opdictentry2" select="$opdict[@x=$r]"/> +mrowStretch(<xsl:value-of select="generate-id()"/>R,"<xsl:value-of +select="$opdictentry2/@top"/>","<xsl:value-of +select="$opdictentry2/@extend"/>","<xsl:value-of +select="$opdictentry2/@middle"/>","<xsl:value-of +select="$opdictentry2/@bottom"/>");<xsl:text/> +</script> +</xsl:template> + + + +<xsl:template match="m:mmultiscripts"> +<table style="display:inline; vertical-align: middle;"> +<tr> +<xsl:for-each select="*[preceding-sibling::m:mprescripts and position() mod 2 = 0]"> +<td><xsl:apply-templates select="."/></td> +</xsl:for-each> +<td rowspan="2"><xsl:apply-templates select="*[1]"/></td> +<xsl:for-each select="*[not(preceding-sibling::m:mprescripts) and position() !=1 and position() mod 2 = 1]"> +<td><xsl:apply-templates select="."/></td> +</xsl:for-each> +</tr> +<tr> +<xsl:for-each select="*[preceding-sibling::m:mprescripts and position() mod 2 = 1]"> +<td><xsl:apply-templates select="."/></td> +</xsl:for-each> +<xsl:for-each select="*[not(preceding-sibling::m:mprescripts) and +not(self::m:mprescripts) and position() mod 2 = 0]"> +<td><xsl:apply-templates select="."/></td> +</xsl:for-each> +</tr> +</table> +</xsl:template> + + +<xsl:template match="m:none"></xsl:template> + +<xsl:template match="m:merror"> +<span class="merror"><xsl:call-template name="mrow"/></span> +</xsl:template> + +<xsl:template match="m:mphantom"> +<span class="mphantom"><xsl:apply-templates/></span> +</xsl:template> + +<xsl:template match="m:maction[@type='tooltip']"> +<span title="{*[2]}"><xsl:apply-templates select="*[1]"/></span> +</xsl:template> + +<xsl:template match="m:maction[@type='toggle']"> +<span id="{generate-id()}" onclick="toggle({generate-id()})"> +<span style="display:inline;"><xsl:apply-templates select="*[1]"/></span> +<xsl:for-each select="*[position() > 1]"> +<span style="display:none;"><xsl:apply-templates select="."/></span> +</xsl:for-each> +</span> +</xsl:template> + + +<xsl:template match="m:maction[@type='statusline']"> +<span id="{generate-id()}" +onmouseover="window.status='{*[2]}';" +onmouseout="window.status='';" +> +<xsl:apply-templates select="*[1]"/></span> +</xsl:template> + + +<xsl:template match="m:maction[@type='highlight']"> +<span id="{generate-id()}" +onmouseover="{generate-id()}.style.backgroundColor='yellow';" +onmouseout="{generate-id()}.style.backgroundColor='white';"><xsl:apply-templates/></span> +</xsl:template> + + + +<xsl:template match="m:mrow" name="mrow"> +<span id="{generate-id()}" class="mrow"> + <xsl:apply-templates select="*"/> +</span> +<xsl:if test="m:mo[@stretch='true' or normalize-space(.)=$opdict[@stretch='true']/@x]"> +<script> +var mrowH = <xsl:value-of select="generate-id()"/>.offsetHeight; +<xsl:for-each select="m:mo[@stretch='true' or + normalize-space(.)=$opdict[@stretch='true']/@x]"> + +<xsl:variable name="o" select="normalize-space(.)"/> +<xsl:variable name="opdictentry" select="$opdict[@x=$o]"/> +mrowStretch(<xsl:value-of select="generate-id()"/>,"<xsl:value-of +select="$opdictentry/@top"/>","<xsl:value-of +select="$opdictentry/@extend"/>","<xsl:value-of +select="$opdictentry/@middle"/>","<xsl:value-of +select="$opdictentry/@bottom"/>");</xsl:for-each> +</script> +</xsl:if> +</xsl:template> + + +<xsl:template match="m:msubsup"> +<span id="{generate-id()}" > +<xsl:apply-templates select="*[1]"/></span +><span id="{generate-id()}b" class="msubsup"><xsl:apply-templates +select="*[2]"/></span +><span id="{generate-id()}p" class="msubsup"><xsl:apply-templates +select="*[3]"/></span +><span id="{generate-id()}x"></span> +<script> +msubsup("<xsl:value-of select="concat(generate-id(),'",',generate-id(),',',generate-id(),'x,',generate-id(),'b,',generate-id())"/>p); +</script> +</xsl:template> + +<xsl:template match="h:table//m:msubsup|m:mtable//m:msubsup|m:msubsup" +priority="2"> +<span> +<xsl:apply-templates select="*[1]"/> +</span +><sub><xsl:apply-templates +select="*[2]"/></sub> +<sup><xsl:apply-templates +select="*[3]"/></sup> +</xsl:template> + +<xsl:template match="m:msup +"> +<span id="{generate-id()}"> +<xsl:apply-templates select="*[1]"/> +</span +><span id="{generate-id()}p" class="msubsup"><xsl:apply-templates +select="*[2]"/></span +><span id="{generate-id()}x"></span> +<script> +msup("<xsl:value-of select="concat(generate-id(),'",',generate-id(),'x,',generate-id())"/>p); +</script> +</xsl:template> + +<xsl:template match="h:table//m:msup|m:mtable//m:msup|m:msup" +priority="2"> +<span> +<xsl:apply-templates select="*[1]"/> +</span +><sup><xsl:apply-templates +select="*[2]"/></sup> +</xsl:template> + +<xsl:template match="m:msub +"> +<span id="{generate-id()}"> +<xsl:apply-templates select="*[1]"/> +</span +><span id="{generate-id()}p" class="msubsup"><xsl:apply-templates +select="*[2]"/></span +><span id="{generate-id()}x"></span> +<script> +msub("<xsl:value-of select="concat(generate-id(),'",',generate-id(),'x,',generate-id())"/>p); +</script> +</xsl:template> + +<xsl:template match="h:table//m:msub|m:mtable//m:msub|m:msub" +priority="2"> +<span> +<xsl:apply-templates select="*[1]"/> +</span +><sub><xsl:apply-templates +select="*[2]"/></sub> +</xsl:template> + + +<xsl:template match="m:*/text()" name="text"> +<xsl:param name="x" select="normalize-space(.)"/> +<xsl:variable name="mo" select="document('')/*/x:x[@x=$x]"/> +<xsl:choose> + <xsl:when test="$mo"><xsl:copy-of select="$mo/node()"/></xsl:when> + <xsl:otherwise><xsl:copy-of select="$x"/></xsl:otherwise> +</xsl:choose> +</xsl:template> + + +<xsl:template match="m:msqrt"> +<span class="msqrtx">\</span><span class="msqrt"> +<xsl:apply-templates/> +</span> +</xsl:template> + +<xsl:template match="m:menclose[@notation='radical']"> +<span class="msqrtx">\</span><span class="msqrt"> +<xsl:apply-templates/> +</span> +</xsl:template> + +<xsl:template match="m:menclose[@notation='actuarial']"> +<span class="actuarial"> +<xsl:apply-templates/> +</span> +</xsl:template> + +<xsl:template match="m:menclose"> +<span class="msqrt"> +<xsl:apply-templates/> +</span> +</xsl:template> + +<xsl:template match="m:mroot"> +<span class="msqrtx"><sup><xsl:apply-templates select="*[2]"/></sup>\</span><span class="msqrt"> +<xsl:apply-templates select="*[1]"/> +</span> +</xsl:template> + + +<xsl:template match="m:mfrac"> +<xsl:param name="full" select="not(ancestor::m:mfrac)"/> +<table class="mfrac"> +<xsl:if test="$full"> + <xsl:attribute name="style">font-size: 75% ;</xsl:attribute> +</xsl:if> +<xsl:if test="not($full)"> + <xsl:attribute name="style">font-size: 100% ;</xsl:attribute> +</xsl:if> +<tr id="a{generate-id()}" class="mfraca"><td class="mfracaa"> +<xsl:apply-templates select="*[1]"/> +</td></tr> +<tr id="b{generate-id()}" class="mfracb"><td> +<xsl:apply-templates select="*[2]"/> +</td></tr> +</table><xsl:if test="$full"><script> +if ( a<xsl:value-of select="generate-id()" + />.offsetHeight > b<xsl:value-of select="generate-id()" + />.offsetHeight ) b<xsl:value-of select="generate-id() + "/>.style.setExpression("height",a<xsl:value-of select="generate-id()"/>.offsetHeight ); +else a<xsl:value-of +select="generate-id()"/>.style.setExpression("height",b<xsl:value-of + select="generate-id()"/>.offsetHeight ); +</script></xsl:if> +</xsl:template> + +<xsl:template match="m:padded"> +<span> +<xsl:attribute name="display"> +</xsl:attribute> +<xsl:apply-templates/> +</span> +</xsl:template> + + +<xsl:template match="m:mspace"> +<span style="padding-left: {@width};"></span> +</xsl:template> + +<xsl:template match="m:mtable"> +<table class="mtable"> +<xsl:apply-templates/> +</table> +<script> +<xsl:variable name="t" select="."/> +<xsl:for-each select="m:mtr[1]/m:mtd"> +<xsl:variable name="c" select="position()"/> +<xsl:for-each select="descendant::m:maligngroup"> +<xsl:variable name="g" select="position()"/> +malign([<xsl:for-each +select="$t/m:mtr/m:mtd[$c]/descendant::m:maligngroup[$g]"> + <xsl:value-of select="generate-id()"/> + <xsl:if test="position()<last()">,</xsl:if> +</xsl:for-each>]);</xsl:for-each> +</xsl:for-each> +</script> +</xsl:template> + +<xsl:template match="m:mtr"> +<tr> +<xsl:apply-templates/> +</tr> +</xsl:template> + + +<xsl:template match="m:mtd"> +<td> +<xsl:apply-templates/> +</td> +</xsl:template> + +<xsl:template match="m:maligngroup"> +<xsl:variable name="g"> +<xsl:choose> +<xsl:when test="@groupalign"> +</xsl:when> +<xsl:when test="ancestor::td/@groupalign"> +</xsl:when> +<xsl:when test="ancestor::tr/@groupalign"> +</xsl:when> +<xsl:when test="ancestor::table/@groupalign"> +</xsl:when> + +<xsl:otherwise>left</xsl:otherwise> +</xsl:choose> +</xsl:variable> +<span id="{generate-id()}"></span> +</xsl:template> + + +</xsl:stylesheet> diff --git a/source/idl/readme-idl.txt b/source/idl/readme-idl.txt new file mode 100644 index 0000000..6fd2f0d --- /dev/null +++ b/source/idl/readme-idl.txt @@ -0,0 +1,30 @@ +This directory contains idl specifications for the custom uno interfaces +defined by the Writer2LaTeX and Writer2xhtml extensions + +To avoid dependencies on the OOo SDK in the build process, compiled versions +are included here. + +If you need to rebuild it, the complete SDK is required. These are the steps: + +To create the registry database for Writer2LaTeX: + +idlc -I<path to SDK>\idl XW2LStarMathConverter.idl +regmerge writer2latex.rdb /UCR XW2LStarMathConverter.urd + +To create the java interface + +javamaker -BUCR -Torg.openoffice.da.writer2latex.XW2LStarMathConverter -nD <path to the OOo installation>\program\types.rdb writer2latex.rdb + +and likewise for Writer2xhtml: + +idlc -I<path to SDK>\idl XBatchConverter.idl +regmerge writer2xhtml.rdb /UCR XBatchConverter.urd + +To create the java interfaces + +javamaker -BUCR -Torg.openoffice.da.writer2xhtml.XBatchConverter -nD <path to the OOo installation>\program\types.rdb writer2xhtml.rdb +javamaker -BUCR -Torg.openoffice.da.writer2xhtml.XBatchHandler -nD <path to the OOo installation>\program\types.rdb writer2xhtml.rdb + + +If you need to use the interfaces from C++ you will also need to run cppumaker + diff --git a/source/idl/writer2latex/XW2LStarMathConverter.idl b/source/idl/writer2latex/XW2LStarMathConverter.idl new file mode 100644 index 0000000..c66f048 --- /dev/null +++ b/source/idl/writer2latex/XW2LStarMathConverter.idl @@ -0,0 +1,22 @@ +#ifndef __org_openoffice_da_writer2latex_XW2LStarMathConverter_idl__ +#define __org_openoffice_da_writer2latex_XW2LStarMathConverter_idl__ +#include <com/sun/star/uno/XInterface.idl> + +module org { module openoffice { module da { module writer2latex { + +interface XW2LStarMathConverter : com::sun::star::uno::XInterface +{ + + // method org::openoffice::da::writer2latex::XW2LStarMathConverter::convertFormula + string convertFormula ( [in] string sStarMathFormula ); + + // method org::openoffice::da::writer2latex::XW2LStarMathConverter::getPreamble + string getPreamble ( ); + +}; + +}; }; }; }; + +#endif + + \ No newline at end of file diff --git a/source/idl/writer2latex/org/openoffice/da/writer2latex/XW2LStarMathConverter.class b/source/idl/writer2latex/org/openoffice/da/writer2latex/XW2LStarMathConverter.class new file mode 100644 index 0000000000000000000000000000000000000000..20fc93e303a05aabb57eae701b0e6360924ca8a3 GIT binary patch literal 557 zcma)3TT8<*6#ioC+RZuVb>1FDRuCh@H^momP}rO>Z^NgoOS?*!NS1l|S>l7B=nwEm ziCI-d6ftnfcg}a=guJ{yJOfzAGQp-)5tSEQ$S{lpPCJZVsaSJWNtou>^z@`sYig$I zOm}ysxa7)k4hTW<oLw@SFcHy4`<w^bfkm(s$R6zv1nnCW9SBKJePJvi3%G+(f<o|~ zQkAMVNEpFtx%QRR)G8LymiKc+Fcxurpg8Nb6HYKw{#|sm-#Ba??D_lE27z~0`?gOK zx6_^U%>|D|DCwaAz5!cikN+thj1#Q=t*vw2m7ULQ2NNzFg3NX-V!cC9`sKyG?;Ync zh3OoMm~oNKV+{E$Mo=Pfw}a%vYn8oY0()0>c!n6(h6y&cFqE55wQK?hE@18~a%Pe< p+@et|8N4xM%vl+ILZM|nqS%_6w5)r~Hd}Vu&Dm*phxyx7i8qB!lV<<` literal 0 HcmV?d00001 diff --git a/source/idl/writer2latex/writer2latex.rdb b/source/idl/writer2latex/writer2latex.rdb new file mode 100644 index 0000000000000000000000000000000000000000..df61666d3265239b26f3352434e62f5077031fd8 GIT binary patch literal 8192 zcmZ<`_Vw88>8{Qo$H2hA1cYe7nSeZ+@=@*(3jrl&$q3Pt6|LZyXL`W!9|}O;P~e-b zcjeOMO0bt$ARcowujY@7E#_d5fberPujzsLd<c0@+e&T*F$n+RgGIl<d;tjG&qZ>I zU*iJ}1`dS!hmP-37-S%PR@qJMU_KW@K2h{@IfD>{KTA-&2ka&`g#3}8D%%*OApF3) zE7HMy9ta;sg1iM|gXmE{Bs>%tC6ktOJ)g}0ieIJ$jL_%@ht=8r;%DIT4@?fAa2x61 zq>)WKER1MP(<7`w@o%;}(U<|0pU~p}3{S%wczy%Mf2ea1J)AVC?HG>n@4Q&(9=JS4 zi~r0P-i7e;8W{iiMd^b&Zs_5-;TZpygr<}+fa+Vc_;(Ra%7xd*p!hFH&C5?q%S=wC zM@SD&JBDNYD;&!UVF0z4(Bgjy+eR*UdkPr;DT#wKPUz{X;TZoL%U}Fq0JY!I;@{-a z(z)>VJuv>ui!w`6i;Qv-OHwQ78Rmn@uHhK}-5-92GJyJLXz?#*9WMg!zX9VvBHYL) zxFoU2H?btcIX|x~wFsCT22<qF!7alv{-qr2|APD1Xz?F5!&C^=-v{@<85kT?!hGV( z5_3vNaP`l?V^!qKAOaabpmqOGn2`b0EobBtG6@3~xQw78ni1SlV+7I=zz7_;0S%>C zGw=YL=lX~yntn>6K5~Jrk3IJaFiTF+VvSt{@)HA-1oHTg5(7_iey)CTX`X&DFxTss z=H=^0c;*4~d|D!;2`CCu0WJsJ@{4jybHMEbUIw1xlA_E!a9;t1BMy{-l^;kNgcx|z zQ%eGhQWJBNa=>W|MHdqTD+4podCWiv8l+$Z@*$zX2NYq44J1K=W0WK@1VH`&N)_gE z@OT|s{txuOxfGQD!Q*$M`5zpz$7R3&p>@88nePX*5tQORA^D%d6Uc_7d?tAQ2Z=B+ zs1nNkKsDF_DE@hD3TA=FbJ5~IS}`>jKHfVT|KI?e$U9{TEPiNBI|6M4#lH<S{%wG4 z?C~#wCH}$AB}jnce{SzLBk=e+TKs=md3HJ|{=wtvqwx<8%jmr)rqep!VdmQbZ3M-? zB{cpmfo$yY53Gn$Yk#m?i6KDopC8F10G@wAi+?5Y&2K^R51x-1jel@(wxupgg~bo8 gX=9*`p!nB=#=jnrjXnOk!0``u;3zQ~0wjb00G<240ssI2 literal 0 HcmV?d00001 diff --git a/source/idl/writer2xhtml/XBatchConverter.idl b/source/idl/writer2xhtml/XBatchConverter.idl new file mode 100644 index 0000000..cd8f72e --- /dev/null +++ b/source/idl/writer2xhtml/XBatchConverter.idl @@ -0,0 +1,52 @@ +#ifndef __org_openoffice_da_writer2xhtml_XBatchConverter_idl__ +#define __org_openoffice_da_writer2xhtml_XBatchConverter_idl__ +#include <com/sun/star/uno/XInterface.idl> +#include <com/sun/star/beans/PropertyValue.idl> + +module org { module openoffice { module da { module writer2xhtml { + +// This interface is an IDL version of the java interface (writer2latex.api.BatchHandler) +interface XBatchHandler : com::sun::star::uno::XInterface +{ + + // method org::openoffice::da::writer2xhtml::XBatchHandler::startConversion + void startConversion (); + + // method org::openoffice::da::writer2xhtml::XBatchHandler::endConversion + void endConversion (); + + // method org::openoffice::da::writer2xhtml::XBatchHandler::startDirectory + void startDirectory ( [in] string sName ); + + // method org::openoffice::da::writer2xhtml::XBatchHandler::endDirectory + void endDirectory ( [in] string sName, [in] boolean bSuccess ); + + // method org::openoffice::da::writer2xhtml::XBatchHandler::startFile + void startFile ( [in] string sName ); + + // method org::openoffice::da::writer2xhtml::XBatchHandler::endFile + void endFile ( [in] string sName, [in] boolean bSuccess ); + + // method org::openoffice::da::writer2xhtml::XBatchHandler::cancel + boolean cancel (); + +}; + +// This interface is an IDL version of the java interface (writer2latex.api.BatchConverter) +interface XBatchConverter : com::sun::star::uno::XInterface +{ + + // method org::openoffice::da::writer2xhtml::XBatchConverter::convert + void convert ( [in] string sSourceURL, + [in] string sTargetURL, + [in] sequence< com::sun::star::beans::PropertyValue > lArguments, + [in] XBatchHandler handler ); + +} ; + + +}; }; }; }; + +#endif + + \ No newline at end of file diff --git a/source/idl/writer2xhtml/org/openoffice/da/writer2xhtml/XBatchConverter.class b/source/idl/writer2xhtml/org/openoffice/da/writer2xhtml/XBatchConverter.class new file mode 100644 index 0000000000000000000000000000000000000000..3c7d608f9e709c27a0746eb9d87cfba24fcdf0c1 GIT binary patch literal 564 zcma)3TT22#7(L@|uD8stCcX6#3gJMnN)JiVf@PqI($l!k>dHDJ<7oD?qKBZUAJC7A zrW?_f1_lo2JKy=v%=hqka}S`196?d49+e|5r0)kFr(H(RRiL@rnf3KBq^&)sy?#ZC zDOZMdKnOAeHf1zqqDO0;0r#{6i(tc(Lpq)aI@V0liIBAA3e(~<k2{znaJ*L+f?2-$ zVNusA5WUiO=B)b3vBQ}d(-URFDt*yp;e?l}|4M(zL^tHBRA>?;PmgPj_Q}3`d{84O ze3=mjotNbLV#EXCOWH8NHK0VW^(oD-N(V`T?Z350T=!-7-P^%@3=ToG9Ed>g5@dh) z9@i~2Q^;U3fi#v<h$9h41S<rwvKPKJTKVTE+7;R5QDUk#jH|i1XSvyU#5P+%%%B9; jkTsQcgEwCoV9mZms%_mO-CkO-tZOXSudsS)6y`huHyEEF literal 0 HcmV?d00001 diff --git a/source/idl/writer2xhtml/org/openoffice/da/writer2xhtml/XBatchHandler.class b/source/idl/writer2xhtml/org/openoffice/da/writer2xhtml/XBatchHandler.class new file mode 100644 index 0000000000000000000000000000000000000000..0d03a746f171325e9e07564e6a39289264d92861 GIT binary patch literal 734 zcma))U2oGc6oy}uG<LhJ4TG(W@%_0<2ur)(iNMl9Xht_eGC2s2}wWrh@>(82Y zfdqmdz>h*WP7y<z5Rt5N9_M|}$vMi8pI^QMc!}o(FLW{B`dBF4@5@y1Ea9IDX+*Jd zHZ-H0@4rq=I((O?EEfd@A&8EW(}d@V8u0Gnkw^`NN3fLY5g(r@J~m0gPn72Sv9e|Q zNh&C61g-LgS=Z`R6l19sfw$b*BbXH`yL{Vpx;L`0m31*8m|x!f({bArQVmv1V`z<+ zWD8fw4oY3!72J@yAfVP$WkH&#ROId}4hUv;x4JvM4{zhGjV?jw<K|!E<nqvsWG2T# zs=nqsHi>PrO7QIeloU7Zvj44pFU(M9ziU%0Fi?VEO)6<#5wx$EFpfKWAr^7Ffi{*H z>I@BrkfF)YVu(V_;YJ<T@R)$DrTMg!zpgV85PR>#cCcr$6xyGH`F2ZSfQ|KOY+%N5 zh*=k#Q*KQ;!gcFx;HlNyh1h&jI_)!x=r_#wysv2Y=5Bf3XWZHD`Ht@T71bQwt5p;@ dx*t?T9X+5GF-H$sMNuVv6jj;_JUMqQ@e5zav9JID literal 0 HcmV?d00001 diff --git a/source/idl/writer2xhtml/writer2xhtml.rdb b/source/idl/writer2xhtml/writer2xhtml.rdb new file mode 100644 index 0000000000000000000000000000000000000000..ba0a1894514515a1c5b598f94f9345942352a84e GIT binary patch literal 16384 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 LLC>D$92x%)p)-t- literal 0 HcmV?d00001 diff --git a/source/java/org/openoffice/da/comp/w2lcommon/filter/ByteArrayXStream.java b/source/java/org/openoffice/da/comp/w2lcommon/filter/ByteArrayXStream.java new file mode 100644 index 0000000..c76cf30 --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/filter/ByteArrayXStream.java @@ -0,0 +1,188 @@ +/************************************************************************ + * + * ByteArrayXStream.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-07-22) + * + */ + +package org.openoffice.da.comp.w2lcommon.filter; + +// This class is based on these java uno adapter classes: +// com.sun.star.lib.uno.adapter.ByteArrayToXInputStreamAdapter; +// com.sun.star.lib.uno.adapter.XOutputStreamToByteArrayAdapter; +// See http://go-oo.org/lxr/source/udk/javaunohelper/com/sun/star/lib/uno/adapter/XOutputStreamToByteArrayAdapter.java +// and http://go-oo.org/lxr/source/udk/javaunohelper/com/sun/star/lib/uno/adapter/ByteArrayToXInputStreamAdapter.java +// for original source + +import com.sun.star.io.XInputStream; +import com.sun.star.io.XOutputStream; +import com.sun.star.io.XSeekable; +import com.sun.star.io.XStream; + +/** <p>This is a java-uno adapter class which implements XStream using a + * byte array. (We need this because XGraphicProvider demans read/write access + * when storing a graphic to a stream.)</p> + */ +public class ByteArrayXStream implements XInputStream, XOutputStream, XSeekable, XStream { + + // Keep data about our byte array (we read and write to the same byte array) + + private int initialSize = 100240; // 10 kb + private int size = 0; // The current buffer size + private int position = 0; // The current write position, always<=size + private int readPosition = 0; // The current read position, always<=position + private boolean closed = false; // The XStream is closed + private byte[] buffer; // The buffer + + // Constructor: Initialize the byte array + + public ByteArrayXStream() { + size = initialSize; + buffer = new byte[size]; + } + + // Implementation of XOutputStream + + public void closeOutput() + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, + com.sun.star.io.IOException { + + // trim buffer + if ( buffer.length > position) { + byte[] newBuffer = new byte[position]; + System.arraycopy(buffer, 0, newBuffer, 0, position); + buffer = newBuffer; + } + closed = true; + } + + public void flush() + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, + com.sun.star.io.IOException { + } + + public void writeBytes(byte[] values) + throws com.sun.star.io.NotConnectedException, + com.sun.star.io.BufferSizeExceededException, + com.sun.star.io.IOException { + if ( values.length > size-position ) { + byte[] newBuffer = null; + while ( values.length > size-position ) + size *= 2; + newBuffer = new byte[size]; + System.arraycopy(buffer, 0, newBuffer, 0, position); + buffer = newBuffer; + } + System.arraycopy(values, 0, buffer, position, values.length); + position += values.length; + } + + // Implementation of XInputStream + + private void _check() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + if(closed) { + throw new com.sun.star.io.IOException("input closed"); + } + } + + public int available() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + _check(); + return position - readPosition; + } + + public void closeInput() throws com.sun.star.io.NotConnectedException, com.sun.star.io.IOException { + closed = true; + } + + public int readBytes(byte[][] values, int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + _check(); + try { + int remain = (int)(position - readPosition); + if (param > remain) param = remain; + /* ARGH!!! */ + if (values[0] == null){ + values[0] = new byte[param]; + // System.err.println("allocated new buffer of "+param+" bytes"); + } + System.arraycopy(buffer, readPosition, values[0], 0, param); + // System.err.println("readbytes() -> "+param); + readPosition += param; + return param; + } catch (ArrayIndexOutOfBoundsException ae) { + // System.err.println("readbytes() -> ArrayIndexOutOfBounds"); + ae.printStackTrace(); + throw new com.sun.star.io.BufferSizeExceededException("buffer overflow"); + } catch (Exception e) { + // System.err.println("readbytes() -> Exception: "+e.getMessage()); + e.printStackTrace(); + throw new com.sun.star.io.IOException("error accessing buffer"); + } + } + + public int readSomeBytes(byte[][] values, int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + // System.err.println("readSomebytes()"); + return readBytes(values, param); + } + + public void skipBytes(int param) throws com.sun.star.io.NotConnectedException, com.sun.star.io.BufferSizeExceededException, com.sun.star.io.IOException { + // System.err.println("skipBytes("+param+")"); + _check(); + if (param > (position - readPosition)) + throw new com.sun.star.io.BufferSizeExceededException("buffer overflow"); + readPosition += param; + } + + + // Implementation of XSeekable + + public long getLength() throws com.sun.star.io.IOException { + // System.err.println("getLength() -> "+m_length); + if (buffer != null) return position; + else throw new com.sun.star.io.IOException("no bytes"); + } + + public long getPosition() throws com.sun.star.io.IOException { + // System.err.println("getPosition() -> "+m_pos); + if (buffer != null) return readPosition; + else throw new com.sun.star.io.IOException("no bytes"); + } + + public void seek(long param) throws com.sun.star.lang.IllegalArgumentException, com.sun.star.io.IOException { + // System.err.println("seek("+param+")"); + if (buffer != null) { + if (param < 0 || param > position) throw new com.sun.star.lang.IllegalArgumentException("invalid seek position"); + else readPosition = (int)param; + } else throw new com.sun.star.io.IOException("no bytes"); + } + + // Implementation of XStream + public XInputStream getInputStream() { return this; } + + public XOutputStream getOutputStream() { return this; } + + // Get the buffer + public byte[] getBuffer() { return buffer; } + +} + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/filter/ExportFilterBase.java b/source/java/org/openoffice/da/comp/w2lcommon/filter/ExportFilterBase.java new file mode 100644 index 0000000..3899deb --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/filter/ExportFilterBase.java @@ -0,0 +1,504 @@ +/************************************************************************ + * + * ExportFilterBase.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-24) + * + */ + +// This file was originally based on OOo's XMergeBridge, which is (c) by Sun Microsystems + +package org.openoffice.da.comp.w2lcommon.filter; + +import com.sun.star.lib.uno.adapter.XInputStreamToInputStreamAdapter; +import com.sun.star.lib.uno.adapter.XOutputStreamToOutputStreamAdapter; + +//import com.sun.star.beans.PropertyValue; +import com.sun.star.io.XInputStream; +import com.sun.star.io.XOutputStream; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XServiceName; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.AnyConverter; +import com.sun.star.ucb.XSimpleFileAccess2; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +//import com.sun.star.xml.sax.InputSource; +//import com.sun.star.xml.sax.XParser; +import com.sun.star.xml.sax.XDocumentHandler; +import com.sun.star.xml.XExportFilter; + +import org.openoffice.da.comp.w2lcommon.helper.MessageBox; +//import org.openoffice.da.comp.w2lcommon.helper.PropertyHelper; +import writer2latex.api.Converter; +import writer2latex.api.ConverterFactory; +import writer2latex.api.ConverterResult; +import writer2latex.api.OutputFile; + +import java.util.Iterator; +//import java.util.Enumeration; +//import java.util.Vector; +import java.io.*; +//import javax.xml.parsers.*; +//import org.xml.sax.SAXException; +//import java.net.URI; + + +/** This class provides an abstract uno component which implements an XExportFilter. + * The filter is actually generic and only then constructor and 3 strings needs + * to changed by the subclass. + */ +public abstract class ExportFilterBase implements + XExportFilter, + XServiceName, + XServiceInfo, + XDocumentHandler, + XTypeProvider { + + /** Service name for the component */ + public static final String __serviceName = ""; + + /** Implementation name for the component */ + public static final String __implementationName = ""; + + /** Filter name to include in error messages */ + public static final String __displayName = ""; + + private static XComponentContext xComponentContext = null; + protected static XMultiServiceFactory xMSF; + private static XInputStream xInStream =null; + private static XOutputStream xOutStream=null; + private static XOutputStream xos = null; + private static String sdMime=null; + private static String sURL=""; + + private Object filterData; + private XSimpleFileAccess2 sfa2; + + + /** We need to get the Service Manager from the Component context to + * instantiate certain services, hence this constructor. + * The subclass must override this to set xMSF properly from the reigstration class + */ + public ExportFilterBase(XComponentContext xComponentContext1) { + xComponentContext = xComponentContext1; + xMSF = null; + } + + + // Some utility methods: + + String getFileName(String origName) { + String name=null; + if (origName !=null) { + if(origName.equalsIgnoreCase("")) + name = "OutFile"; + else { + if (origName.lastIndexOf("/")>=0) { + origName=origName.substring(origName.lastIndexOf("/")+1,origName.length()); + } + if (origName.lastIndexOf(".")>=0) { + name = origName.substring(0,(origName.lastIndexOf("."))); + } + else { + name=origName; + } + } + } + else{ + name = "OutFile"; + } + + return name; + } + + public String replace(String origString, String origChar, String replaceChar){ + String tmp=""; + int index=origString.indexOf(origChar); + if(index !=-1){ + while (index !=-1){ + String first =origString.substring(0,index); + first=first.concat(replaceChar); + tmp=tmp.concat(first); + origString=origString.substring(index+1,origString.length()); + index=origString.indexOf(origChar); + if(index==-1) { + tmp=tmp.concat(origString); + } + } + } + return tmp; + } + + public String needsMask(String origString) { + if (origString.indexOf("&")!=-1){ + origString=replace(origString,"&","&"); + } + if (origString.indexOf("\"")!=-1){ + origString=replace(origString,"\"","""); + } + if (origString.indexOf("<")!=-1){ + origString=replace(origString,"<","<"); + } + if (origString.indexOf(">")!=-1){ + origString=replace(origString,">",">"); + } + return origString; + + } + + + // Implementation of XExportFilter: + + public boolean exporter(com.sun.star.beans.PropertyValue[] aSourceData, + java.lang.String[] msUserData) throws com.sun.star.uno.RuntimeException{ + + sURL=null; + filterData = null; + + // Get user data from configuration (type detection) + //String udConvertClass=msUserData[0]; + //String udImport =msUserData[2]; + //String udExport =msUserData[3]; + sdMime = msUserData[5]; + + // Get source data (only the OutputStream and the URL are actually used) + com.sun.star.beans.PropertyValue[] pValue = aSourceData; + for (int i = 0 ; i < pValue.length; i++) { + try{ + if (pValue[i].Name.compareTo("OutputStream")==0){ + xos=(com.sun.star.io.XOutputStream)AnyConverter.toObject(new Type(com.sun.star.io.XOutputStream.class), pValue[i].Value); + } + //if (pValue[i].Name.compareTo("FileName")==0){ + // sFileName=(String)AnyConverter.toObject(new Type(java.lang.String.class), pValue[i].Value); + //} + if (pValue[i].Name.compareTo("URL")==0){ + sURL=(String)AnyConverter.toObject(new Type(java.lang.String.class), pValue[i].Value); + } + //if (pValue[i].Name.compareTo("Title")==0){ + // title=(String)AnyConverter.toObject(new Type(java.lang.String.class), pValue[i].Value); + //} + if (pValue[i].Name.compareTo("FilterData")==0) { + filterData = pValue[i].Value; + } + } + catch(com.sun.star.lang.IllegalArgumentException AnyExec){ + System.err.println("\nIllegalArgumentException "+AnyExec); + } + } + + + if (sURL==null){ + sURL=""; + } + + // Create a pipe to be used by the XDocumentHandler implementation: + try { + Object xPipeObj=xMSF.createInstance("com.sun.star.io.Pipe"); + xInStream = (XInputStream) UnoRuntime.queryInterface( + XInputStream.class , xPipeObj ); + xOutStream = (XOutputStream) UnoRuntime.queryInterface( + XOutputStream.class , xPipeObj ); + } + catch (Exception e){ + System.err.println("Exception "+e); + return false; + } + + return true; + } + + + + // Implementation of XDocumentHandler: + // Flat xml is created by the sax events and passed through the pipe + // created by exporter() + + public void startDocument () { + //Do nothing + } + + public void endDocument()throws com.sun.star.uno.RuntimeException { + try{ + xOutStream.closeOutput(); + convert(xInStream,xos); + } + catch (IOException e){ + MessageBox msgBox = new MessageBox(xComponentContext); + msgBox.showMessage(__displayName+": IO error in conversion", + e.toString()+" at "+e.getStackTrace()[0].toString()); + throw new com.sun.star.uno.RuntimeException(e.getMessage()); + } + catch (Exception e){ + MessageBox msgBox = new MessageBox(xComponentContext); + msgBox.showMessage(__displayName+": Internal error in conversion", + e.toString()+" at "+e.getStackTrace()[0].toString()); + throw new com.sun.star.uno.RuntimeException(__displayName+" Exception"); + } + } + + + + public void startElement (String str, com.sun.star.xml.sax.XAttributeList xattribs) + { + + str="<".concat(str); + if (xattribs !=null) + { + str= str.concat(" "); + int len=xattribs.getLength(); + for (short i=0;i<len;i++) + { + str=str.concat(xattribs.getNameByIndex(i)); + str=str.concat("=\""); + str=str.concat(needsMask(xattribs.getValueByIndex(i))); + str=str.concat("\" "); + } + } + str=str.concat(">"); + try{ + xOutStream.writeBytes(str.getBytes("UTF-8")); + } + catch (Exception e){ + System.err.println("\n"+e); + } + + } + + public void endElement(String str){ + + str="</".concat(str); + str=str.concat(">"); + try{ + xOutStream.writeBytes(str.getBytes("UTF-8")); + + } + catch (Exception e){ + System.err.println("\n"+e); + } + + + } + public void characters(String str){ + str=needsMask(str); + try{ + xOutStream.writeBytes(str.getBytes("UTF-8")); + } + catch (Exception e){ + System.err.println("\n"+e); + } + + + } + + public void ignorableWhitespace(String str){ + + + } + public void processingInstruction(String aTarget, String aData){ + + } + + public void setDocumentLocator(com.sun.star.xml.sax.XLocator xLocator){ + + } + + + + // This is the actual conversion method, using Writer2LaTeX to convert + // the flat xml recieved from the XInputStream, and writing the result + // to the XOutputStream. The XMLExporter does not support export to + // compound documents with multiple output files; so the main file + // is written to the XOutStream and other files are written using ucb. + + public void convert (com.sun.star.io.XInputStream xml,com.sun.star.io.XOutputStream exportStream) + throws com.sun.star.uno.RuntimeException, IOException { + + // Initialise the file access + sfa2 = null; + try { + Object sfaObject = xComponentContext.getServiceManager().createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", xComponentContext); + sfa2 = (XSimpleFileAccess2) UnoRuntime.queryInterface(XSimpleFileAccess2.class, sfaObject); + } + catch (com.sun.star.uno.Exception e) { + // failed to get SimpleFileAccess service (should not happen) + } + + // Get base name from the url provided by OOo + String sName= getFileName(sURL); + + // Adapter for input stream (OpenDocument flat xml) + XInputStreamToInputStreamAdapter xis =new XInputStreamToInputStreamAdapter(xml); + + // Adapter for output stream (Main output file) + XOutputStreamToOutputStreamAdapter newxos =new XOutputStreamToOutputStreamAdapter(exportStream); + + // Create converter + Converter converter = ConverterFactory.createConverter(sdMime); + if (converter==null) { + throw new com.sun.star.uno.RuntimeException("Failed to create converter to "+sdMime); + } + + // Apply the FilterData to the converter + if (filterData!=null) { + FilterDataParser fdp = new FilterDataParser(xComponentContext); + fdp.applyFilterData(filterData,converter); + } + + // Do conversion + converter.setGraphicConverter(new GraphicConverterImpl(xComponentContext)); + + ConverterResult dataOut = null; + try { + dataOut = converter.convert(xis,sName); + } + catch (IOException e) { + // Fail silently + } + + // Write out files + Iterator docEnum = dataOut.iterator(); + + // Remove the file name part of the url + String sNewURL = null; + if (sURL.lastIndexOf("/")>-1) { + // Take the url up to and including the last slash + sNewURL = sURL.substring(0,sURL.lastIndexOf("/")+1); + } + else { + // The url does not include a path; this should not really happen, + // but in this case we will write to the current default directory + sNewURL = ""; + } + + while (docEnum.hasNext() && sURL.startsWith("file:")) { + OutputFile docOut = (OutputFile)docEnum.next(); + + if (dataOut.getMasterDocument()==docOut) { + // The master document is written to the XOutStream supplied + // by the XMLFilterAdaptor + docOut.write(newxos); + newxos.flush(); + newxos.close(); + } + else { + // Additional documents are written directly using ucb + + // Get the file name and the (optional) directory name + String sFullFileName = docOut.getFileName(); + String sDirName = ""; + String sFileName = sFullFileName; + int nSlash = sFileName.indexOf("/"); + if (nSlash>-1) { + sDirName = sFileName.substring(0,nSlash); + sFileName = sFileName.substring(nSlash+1); + } + + try{ + // Create subdirectory if required + if (sDirName.length()>0 && !sfa2.exists(sNewURL+sDirName)) { + sfa2.createFolder(sNewURL+sDirName); + } + + // writeFile demands an InputStream, so we need a pipe + Object xPipeObj=xMSF.createInstance("com.sun.star.io.Pipe"); + XInputStream xInStream + = (XInputStream) UnoRuntime.queryInterface(XInputStream.class, xPipeObj ); + XOutputStream xOutStream + = (XOutputStream) UnoRuntime.queryInterface(XOutputStream.class, xPipeObj ); + OutputStream outStream = new XOutputStreamToOutputStreamAdapter(xOutStream); + // Feed the pipe with content... + docOut.write(outStream); + outStream.flush(); + outStream.close(); + xOutStream.closeOutput(); + // ...and then write the content to the url + sfa2.writeFile(sNewURL+sFullFileName,xInStream); + } + catch (Throwable e){ + MessageBox msgBox = new MessageBox(xComponentContext); + msgBox.showMessage(__displayName+": Error writing files", + e.toString()+" at "+e.getStackTrace()[0].toString()); + } + } + + } + + } + + + // Implement methods from interface XTypeProvider + // Implementation of XTypeProvider + + public com.sun.star.uno.Type[] getTypes() { + Type[] typeReturn = {}; + + try { + typeReturn = new Type[] { + new Type( XTypeProvider.class ), + new Type( XExportFilter.class ), + new Type( XServiceName.class ), + new Type( XServiceInfo.class ) }; + } + catch( Exception exception ) { + + } + + return( typeReturn ); + } + + + public byte[] getImplementationId() { + byte[] byteReturn = {}; + + byteReturn = new String( "" + this.hashCode() ).getBytes(); + + return( byteReturn ); + } + + // Implement method from interface XServiceName + public String getServiceName() { + return( __serviceName ); + } + + // Implement methods from interface XServiceInfo + public boolean supportsService(String stringServiceName) { + return( stringServiceName.equals( __serviceName ) ); + } + + public String getImplementationName() { + return __implementationName; + //return( W2LExportFilter.class.getName() ); + } + + public String[] getSupportedServiceNames() { + String[] stringSupportedServiceNames = { __serviceName }; + return( stringSupportedServiceNames ); + } + + +} + + + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/filter/FilterDataParser.java b/source/java/org/openoffice/da/comp/w2lcommon/filter/FilterDataParser.java new file mode 100644 index 0000000..765dd6b --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/filter/FilterDataParser.java @@ -0,0 +1,271 @@ +/************************************************************************ + * + * FilterDataParser.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package org.openoffice.da.comp.w2lcommon.filter; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Enumeration; + +import com.sun.star.beans.PropertyValue; +import com.sun.star.io.NotConnectedException; +import com.sun.star.io.XInputStream; +import com.sun.star.io.XOutputStream; +import com.sun.star.ucb.CommandAbortedException; +import com.sun.star.ucb.XSimpleFileAccess2; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.util.XStringSubstitution; + +import com.sun.star.lib.uno.adapter.XInputStreamToInputStreamAdapter; +import com.sun.star.lib.uno.adapter.XOutputStreamToOutputStreamAdapter; + +import org.openoffice.da.comp.w2lcommon.helper.PropertyHelper; +import writer2latex.api.Converter; + + +/** This class parses the FilterData property passed to the filter and + * applies it to a <code>Converter</code> + * All errors are silently ignored + */ +public class FilterDataParser { + + //private static XComponentContext xComponentContext = null; + + private XSimpleFileAccess2 sfa2; + private XStringSubstitution xPathSub; + + public FilterDataParser(XComponentContext xComponentContext) { + //this.xComponentContext = xComponentContext; + + // Get the SimpleFileAccess service + sfa2 = null; + try { + Object sfaObject = xComponentContext.getServiceManager().createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", xComponentContext); + sfa2 = (XSimpleFileAccess2) UnoRuntime.queryInterface(XSimpleFileAccess2.class, sfaObject); + } + catch (com.sun.star.uno.Exception e) { + // failed to get SimpleFileAccess service (should not happen) + } + + // Get the PathSubstitution service + xPathSub = null; + try { + Object psObject = xComponentContext.getServiceManager().createInstanceWithContext( + "com.sun.star.util.PathSubstitution", xComponentContext); + xPathSub = (XStringSubstitution) UnoRuntime.queryInterface(XStringSubstitution.class, psObject); + } + catch (com.sun.star.uno.Exception e) { + // failed to get PathSubstitution service (should not happen) + } + } + + /** Apply the given FilterData property to the given converter + * @param data an Any containing the FilterData property + * @param converter a <code>writer2latex.api.Converter</code> implementation + */ + public void applyFilterData(Object data, Converter converter) { + // Get the array from the data, if possible + PropertyValue[] filterData = null; + if (AnyConverter.isArray(data)) { + try { + Object[] arrayData = (Object[]) AnyConverter.toArray(data); + if (arrayData instanceof PropertyValue[]) { + filterData = (PropertyValue[]) arrayData; + } + } + catch (com.sun.star.lang.IllegalArgumentException e) { + // Failed to convert to array; should not happen - ignore + } + } + if (filterData==null) { return; } + + PropertyHelper props = new PropertyHelper(filterData); + + // Get the special properties TemplateURL, ConfigURL and AutoCreate + Object tpl = props.get("TemplateURL"); + String sTemplate = null; + if (tpl!=null && AnyConverter.isString(tpl)) { + try { + sTemplate = substituteVariables(AnyConverter.toString(tpl)); + } + catch (com.sun.star.lang.IllegalArgumentException e) { + // Failed to convert to String; should not happen - ignore + } + } + + Object auto = props.get("AutoCreate"); + boolean bAutoCreate = false; + if (auto!=null && AnyConverter.isString(auto)) { + try { + if ("true".equals(AnyConverter.toString(auto))) { + bAutoCreate = true; + } + } + catch (com.sun.star.lang.IllegalArgumentException e) { + // Failed to convert to String; should not happen - ignore + } + } + + Object cfg = props.get("ConfigURL"); + String sConfig = null; + if (cfg!=null && AnyConverter.isString(cfg)) { + try { + sConfig = substituteVariables(AnyConverter.toString(cfg)); + } + catch (com.sun.star.lang.IllegalArgumentException e) { + // Failed to convert to String; should not happen - ignore + } + } + + // Load the template from the specified URL, if any + if (sfa2!=null && sTemplate!=null && sTemplate.length()>0) { + try { + XInputStream xIs = sfa2.openFileRead(sTemplate); + if (xIs!=null) { + InputStream is = new XInputStreamToInputStreamAdapter(xIs); + converter.readTemplate(is); + is.close(); + xIs.closeInput(); + } + } + catch (IOException e) { + // ignore + } + catch (NotConnectedException e) { + // ignore + } + catch (CommandAbortedException e) { + // ignore + } + catch (com.sun.star.uno.Exception e) { + // ignore + } + } + + // Create config if required + try { + if (bAutoCreate && sfa2!=null && sConfig!=null && !sConfig.startsWith("*") && !sfa2.exists(sConfig)) { + // Note: Requires random access, ie. must be a file URL: + XOutputStream xOs = sfa2.openFileWrite(sConfig); + if (xOs!=null) { + OutputStream os = new XOutputStreamToOutputStreamAdapter(xOs); + converter.getConfig().write(os); + os.flush(); + os.close(); + xOs.closeOutput(); + } + } + } + catch (IOException e) { + // ignore + } + catch (NotConnectedException e) { + // ignore + } + catch (CommandAbortedException e) { + // Ignore + } + catch (com.sun.star.uno.Exception e) { + // Ignore + } + + // Load the configuration from the specified URL, if any + if (sConfig!=null) { + if (sConfig.startsWith("*")) { // internal configuration + try { + converter.getConfig().readDefaultConfig(sConfig.substring(1)); + } + catch (IllegalArgumentException e) { + // ignore + } + } + else if (sfa2!=null) { // real URL + try { + XInputStream xIs = sfa2.openFileRead(sConfig);; + if (xIs!=null) { + InputStream is = new XInputStreamToInputStreamAdapter(xIs); + converter.getConfig().read(is); + is.close(); + xIs.closeInput(); + } + } + catch (IOException e) { + // Ignore + } + catch (NotConnectedException e) { + // Ignore + } + catch (CommandAbortedException e) { + // Ignore + } + catch (com.sun.star.uno.Exception e) { + // Ignore + } + } + } + + // Read further configuration properties + Enumeration keys = props.keys(); + while (keys.hasMoreElements()) { + String sKey = (String) keys.nextElement(); + if (!"ConfigURL".equals(sKey) && !"TemplateURL".equals(sKey) && !"AutoCreate".equals(sKey)) { + Object value = props.get(sKey); + if (AnyConverter.isString(value)) { + try { + converter.getConfig().setOption(sKey,AnyConverter.toString(value)); + } + catch (com.sun.star.lang.IllegalArgumentException e) { + // Failed to convert to String; should not happen - ignore + } + } + } + } + } + + private String substituteVariables(String sUrl) { + if (xPathSub!=null) { + try { + return xPathSub.substituteVariables(sUrl, false); + } + catch (com.sun.star.container.NoSuchElementException e) { + // Found an unknown variable, no substitution + // (This will only happen if false is replaced by true above) + return sUrl; + } + } + else { // Not path substitution available + return sUrl; + } + } + +} + + + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl.java b/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl.java new file mode 100644 index 0000000..e291271 --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl.java @@ -0,0 +1,67 @@ +/************************************************************************ + * + * GraphicConverterImpl.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-07-21) + * + */ + +package org.openoffice.da.comp.w2lcommon.filter; + +import com.sun.star.uno.XComponentContext; + +import writer2latex.api.GraphicConverter; + +public class GraphicConverterImpl implements GraphicConverter { + + private GraphicConverter graphicConverter1; + private GraphicConverter graphicConverter2; + + public GraphicConverterImpl(XComponentContext xComponentContext) { + graphicConverter1 = new GraphicConverterImpl1(xComponentContext); + graphicConverter2 = new GraphicConverterImpl2(xComponentContext); + } + + public boolean supportsConversion(String sSourceMime, String sTargetMime, boolean bCrop, boolean bResize) { + return graphicConverter1.supportsConversion(sSourceMime, sTargetMime, bCrop, bResize) || + graphicConverter2.supportsConversion(sSourceMime, sTargetMime, bCrop, bResize); + } + + public byte[] convert(byte[] source, String sSourceMime, String sTargetMime) { + byte[] result = null; + + // Prefer the simple implementation (GraphicProvider) + if (graphicConverter1.supportsConversion(sSourceMime, sTargetMime, false, false)) { + result = graphicConverter1.convert(source, sSourceMime, sTargetMime); + } + + // If this is not possible or fails, try the complex implementation + if (result==null) { + result = graphicConverter2.convert(source, sSourceMime, sTargetMime); + } + + return result; + + } + + +} + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl1.java b/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl1.java new file mode 100644 index 0000000..d2faa17 --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl1.java @@ -0,0 +1,194 @@ +/************************************************************************ + * + * GraphicConverterImpl1.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + */ + +// Version 1.0 (2008-11-22) + +package org.openoffice.da.comp.w2lcommon.filter; + +// Java uno helper class +import com.sun.star.lib.uno.adapter.ByteArrayToXInputStreamAdapter; + +// UNO classes +import com.sun.star.beans.PropertyValue; +import com.sun.star.graphic.XGraphic; +import com.sun.star.graphic.XGraphicProvider; +//import com.sun.star.io.XInputStream; +//import com.sun.star.io.XOutputStream; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.uno.XComponentContext; +import com.sun.star.uno.UnoRuntime; + +//import java.io.InputStream; +//import java.io.OutputStream; + +import writer2latex.api.GraphicConverter; +import writer2latex.api.MIMETypes; + +/** A GraphicConverter implementation which uses the GraphicProvider service + * to convert the graphic. This service does only support simple format + * conversion using the "internal" graphics filters in Draw. Advanced features + * like pdf, crop and resize thus cannot be handled. + */ +public class GraphicConverterImpl1 implements GraphicConverter { + + // Signatures for start and end in exp + private byte[] psStart; + private byte[] psEnd; + + + private XGraphicProvider xGraphicProvider; + + public GraphicConverterImpl1(XComponentContext xComponentContext) { + try { + // Get the XGraphicProvider interface of the GraphicProvider service + XMultiComponentFactory xMCF = xComponentContext.getServiceManager(); + Object graphicProviderObject = xMCF.createInstanceWithContext("com.sun.star.graphic.GraphicProvider", xComponentContext); + xGraphicProvider = (XGraphicProvider) UnoRuntime.queryInterface(XGraphicProvider.class, graphicProviderObject); + } + catch (com.sun.star.uno.Exception ex) { + System.err.println("Failed to get XGraphicProvider object"); + xGraphicProvider = null; + } + try { + psStart = "%!PS-Adobe".getBytes("US-ASCII"); + psEnd = "%%EOF".getBytes("US-ASCII"); + } + catch (java.io.UnsupportedEncodingException ex) { + // US-ASCII *is* supported :-) + } + + } + + public boolean supportsConversion(String sSourceMime, String sTargetMime, boolean bCrop, boolean bResize) { + // We don't support cropping and resizing + if (bCrop || bResize) { return false; } + + // We can convert vector formats to eps: + if (MIMETypes.EPS.equals(sTargetMime) && (MIMETypes.WMF.equals(sSourceMime) && MIMETypes.SVM.equals(sSourceMime))) { + return true; + } + + // And we can convert all formats to bitmaps + boolean bSupportsSource = + MIMETypes.PNG.equals(sSourceMime) || MIMETypes.JPEG.equals(sSourceMime) || + MIMETypes.GIF.equals(sSourceMime) || MIMETypes.TIFF.equals(sSourceMime) || + MIMETypes.BMP.equals(sSourceMime) || MIMETypes.WMF.equals(sSourceMime) || + MIMETypes.SVM.equals(sSourceMime); + boolean bSupportsTarget = + MIMETypes.PNG.equals(sTargetMime) || MIMETypes.JPEG.equals(sTargetMime) || + MIMETypes.GIF.equals(sTargetMime) || MIMETypes.TIFF.equals(sTargetMime) || + MIMETypes.BMP.equals(sTargetMime); + return bSupportsSource && bSupportsTarget; + } + + public byte[] convert(byte[] source, String sSourceMime, String sTargetMime) { + + // It seems that the GraphicProvider can only create proper eps if + // the source is a vector format, hence + if (MIMETypes.EPS.equals(sTargetMime)) { + if (!MIMETypes.WMF.equals(sSourceMime) && !MIMETypes.SVM.equals(sSourceMime)) { + return null; + } + } + + ByteArrayToXInputStreamAdapter xSource = new ByteArrayToXInputStreamAdapter(source); + ByteArrayXStream xTarget = new ByteArrayXStream(); + try { + // Read the source + PropertyValue[] sourceProps = new PropertyValue[1]; + sourceProps[0] = new PropertyValue(); + sourceProps[0].Name = "InputStream"; + sourceProps[0].Value = xSource; + XGraphic result = xGraphicProvider.queryGraphic(sourceProps); + + // Store as new type + PropertyValue[] targetProps = new PropertyValue[2]; + targetProps[0] = new PropertyValue(); + targetProps[0].Name = "MimeType"; + targetProps[0].Value = sTargetMime; + targetProps[1] = new PropertyValue(); + targetProps[1].Name = "OutputStream"; + targetProps[1].Value = xTarget; + xGraphicProvider.storeGraphic(result,targetProps); + + + // Close the output and return the result + xTarget.closeOutput(); + xTarget.flush(); + if (MIMETypes.EPS.equals(sTargetMime)) { + return cleanEps(xTarget.getBuffer()); + } + else { + return xTarget.getBuffer(); + } + } + catch (com.sun.star.io.IOException e) { + return null; + } + catch (com.sun.star.lang.IllegalArgumentException e) { + return null; + } + catch (com.sun.star.lang.WrappedTargetException e) { + return null; + } + catch (Throwable e) { + return null; + } + } + + private byte[] cleanEps(byte[] blob) { + int n = blob.length; + + int nStart = 0; + for (int i=0; i<n; i++) { + if (match(blob,psStart,i)) { + nStart=i; + break; + } + } + + int nEnd = n; + for (int i=nStart; i<n; i++) { + if (match(blob,psEnd,i)) { + nEnd=i+psEnd.length; + break; + } + } + + byte[] newBlob = new byte[nEnd-nStart]; + System.arraycopy(blob,nStart,newBlob,0,nEnd-nStart); + return newBlob; + } + + private boolean match(byte[] blob, byte[] sig, int nStart) { + int n = sig.length; + if (nStart+n>=blob.length) { return false; } + for (int i=0; i<n; i++) { + if (blob[nStart+i]!=sig[i]) { return false; } + } + return true; + } + + +} + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl2.java b/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl2.java new file mode 100644 index 0000000..4f22413 --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/filter/GraphicConverterImpl2.java @@ -0,0 +1,267 @@ +/************************************************************************ + * + * GraphicConverterImpl2.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-10-07) + * + */ + + +package org.openoffice.da.comp.w2lcommon.filter; + +import java.util.Hashtable; + +import com.sun.star.awt.Point; +import com.sun.star.awt.Size; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertySet; +import com.sun.star.drawing.XDrawPage; +import com.sun.star.drawing.XDrawPages; +import com.sun.star.drawing.XDrawPagesSupplier; +import com.sun.star.drawing.XShape; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XStorable; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.uno.XComponentContext; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.util.XRefreshable; + +import com.sun.star.lib.uno.adapter.ByteArrayToXInputStreamAdapter; +import com.sun.star.lib.uno.adapter.XOutputStreamToByteArrayAdapter; + +import writer2latex.api.GraphicConverter; +import writer2latex.api.MIMETypes; + +/** A GraphicConverter implementation which uses a hidden Draw document to + * store the graphic, providing more control over the image than the + * simple GraphicProvider implementation. + */ +public class GraphicConverterImpl2 implements GraphicConverter { + + private XComponentContext xComponentContext; + private Hashtable importFilter; + private Hashtable exportFilter; + + public GraphicConverterImpl2(XComponentContext xComponentContext) { + this.xComponentContext = xComponentContext; + + importFilter = new Hashtable(); + importFilter.put(MIMETypes.BMP, "BMP - MS Windows"); + //importFilter.put(MIMETypes.EMF, "EMF - MS Windows Metafile"); + importFilter.put(MIMETypes.EPS, "EPS - Encapsulated PostScript"); + importFilter.put(MIMETypes.GIF, "GIF - Graphics Interchange Format"); + importFilter.put(MIMETypes.JPEG, "JPG - JPEG"); + importFilter.put(MIMETypes.PNG, "PNG - Portable Network Graphic"); + importFilter.put(MIMETypes.SVM, "SVM - StarView Metafile"); + importFilter.put(MIMETypes.TIFF, "TIF - Tag Image File"); + importFilter.put(MIMETypes.WMF, "WMF - MS Windows Metafile"); + + exportFilter = new Hashtable(); + exportFilter.put(MIMETypes.BMP,"draw_bmp_Export"); + //exportFilter.put(MIMETypes.EMF,"draw_emf_Export"); + exportFilter.put(MIMETypes.EPS,"draw_eps_Export"); + exportFilter.put(MIMETypes.GIF,"draw_gif_Export"); + exportFilter.put(MIMETypes.JPEG,"draw_jpg_Export"); + exportFilter.put(MIMETypes.PNG,"draw_png_Export"); + //exportFilter.put(MIMETypes.SVG,"draw_svg_Export"); + exportFilter.put(MIMETypes.SVM,"draw_svm_Export"); + exportFilter.put(MIMETypes.TIFF,"draw_tif_Export"); + exportFilter.put(MIMETypes.WMF,"draw_wmf_Export"); + exportFilter.put(MIMETypes.PDF,"draw_pdf_Export"); + } + + public boolean supportsConversion(String sSourceMime, String sTargetMime, boolean bCrop, boolean bResize) { + // We don't support cropping and resizing + if (bCrop || bResize) { return false; } + + // We currently only support conversion of svm into pdf + // Trying wmf causes an IllegalArgumentException "URL seems to be an unsupported one" + // Seems to be an OOo bug; workaround: Use temporary files..?? + boolean bSupportsSource = MIMETypes.SVM.equals(sSourceMime); + /*MIMETypes.PNG.equals(sSourceMime) || MIMETypes.JPEG.equals(sSourceMime) || + MIMETypes.GIF.equals(sSourceMime) || MIMETypes.TIFF.equals(sSourceMime) || + MIMETypes.BMP.equals(sSourceMime) || MIMETypes.WMF.equals(sSourceMime) || + MIMETypes.SVM.equals(sSourceMime);*/ + return bSupportsSource && MIMETypes.PDF.equals(sTargetMime); + } + + public byte[] convert(byte[] source, String sSourceMime, String sTargetMime) { + // Open a hidden sdraw document + XMultiComponentFactory xMCF = xComponentContext.getServiceManager(); + +org.openoffice.da.comp.w2lcommon.helper.MessageBox msgBox = new org.openoffice.da.comp.w2lcommon.helper.MessageBox(xComponentContext); + + try { + // Load the graphic into a new draw document as xDocument + // using a named filter + Object desktop = xMCF.createInstanceWithContext( + "com.sun.star.frame.Desktop", xComponentContext); + + XComponentLoader xComponentLoader = (XComponentLoader) + UnoRuntime.queryInterface(XComponentLoader.class, desktop); +//msgBox.showMessage("Graphics","Trying to load using filter name "+importFilter.get(sSourceMime)); + + PropertyValue[] fileProps = new PropertyValue[3]; + fileProps[0] = new PropertyValue(); + fileProps[0].Name = "FilterName"; + fileProps[0].Value = (String) importFilter.get(sSourceMime); + fileProps[1] = new PropertyValue(); + fileProps[1].Name = "InputStream"; + fileProps[1].Value = new ByteArrayToXInputStreamAdapter(source); + fileProps[2] = new PropertyValue(); + fileProps[2].Name = "Hidden"; + fileProps[2].Value = new Boolean(true); + + XComponent xDocument = xComponentLoader.loadComponentFromURL( + "private:stream", "_blank", 0, fileProps); + + // Get the first draw page as xDrawPage + XDrawPagesSupplier xDrawPagesSupplier = (XDrawPagesSupplier) + UnoRuntime.queryInterface(XDrawPagesSupplier.class, xDocument); + XDrawPages xDrawPages = xDrawPagesSupplier.getDrawPages(); + Object drawPage = xDrawPages.getByIndex(0); + XDrawPage xDrawPage = (XDrawPage) UnoRuntime.queryInterface( + XDrawPage.class, drawPage); + + // Get the shape as xShape + Object shape = xDrawPage.getByIndex(0); + XShape xShape = (XShape) UnoRuntime.queryInterface(XShape.class, shape); + + // Move the shape to upper left corner of the page + Point position = new Point(); + position.X = 0; + position.Y = 0; + xShape.setPosition(position); + + // Adjust the page size and margin to the size of the graphic + XPropertySet xPageProps = (XPropertySet) UnoRuntime.queryInterface( + XPropertySet.class, xDrawPage); + Size size = xShape.getSize(); + xPageProps.setPropertyValue("Width", new Integer(size.Width)); + xPageProps.setPropertyValue("Height", new Integer(size.Height)); + xPageProps.setPropertyValue("BorderTop", new Integer(0)); + xPageProps.setPropertyValue("BorderBottom", new Integer(0)); + xPageProps.setPropertyValue("BorderLeft", new Integer(0)); + xPageProps.setPropertyValue("BorderRight", new Integer(0)); + + // Export the draw document (xDocument) + refreshDocument(xDocument); + + XOutputStreamToByteArrayAdapter outputStream = new XOutputStreamToByteArrayAdapter(); + + PropertyValue[] exportProps = new PropertyValue[3]; + exportProps[0] = new PropertyValue(); + exportProps[0].Name = "FilterName"; + exportProps[0].Value = (String) exportFilter.get(sTargetMime); + exportProps[1] = new PropertyValue(); + exportProps[1].Name = "OutputStream"; + exportProps[1].Value = outputStream; + exportProps[2] = new PropertyValue(); + exportProps[2].Name = "Overwrite"; + exportProps[2].Value = new Boolean(true); + + XStorable xStore = (XStorable) UnoRuntime.queryInterface ( + XStorable.class, xDocument); + xStore.storeToURL ("private:stream", exportProps); + outputStream.closeOutput(); + + byte[] result = outputStream.getBuffer(); + xDocument.dispose(); + + return result; + + } + catch (com.sun.star.beans.PropertyVetoException e) { +msgBox.showMessage("Exception",e.toString()); + } + catch (com.sun.star.beans.UnknownPropertyException e) { +msgBox.showMessage("Exception",e.toString()); + } + catch (com.sun.star.io.IOException e) { +msgBox.showMessage("Exception",e.toString()); + } + catch (com.sun.star.lang.IllegalArgumentException e) { +msgBox.showMessage("Exception",e.toString()); + } + catch (com.sun.star.lang.IndexOutOfBoundsException e) { +msgBox.showMessage("Exception",e.toString()); + } + catch (com.sun.star.lang.WrappedTargetException e) { +msgBox.showMessage("Exception",e.toString()); + } + catch (com.sun.star.uno.Exception e) { +msgBox.showMessage("Exception",e.toString()); + } + + // Conversion failed, for whatever reason + return null; + + } + + protected void refreshDocument(XComponent document) { + XRefreshable refreshable = (XRefreshable) UnoRuntime.queryInterface(XRefreshable.class, document); + if (refreshable != null) { + refreshable.refresh(); + } + } + + + + +/* Dim SelSize As New com.sun.star.awt.Size + SelSize = oGraphic.Size + oDrawGraphic.GraphicURL = oGraphic.GraphicURL + oDrawGraphic.Size = SelSize + oDrawPage.add(oDrawGraphic) + oDrawGraphic.GraphicCrop = oGraphic.GraphicCrop + oDrawPage.Width = oGraphic.Size.Width + oDrawPage.Height = oGraphic.Size.Height + Dim aFilterData (1) As new com.sun.star.beans.PropertyValue + aFilterData(0).Name = "PixelWidth" ' + aFilterData(0).Value = oDrawPage.Width/100 * iPixels / 25.40 + aFilterData(1).Name = "PixelHeight" + aFilterData(1).Value = oDrawPage.Height/100 * iPixels / 25.40 + Export( oDrawPage, sURLImageResized , aFilterData() ) + On error resume Next + oDrawDoc.Close(True) + On error goto 0 + + SUB Export( xObject, sFileUrl As String, aFilterData ) + Dim xExporter As Object + xExporter = createUnoService( "com.sun.star.drawing.GraphicExportFilter" ) + xExporter.SetSourceDocument( xObject ) + Dim aArgs (2) As new com.sun.star.beans.PropertyValue + 'sFileURL = ConvertToURL(sFileURL) + aArgs(0).Name = "FilterName" + aArgs(0).Value = "jpg" + aArgs(1).Name = "URL" + aArgs(1).Value = sFileURL + 'print sFileURL + aArgs(2).Name = "FilterData" + aArgs(2).Value = aFilterData + xExporter.filter( aArgs() ) +END SUB*/ + + + +} + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/filter/OptionsDialogBase.java b/source/java/org/openoffice/da/comp/w2lcommon/filter/OptionsDialogBase.java new file mode 100644 index 0000000..e1d0424 --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/filter/OptionsDialogBase.java @@ -0,0 +1,547 @@ +/************************************************************************ + * + * OptionsDialogBase.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-10-14) + * + */ + +package org.openoffice.da.comp.w2lcommon.filter; + +import java.util.HashSet; + +import com.sun.star.awt.XDialogEventHandler; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.XPropertyAccess; +import com.sun.star.beans.XPropertySet; +import com.sun.star.container.XNameAccess; +import com.sun.star.document.XDocumentInfoSupplier; +import com.sun.star.frame.XDesktop; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XServiceName; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.ui.dialogs.XExecutableDialog; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.util.XChangesBatch; +import com.sun.star.util.XMacroExpander; + +import org.openoffice.da.comp.w2lcommon.helper.DialogBase; +import org.openoffice.da.comp.w2lcommon.helper.PropertyHelper; + +/** This class provides an abstract uno component which implements a filter ui + */ +public abstract class OptionsDialogBase extends DialogBase implements + XPropertyAccess { // Filter ui requires XExecutableDialog + XPropertyAccess + + ////////////////////////////////////////////////////////////////////////// + // The subclass must override the following; and override the + // implementation of XDialogEventHandler if needed + + /** Load settings from the registry to the dialog + * The subclass must implement this + */ + protected abstract void loadSettings(XPropertySet xRegistryProps); + + /** Save settings from the dialog to the registry and create FilterData + * The subclass must implement this + */ + protected abstract void saveSettings(XPropertySet xRegistryProps, PropertyHelper filterData); + + /** Return the name of the library containing the dialog + */ + public abstract String getDialogLibraryName(); + + /** Return the name of the dialog within the library + */ + public abstract String getDialogName(); + + /** Return the path to the options in the registry */ + public abstract String getRegistryPath(); + + /** Create a new OptionsDialogBase */ + public OptionsDialogBase(XComponentContext xContext) { + super(xContext); + this.xMSF = null; // must be set properly by subclass + mediaProps = null; + sConfigNames = null; + lockedOptions = new HashSet(); + } + + ////////////////////////////////////////////////////////////////////////// + // Implement some methods required by DialogBase + + /** Initialize the dialog (eg. with settings from the registry) + */ + public void initialize() { + try { + // Prepare registry view + Object view = getRegistryView(false); + XPropertySet xProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class,view); + + // Load settings using method from subclass + loadSettings(xProps); + + // Dispose the registry view + disposeRegistryView(view); + } + catch (com.sun.star.uno.Exception e) { + // Failed to get registry view + } + } + + /** Finalize the dialog after execution (eg. save settings to the registry) + */ + public void finalize() { + try { + // Prepare registry view + Object rwview = getRegistryView(true); + XPropertySet xProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class,rwview); + + // Save settings and create FilterData using method from subclass + PropertyHelper filterData = new PropertyHelper(); + saveSettings(xProps, filterData); + + // Commit registry changes + XChangesBatch xUpdateContext = (XChangesBatch) + UnoRuntime.queryInterface(XChangesBatch.class,rwview); + try { + xUpdateContext.commitChanges(); + } + catch (Exception e) { + // ignore + } + + // Dispose the registry view + disposeRegistryView(rwview); + + // Update the media properties with the FilterData + PropertyHelper helper = new PropertyHelper(mediaProps); + helper.put("FilterData",filterData.toArray()); + mediaProps = helper.toArray(); + } + catch (com.sun.star.uno.Exception e) { + // Failed to get registry view + } + } + + + ////////////////////////////////////////////////////////////////////////// + // Some private global variables + + // The service factory + protected XMultiServiceFactory xMSF; + + // The media properties (set/get via XPropertyAccess implementation) + private PropertyValue[] mediaProps; + + // Configuration names (stored during execution of dialog) + private String[] sConfigNames; + + // Set of locked controls + private HashSet lockedOptions; + + + ////////////////////////////////////////////////////////////////////////// + // Some private utility methods + + // Perform macro extansion + private String expandMacros(String s) { + if (s.startsWith("vnd.sun.star.expand:")) { + // The string contains a macro, usually as a result of using %origin% in the registry + s = s.substring(20); + Object expander = xContext.getValueByName("/singletons/com.sun.star.util.theMacroExpander"); + XMacroExpander xExpander = (XMacroExpander) UnoRuntime.queryInterface (XMacroExpander.class, expander); + try { + return xExpander.expandMacros(s); + } + catch (IllegalArgumentException e) { + // Unknown macro name found, proceed and hope for the best + return s; + } + } + else { + return s; + } + } + + // Get the template name from the document with ui focus + private String getTemplateName() { + try { + // Get current component + Object desktop = xContext.getServiceManager() + .createInstanceWithContext("com.sun.star.frame.Desktop",xContext); + XDesktop xDesktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class,desktop); + XComponent xComponent = xDesktop.getCurrentComponent(); + + // Get the document info property set + XDocumentInfoSupplier xDocInfoSuppl = (XDocumentInfoSupplier) + UnoRuntime.queryInterface(XDocumentInfoSupplier.class, xComponent); + Object docInfo = xDocInfoSuppl.getDocumentInfo(); + XPropertySet xDocInfo = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class, docInfo); + + return getPropertyValueAsString(xDocInfo,"Template"); + } + catch (Exception e) { + return ""; + } + } + + // Get a view of the options root in the registry + private Object getRegistryView(boolean bUpdate) + throws com.sun.star.uno.Exception { + Object provider = xMSF.createInstance( + "com.sun.star.configuration.ConfigurationProvider"); + XMultiServiceFactory xProvider = (XMultiServiceFactory) + UnoRuntime.queryInterface(XMultiServiceFactory.class,provider); + PropertyValue[] args = new PropertyValue[1]; + args[0] = new PropertyValue(); + args[0].Name = "nodepath"; + args[0].Value = getRegistryPath(); + String sServiceName = bUpdate ? + "com.sun.star.configuration.ConfigurationUpdateAccess" : + "com.sun.star.configuration.ConfigurationAccess"; + Object view = xProvider.createInstanceWithArguments(sServiceName,args); + return view; + } + + // Dispose a previously obtained registry view + private void disposeRegistryView(Object view) { + XComponent xComponent = (XComponent) + UnoRuntime.queryInterface(XComponent.class,view); + xComponent.dispose(); + } + + ////////////////////////////////////////////////////////////////////////// + // Implement uno interfaces + + // Override getTypes() from the interface XTypeProvider + public Type[] getTypes() { + Type[] typeReturn = {}; + try { + typeReturn = new Type[] { + new Type( XServiceName.class ), + new Type( XServiceInfo.class ), + new Type( XTypeProvider.class ), + new Type( XExecutableDialog.class ), + new Type( XPropertyAccess.class ), + new Type( XDialogEventHandler.class ) }; + } catch(Exception exception) { + } + return typeReturn; + } + + + // Implement the interface XPropertyAccess + public PropertyValue[] getPropertyValues() { + return mediaProps; + } + + public void setPropertyValues(PropertyValue[] props) { + mediaProps = props; + } + + + ////////////////////////////////////////////////////////////////////////// + // Various utility methods to be used by the sublasses + + // Helpers to load and save settings + + protected void updateLockedOptions() { + lockedOptions.clear(); + short nItem = getListBoxSelectedItem("Config"); + int nStdConfigs = getListBoxStringItemList("Config").length - sConfigNames.length; + if (nItem>=nStdConfigs) { + // Get current configuration name + String sName = sConfigNames[nItem-nStdConfigs]; + + try { + // Prepare registry view + Object view = getRegistryView(false); + XPropertySet xProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class,view); + + // Get the available configurations + Object configurations = getPropertyValue(xProps,"Configurations"); + XNameAccess xConfigurations = (XNameAccess) + UnoRuntime.queryInterface(XNameAccess.class,configurations); + + // Get the LockedOptions node from the desired configuration + String sLockedOptions = ""; + Object config = xConfigurations.getByName(sName); + XPropertySet xCfgProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class,config); + sLockedOptions = getPropertyValueAsString(xCfgProps,"LockedOptions"); + + // Dispose the registry view + disposeRegistryView(view); + + // Feed lockedOptions with the comma separated list of options + int nStart = 0; + for (int i=0; i<sLockedOptions.length(); i++) { + if (sLockedOptions.charAt(i)==',') { + lockedOptions.add(sLockedOptions.substring(nStart,i).trim()); + nStart = i+1; + } + } + if (nStart<sLockedOptions.length()) { + lockedOptions.add(sLockedOptions.substring(nStart).trim()); + } + } + catch (Exception e) { + // no options will be locked... + } + } + + } + + protected boolean isLocked(String sOptionName) { + return lockedOptions.contains(sOptionName); + } + + // Configuration + protected void loadConfig(XPropertySet xProps) { + // The list box is extended with configurations from the registry + String[] sStdConfigs = getListBoxStringItemList("Config"); + int nStdConfigs = sStdConfigs.length; + + Object configurations = getPropertyValue(xProps,"Configurations"); + XNameAccess xConfigurations = (XNameAccess) + UnoRuntime.queryInterface(XNameAccess.class,configurations); + sConfigNames = xConfigurations.getElementNames(); + int nRegConfigs = sConfigNames.length; + + String[] sAllConfigs = new String[nStdConfigs+nRegConfigs]; + for (short i=0; i<nStdConfigs; i++) { + sAllConfigs[i] = sStdConfigs[i]; + } + + for (short i=0; i<nRegConfigs; i++) { + try { + Object config = xConfigurations.getByName(sConfigNames[i]); + XPropertySet xCfgProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class,config); + sAllConfigs[nStdConfigs+i] = getPropertyValueAsString(xCfgProps,"DisplayName"); + } + catch (Exception e) { + sAllConfigs[nStdConfigs+i] = ""; + } + } + + setListBoxStringItemList("Config",sAllConfigs); + if (nStdConfigs+nRegConfigs<=12) { + setListBoxLineCount("Config",(short) (nStdConfigs+nRegConfigs)); + } + else { + setListBoxLineCount("Config",(short) 12); + } + + // Select item based on template name + String sTheTemplateName = getTemplateName(); + Object templates = getPropertyValue(xProps,"Templates"); + XNameAccess xTemplates = (XNameAccess) + UnoRuntime.queryInterface(XNameAccess.class,templates); + String[] sTemplateNames = xTemplates.getElementNames(); + for (int i=0; i<sTemplateNames.length; i++) { + try { + Object template = xTemplates.getByName(sTemplateNames[i]); + XPropertySet xTplProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class,template); + String sTemplateName = getPropertyValueAsString(xTplProps,"TemplateName"); + if (sTemplateName.equals(sTheTemplateName)) { + String sConfigName = getPropertyValueAsString(xTplProps,"ConfigName"); + for (short j=0; j<nRegConfigs; j++) { + if (sConfigNames[j].equals(sConfigName)) { + setListBoxSelectedItem("Config",(short) (nStdConfigs+j)); + return; + } + } + } + } + catch (Exception e) { + // ignore + } + } + + // Select item based on value stored in registry + short nConfig = getPropertyValueAsShort(xProps,"Config"); + if (nConfig<nStdConfigs) { + setListBoxSelectedItem("Config",nConfig); + } + else { // Registry configurations are stored by name + String sConfigName = getPropertyValueAsString(xProps,"ConfigName"); + for (short i=0; i<nRegConfigs; i++) { + if (sConfigNames[i].equals(sConfigName)) { + setListBoxSelectedItem("Config",(short) (nStdConfigs+i)); + } + } + } + } + + protected short saveConfig(XPropertySet xProps, PropertyHelper filterData) { + // The Config list box is common for all dialogs + Object configurations = getPropertyValue(xProps,"Configurations"); + XNameAccess xNameAccess = (XNameAccess) + UnoRuntime.queryInterface(XNameAccess.class,configurations); + + boolean bFound = false; + short nConfig = getListBoxSelectedItem("Config"); + int nStdConfigs = getListBoxStringItemList("Config").length - sConfigNames.length; + if (nConfig>=nStdConfigs) { // only handle registry configurations + int i = nConfig-nStdConfigs; + try { + Object config = xNameAccess.getByName(sConfigNames[i]); + XPropertySet xCfgProps = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class,config); + filterData.put("ConfigURL",expandMacros(getPropertyValueAsString(xCfgProps,"ConfigURL"))); + filterData.put("TemplateURL",expandMacros(getPropertyValueAsString(xCfgProps,"TargetTemplateURL"))); + setPropertyValue(xProps,"ConfigName",sConfigNames[i]); + bFound = true; + } + catch (Exception e) { + } + } + setPropertyValue(xProps,"Config",nConfig); + if (!bFound) { setPropertyValue(xProps,"ConfigName",""); } + return nConfig; + } + + // Check box option (boolean) + protected boolean loadCheckBoxOption(XPropertySet xProps, String sName) { + boolean bValue = getPropertyValueAsBoolean(xProps,sName); + setCheckBoxStateAsBoolean(sName, bValue); + return bValue; + } + + protected boolean saveCheckBoxOption(XPropertySet xProps, String sName) { + boolean bValue = getCheckBoxStateAsBoolean(sName); + setPropertyValue(xProps, sName, bValue); + return bValue; + } + + protected boolean saveCheckBoxOption(XPropertySet xProps, PropertyHelper filterData, + String sName, String sOptionName) { + boolean bValue = saveCheckBoxOption(xProps, sName); + if (!isLocked(sOptionName)) { + filterData.put(sOptionName, Boolean.toString(bValue)); + } + return bValue; + } + + // List box option + protected short loadListBoxOption(XPropertySet xProps, String sName) { + short nValue = getPropertyValueAsShort(xProps, sName); + setListBoxSelectedItem(sName ,nValue); + return nValue; + } + + protected short saveListBoxOption(XPropertySet xProps, String sName) { + short nValue = getListBoxSelectedItem(sName); + setPropertyValue(xProps, sName, nValue); + return nValue; + } + + protected short saveListBoxOption(XPropertySet xProps, PropertyHelper filterData, + String sName, String sOptionName, String[] sValues) { + short nValue = saveListBoxOption(xProps, sName); + if (!isLocked(sOptionName) && (nValue>=0) && (nValue<sValues.length)) { + filterData.put(sOptionName, sValues[nValue]); + } + return nValue; + } + + // Combo box option + protected String loadComboBoxOption(XPropertySet xProps, String sName) { + String sValue = getPropertyValueAsString(xProps, sName); + setComboBoxText(sName ,sValue); + return sValue; + } + + protected String saveComboBoxOption(XPropertySet xProps, String sName) { + String sValue = getComboBoxText(sName); + setPropertyValue(xProps, sName, sValue); + return sValue; + } + + protected String saveComboBoxOption(XPropertySet xProps, PropertyHelper filterData, + String sName, String sOptionName) { + String sValue = saveComboBoxOption(xProps, sName); + if (!isLocked(sOptionName)) { + filterData.put(sOptionName, sValue); + } + return sValue; + } + + // Text Field option + protected String loadTextFieldOption(XPropertySet xProps, String sName) { + String sValue = getPropertyValueAsString(xProps, sName); + setTextFieldText(sName ,sValue); + return sValue; + } + + protected String saveTextFieldOption(XPropertySet xProps, String sName) { + String sValue = getTextFieldText(sName); + setPropertyValue(xProps, sName, sValue); + return sValue; + } + + protected String saveTextFieldOption(XPropertySet xProps, PropertyHelper filterData, + String sName, String sOptionName) { + String sValue = saveTextFieldOption(xProps, sName); + if (!isLocked(sOptionName)) { + filterData.put(sOptionName, sValue); + } + return sValue; + } + + // Numeric option + protected int loadNumericOption(XPropertySet xProps, String sName) { + int nValue = getPropertyValueAsInteger(xProps, sName); + setNumericFieldValue(sName, nValue); + return nValue; + } + + protected int saveNumericOption(XPropertySet xProps, String sName) { + int nValue = getNumericFieldValue(sName); + setPropertyValue(xProps, sName, nValue); + return nValue; + } + + protected int saveNumericOptionAsPercentage(XPropertySet xProps, + PropertyHelper filterData, String sName, String sOptionName) { + int nValue = saveNumericOption(xProps, sName); + if (!isLocked(sOptionName)) { + filterData.put(sOptionName,Integer.toString(nValue)+"%"); + } + return nValue; + } + +} + + + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/helper/DialogBase.java b/source/java/org/openoffice/da/comp/w2lcommon/helper/DialogBase.java new file mode 100644 index 0000000..76a6dff --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/helper/DialogBase.java @@ -0,0 +1,503 @@ +/************************************************************************ + * + * DialogBase.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-11) + * + */ + +package org.openoffice.da.comp.w2lcommon.helper; + +import com.sun.star.awt.XControl; +import com.sun.star.awt.XControlContainer; +import com.sun.star.awt.XControlModel; +import com.sun.star.awt.XDialog; +import com.sun.star.awt.XDialogEventHandler; +import com.sun.star.awt.XDialogProvider2; +import com.sun.star.beans.PropertyVetoException; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.beans.XPropertySet; +import com.sun.star.lang.IllegalArgumentException; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XMultiComponentFactory; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XServiceName; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.ui.dialogs.ExecutableDialogResults; +import com.sun.star.ui.dialogs.XExecutableDialog; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + + +/** This class provides an abstract uno component which implements a dialog + * from an xml description (using the DialogProvider2 service) + */ +public abstract class DialogBase implements + XTypeProvider, XServiceInfo, XServiceName, // Uno component + XExecutableDialog, // Execute the dialog + XDialogEventHandler { // Event handling for dialog + + + ////////////////////////////////////////////////////////////////////////// + // The subclass must override the following; and override the + // implementation of XDialogEventHandler if needed + + /** The component will be registered under this name. + * The subclass must override this with a suitable name + */ + public static String __serviceName = ""; + + /** The component should also have an implementation name. + * The subclass must override this with a suitable name + */ + public static String __implementationName = ""; + + /** Return the name of the library containing the dialog + * The subclass must override this to provide the name of the library + */ + public abstract String getDialogLibraryName(); + + /** Return the name of the dialog within the library + * The subclass must override this to provide the name of the dialog + */ + public abstract String getDialogName(); + + /** Initialize the dialog (eg. with settings from the registry) + * The subclass must implement this + */ + protected abstract void initialize(); + + /** Finalize the dialog after execution (eg. save settings to the registry) + * The subclass must implement this + */ + protected abstract void finalize(); + + ////////////////////////////////////////////////////////////////////////// + // Some constants + + // State of a checkbox + protected static final short CHECKBOX_NOT_CHECKED = 0; + protected static final short CHECKBOX_CHECKED = 1; + protected static final short CHECKBOX_DONT_KNOW = 2; + + + ////////////////////////////////////////////////////////////////////////// + // Some private global variables + + // The component context (from constructor) + protected XComponentContext xContext; + + // The dialog (created by XExecutableDialog implementation) + private XDialog xDialog; + private String sTitle; + + + ////////////////////////////////////////////////////////////////////////// + // The constructor + + /** Create a new OptionsDialogBase */ + public DialogBase(XComponentContext xContext) { + this.xContext = xContext; + xDialog = null; + sTitle = null; + } + + + ////////////////////////////////////////////////////////////////////////// + // Implement uno interfaces + + // Implement the interface XTypeProvider + public Type[] getTypes() { + Type[] typeReturn = {}; + try { + typeReturn = new Type[] { + new Type( XServiceName.class ), + new Type( XServiceInfo.class ), + new Type( XTypeProvider.class ), + new Type( XExecutableDialog.class ), + new Type( XDialogEventHandler.class ) }; + } catch(Exception exception) { + } + return typeReturn; + } + + public byte[] getImplementationId() { + byte[] byteReturn = {}; + byteReturn = new String( "" + this.hashCode() ).getBytes(); + return( byteReturn ); + } + + + // Implement the interface XServiceName + public String getServiceName() { + return __serviceName; + } + + + // Implement the interface XServiceInfo + public boolean supportsService(String sServiceName) { + return sServiceName.equals(__serviceName); + } + + public String getImplementationName() { + return __implementationName; + } + + public String[] getSupportedServiceNames() { + String[] sSupportedServiceNames = { __serviceName }; + return sSupportedServiceNames; + } + + + // Implement the interface XExecutableDialog + public void setTitle(String sTitle) { + this.sTitle = sTitle; + } + + public short execute() { + try { + // Create the dialog + XMultiComponentFactory xMCF = xContext.getServiceManager(); + Object provider = xMCF.createInstanceWithContext( + "com.sun.star.awt.DialogProvider2", xContext); + XDialogProvider2 xDialogProvider = (XDialogProvider2) + UnoRuntime.queryInterface(XDialogProvider2.class, provider); + String sDialogUrl = "vnd.sun.star.script:"+getDialogLibraryName()+"." + +getDialogName()+"?location=application"; + xDialog = xDialogProvider.createDialogWithHandler(sDialogUrl, this); + if (sTitle!=null) { xDialog.setTitle(sTitle); } + + // Do initialization using method from subclass + initialize(); + + // Execute the dialog + short nResult = xDialog.execute(); + + if (nResult == ExecutableDialogResults.OK) { + // Finalize after execution of dialog using method from subclass + finalize(); + } + xDialog.endExecute(); + return nResult; + } + catch (Exception e) { +MessageBox msgBox = new MessageBox(xContext); +msgBox.showMessage("Error",e.toString()+" "+e.getStackTrace()[0].toString()); + + // continue as if the dialog was executed OK + return ExecutableDialogResults.OK; + } + } + + + // Implement the interface XDialogEventHandler + public boolean callHandlerMethod(XDialog xDialog, Object event, String sMethod) { + // Do nothing, leaving the responsibility to the subclass + return true; + } + + public String[] getSupportedMethodNames() { + // We do not support any method names, subclass should take care of this + return new String[0]; + } + + + ////////////////////////////////////////////////////////////////////////// + // Helpers to access controls in the dialog (to be used by the subclass) + // Note: The helpers fail silently if an exception occurs. Could query the + // the ClassId property for the control type and check that the property + // exists to ensure a correct behaviour in all cases, but as long as the + // helpers are used correctly, this doesn't really matter. + + // Get the properties of a named control in the dialog + private XPropertySet getControlProperties(String sControlName) { + XControlContainer xContainer = (XControlContainer) + UnoRuntime.queryInterface(XControlContainer.class, xDialog); + XControl xControl = xContainer.getControl(sControlName); + XControlModel xModel = xControl.getModel(); + XPropertySet xPropertySet = (XPropertySet) + UnoRuntime.queryInterface(XPropertySet.class, xModel); + return xPropertySet; + } + + + protected void setControlEnabled(String sControlName, boolean bEnabled) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("Enabled", new Boolean(bEnabled)); + } + catch (Exception e) { + // Will fail if the control does not exist + } + } + + protected short getCheckBoxState(String sControlName) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + return ((Short) xPropertySet.getPropertyValue("State")).shortValue(); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a checkbox + return CHECKBOX_DONT_KNOW; + } + } + + protected boolean getCheckBoxStateAsBoolean(String sControlName) { + return getCheckBoxState(sControlName)==CHECKBOX_CHECKED; + } + + protected void setCheckBoxState(String sControlName, short nState) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("State",new Short(nState)); + } + catch (Exception e) { + // will fail if the control does not exist or is not a checkbox or + // nState has an illegal value + } + } + + protected void setCheckBoxStateAsBoolean(String sControlName, boolean bChecked) { + setCheckBoxState(sControlName,bChecked ? CHECKBOX_CHECKED : CHECKBOX_NOT_CHECKED); + } + + protected String[] getListBoxStringItemList(String sControlName) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + return (String[]) xPropertySet.getPropertyValue("StringItemList"); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a list box + return new String[0]; + } + } + + protected void setListBoxStringItemList(String sControlName, String[] items) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("StringItemList",items); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a list box + } + } + + protected short getListBoxSelectedItem(String sControlName) { + // Returns the first selected element in case of a multiselection + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + short[] selection = (short[]) xPropertySet.getPropertyValue("SelectedItems"); + return selection[0]; + } + catch (Exception e) { + // Will fail if the control does not exist or is not a list box + return -1; + } + } + + protected void setListBoxSelectedItem(String sControlName, short nIndex) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + short[] selection = new short[1]; + selection[0] = nIndex; + xPropertySet.setPropertyValue("SelectedItems",selection); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a list box or + // nIndex is an illegal value + } + } + + protected short getListBoxLineCount(String sControlName) { + // Returns the first selected element in case of a multiselection + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + return ((Short) xPropertySet.getPropertyValue("LineCount")).shortValue(); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a list box + return 0; + } + } + + protected void setListBoxLineCount(String sControlName, short nLineCount) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("LineCount",new Short(nLineCount)); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a list box or + // nLineCount is an illegal value + } + } + + protected String getComboBoxText(String sControlName) { + // Returns the text of a combobox + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + return (String) xPropertySet.getPropertyValue("Text"); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a combo + return ""; + } + } + + protected void setComboBoxText(String sControlName, String sText) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("Text", sText); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a combo box or + // nText is an illegal value + } + } + + protected String getTextFieldText(String sControlName) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + return (String) xPropertySet.getPropertyValue("Text"); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a text field + return ""; + } + } + + protected void setTextFieldText(String sControlName, String sText) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("Text",sText); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a text field + } + } + + protected String getFormattedFieldText(String sControlName) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + return (String) xPropertySet.getPropertyValue("Text"); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a formatted field + return ""; + } + } + + protected void setFormattedFieldText(String sControlName, String sText) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("Text",sText); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a formatted field + } + } + + protected int getNumericFieldValue(String sControlName) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + return ((Double) xPropertySet.getPropertyValue("Value")).intValue(); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a numeric field + return 0; + } + } + + protected void setNumericFieldValue(String sControlName, int nValue) { + XPropertySet xPropertySet = getControlProperties(sControlName); + try { + xPropertySet.setPropertyValue("Value",new Double(nValue)); + } + catch (Exception e) { + // Will fail if the control does not exist or is not a numeric field + } + } + + // Helpers for access to an XPropertySet. The helpers will fail silently if + // names or data is provided, but the subclass is expected to use them with + // correct data only... + protected Object getPropertyValue(XPropertySet xProps, String sName) { + try { + return xProps.getPropertyValue(sName); + } + catch (UnknownPropertyException e) { + return null; + } + catch (WrappedTargetException e) { + return null; + } + } + + protected void setPropertyValue(XPropertySet xProps, String sName, Object value) { + try { + xProps.setPropertyValue(sName,value); + } + catch (UnknownPropertyException e) { + } + catch (PropertyVetoException e) { // unacceptable value + } + catch (IllegalArgumentException e) { + } + catch (WrappedTargetException e) { + } + } + + protected String getPropertyValueAsString(XPropertySet xProps, String sName) { + Object value = getPropertyValue(xProps,sName); + return value instanceof String ? (String) value : ""; + } + + protected int getPropertyValueAsInteger(XPropertySet xProps, String sName) { + Object value = getPropertyValue(xProps,sName); + return value instanceof Integer ? ((Integer) value).intValue() : 0; + } + + protected void setPropertyValue(XPropertySet xProps, String sName, int nValue) { + setPropertyValue(xProps,sName,new Integer(nValue)); + } + + protected short getPropertyValueAsShort(XPropertySet xProps, String sName) { + Object value = getPropertyValue(xProps,sName); + return value instanceof Short ? ((Short) value).shortValue() : 0; + } + + protected void setPropertyValue(XPropertySet xProps, String sName, short nValue) { + setPropertyValue(xProps,sName,new Short(nValue)); + } + + protected boolean getPropertyValueAsBoolean(XPropertySet xProps, String sName) { + Object value = getPropertyValue(xProps,sName); + return value instanceof Boolean ? ((Boolean) value).booleanValue() : false; + } + + protected void setPropertyValue(XPropertySet xProps, String sName, boolean bValue) { + setPropertyValue(xProps,sName,new Boolean(bValue)); + } + +} + + + diff --git a/source/java/org/openoffice/da/comp/w2lcommon/helper/MessageBox.java b/source/java/org/openoffice/da/comp/w2lcommon/helper/MessageBox.java new file mode 100644 index 0000000..d930b83 --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/helper/MessageBox.java @@ -0,0 +1,105 @@ +/************************************************************************ + * + * MessageBox.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-07-21) + * + */ + +package org.openoffice.da.comp.w2lcommon.helper; + +import com.sun.star.awt.Rectangle; +import com.sun.star.awt.WindowAttribute; +import com.sun.star.awt.WindowClass; +import com.sun.star.awt.WindowDescriptor; +import com.sun.star.awt.XMessageBox; +import com.sun.star.awt.XToolkit; +import com.sun.star.awt.XWindowPeer; +import com.sun.star.frame.XDesktop; +import com.sun.star.frame.XFrame; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +/** This class provides simple access to a uno awt message box + */ +public class MessageBox { + + private XFrame xFrame; + private XToolkit xToolkit; + + /** Create a new MessageBox belonging to the current frame + */ + public MessageBox(XComponentContext xContext) { + this(xContext,null); + } + + /** Create a new MessageBox belonging to a specific frame + */ + public MessageBox(XComponentContext xContext, XFrame xFrame) { + try { + Object toolkit = xContext.getServiceManager() + .createInstanceWithContext("com.sun.star.awt.Toolkit",xContext); + xToolkit = (XToolkit) UnoRuntime.queryInterface(XToolkit.class,toolkit); + if (xFrame==null) { + Object desktop = xContext.getServiceManager() + .createInstanceWithContext("com.sun.star.frame.Desktop",xContext); + XDesktop xDesktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class,desktop); + xFrame = xDesktop.getCurrentFrame(); + } + this.xFrame = xFrame; + } + catch (Exception e) { + // Failed to get toolkit or frame + xToolkit = null; + xFrame = null; + } + } + + + public void showMessage(String sTitle, String sMessage) { + if (xToolkit==null || xFrame==null) { return; } + try { + WindowDescriptor descriptor = new WindowDescriptor(); + descriptor.Type = WindowClass.MODALTOP; + descriptor.WindowServiceName = "infobox"; + descriptor.ParentIndex = -1; + descriptor.Parent = (XWindowPeer) UnoRuntime.queryInterface( + XWindowPeer.class,xFrame.getContainerWindow()); + descriptor.Bounds = new Rectangle(0,0,300,200); + descriptor.WindowAttributes = WindowAttribute.BORDER | + WindowAttribute.MOVEABLE | WindowAttribute.CLOSEABLE; + XWindowPeer xPeer = xToolkit.createWindow(descriptor); + if (xPeer!=null) { + XMessageBox xMessageBox = (XMessageBox) + UnoRuntime.queryInterface(XMessageBox.class,xPeer); + if (xMessageBox!=null) { + xMessageBox.setCaptionText(sTitle); + xMessageBox.setMessageText(sMessage); + xMessageBox.execute(); + } + } + } + catch (Exception e) { + // ignore, give up... + } + } + +} diff --git a/source/java/org/openoffice/da/comp/w2lcommon/helper/PropertyHelper.java b/source/java/org/openoffice/da/comp/w2lcommon/helper/PropertyHelper.java new file mode 100644 index 0000000..647f37c --- /dev/null +++ b/source/java/org/openoffice/da/comp/w2lcommon/helper/PropertyHelper.java @@ -0,0 +1,78 @@ +/************************************************************************ + * + * PropertyHelper.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-07-21) + * + */ + +package org.openoffice.da.comp.w2lcommon.helper; + +import java.util.Enumeration; +import java.util.Hashtable; + +import com.sun.star.beans.PropertyValue; + +/** This class provides access by name to a <code>PropertyValue</code> array + */ +public class PropertyHelper { + + private Hashtable data; + + public PropertyHelper() { + data = new Hashtable(); + } + + public PropertyHelper(PropertyValue[] props) { + data = new Hashtable(); + int nLen = props.length; + for (int i=0; i<nLen; i++) { + data.put(props[i].Name,props[i].Value); + } + } + + public void put(String sName, Object value) { + data.put(sName,value); + } + + public Object get(String sName) { + return data.get(sName); + } + + public Enumeration keys() { + return data.keys(); + } + + public PropertyValue[] toArray() { + int nSize = data.size(); + PropertyValue[] props = new PropertyValue[nSize]; + int i=0; + Enumeration keys = keys(); + while (keys.hasMoreElements()) { + String sKey = (String) keys.nextElement(); + props[i] = new PropertyValue(); + props[i].Name = sKey; + props[i++].Value = get(sKey); + } + return props; + } + +} diff --git a/source/java/org/openoffice/da/comp/writer2latex/LaTeXOptionsDialog.java b/source/java/org/openoffice/da/comp/writer2latex/LaTeXOptionsDialog.java new file mode 100644 index 0000000..dc39e5e --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2latex/LaTeXOptionsDialog.java @@ -0,0 +1,351 @@ +/************************************************************************ + * + * LaTeXOptionsDialog.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-18) + * + */ + +package org.openoffice.da.comp.writer2latex; + +import com.sun.star.awt.XDialog; +import com.sun.star.beans.XPropertySet; +//import com.sun.star.frame.XDesktop; +//import com.sun.star.lang.XComponent; +//import com.sun.star.text.XTextFieldsSupplier; +//import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +import org.openoffice.da.comp.w2lcommon.helper.PropertyHelper; +import org.openoffice.da.comp.w2lcommon.filter.OptionsDialogBase; + +/** This class provides a uno component which implements a filter ui for the + * LaTeX export + */ +public class LaTeXOptionsDialog extends OptionsDialogBase { + + // Translate list box items to configuration option values + private static final String[] BACKEND_VALUES = + { "generic", "pdftex", "dvips", "unspecified" }; + private static final String[] INPUTENCODING_VALUES = + { "ascii", "latin1", "latin2", "iso-8859-7", "cp1250", "cp1251", "koi8-r", "utf8" }; + private static final String[] NOTES_VALUES = + { "ignore", "comment", "marginpar", "pdfannotation" }; + private static final String[] FLOATOPTIONS_VALUES = + { "", "tp", "bp", "htp", "hbp" }; + + /** The component will be registered under this name. + */ + public static String __serviceName = "org.openoffice.da.writer2latex.LaTeXOptionsDialog"; + + /** The component should also have an implementation name. + * The subclass should override this with a suitable name + */ + public static String __implementationName = "org.openoffice.da.comp.writer2latex.LaTeXOptionsDialog"; + + public String getDialogLibraryName() { return "W2LDialogs"; } + + + /** Create a new LaTeXOptionsDialog */ + public LaTeXOptionsDialog(XComponentContext xContext) { + super(xContext); + xMSF = W2LRegistration.xMultiServiceFactory; + } + + /** Return the name of the dialog within the library + */ + public String getDialogName() { return "LaTeXOptions"; } + + /** Return the name of the registry path + */ + public String getRegistryPath() { + return "/org.openoffice.da.Writer2LaTeX.Options/LaTeXOptions"; + } + + /** Load settings from the registry to the dialog */ + protected void loadSettings(XPropertySet xProps) { + // General + loadConfig(xProps); + loadListBoxOption(xProps,"Backend"); + loadListBoxOption(xProps,"Inputencoding"); + loadCheckBoxOption(xProps,"Multilingual"); + loadCheckBoxOption(xProps,"GreekMath"); + loadCheckBoxOption(xProps,"AdditionalSymbols"); + + // Bibliography + loadCheckBoxOption(xProps,"UseBibtex"); + loadComboBoxOption(xProps,"BibtexStyle"); + + // Files + loadCheckBoxOption(xProps,"WrapLines"); + loadNumericOption(xProps,"WrapLinesAfter"); + loadCheckBoxOption(xProps,"SplitLinkedSections"); + loadCheckBoxOption(xProps,"SplitToplevelSections"); + loadCheckBoxOption(xProps,"SaveImagesInSubdir"); + + // Special content + loadListBoxOption(xProps,"Notes"); + loadCheckBoxOption(xProps,"Metadata"); + + // Figures and tables + loadCheckBoxOption(xProps,"OriginalImageSize"); + loadCheckBoxOption(xProps,"OptimizeSimpleTables"); + loadNumericOption(xProps,"SimpleTableLimit"); + loadCheckBoxOption(xProps,"FloatTables"); + loadCheckBoxOption(xProps,"FloatFigures"); + loadListBoxOption(xProps,"FloatOptions"); + + // AutoCorrect + loadCheckBoxOption(xProps,"IgnoreHardPageBreaks"); + loadCheckBoxOption(xProps,"IgnoreHardLineBreaks"); + loadCheckBoxOption(xProps,"IgnoreEmptyParagraphs"); + loadCheckBoxOption(xProps,"IgnoreDoubleSpaces"); + + updateLockedOptions(); + enableControls(); + } + + /** Save settings from the dialog to the registry and create FilterData */ + protected void saveSettings(XPropertySet xProps, PropertyHelper filterData) { + // General + short nConfig = saveConfig(xProps, filterData); + switch (nConfig) { + case 0: filterData.put("ConfigURL","*ultraclean.xml"); break; + case 1: filterData.put("ConfigURL","*clean.xml"); break; + case 2: filterData.put("ConfigURL","*default.xml"); break; + case 3: filterData.put("ConfigURL","*pdfprint.xml"); break; + case 4: filterData.put("ConfigURL","*pdfscreen.xml"); break; + case 5: filterData.put("ConfigURL","$(user)/writer2latex.xml"); + filterData.put("AutoCreate","true"); + } + + saveListBoxOption(xProps, filterData, "Backend", "backend", BACKEND_VALUES ); + if (getListBoxSelectedItem("Config")==4) { + // pdfscreen locks the backend to pdftex + filterData.put("backend","pdftex"); + } + saveListBoxOption(xProps, filterData, "Inputencoding", "inputencoding", INPUTENCODING_VALUES); + saveCheckBoxOption(xProps, filterData, "Multilingual", "multilingual"); + saveCheckBoxOption(xProps, filterData, "GreekMath", "greek_math"); + // AdditionalSymbols sets 5 different w2l options... + saveCheckBoxOption(xProps, filterData, "AdditionalSymbols", "use_pifont"); + saveCheckBoxOption(xProps, filterData, "AdditionalSymbols", "use_ifsym"); + saveCheckBoxOption(xProps, filterData, "AdditionalSymbols", "use_wasysym"); + saveCheckBoxOption(xProps, filterData, "AdditionalSymbols", "use_eurosym"); + saveCheckBoxOption(xProps, filterData, "AdditionalSymbols", "use_tipa"); + + // Bibliography + saveCheckBoxOption(xProps, filterData, "UseBibtex", "use_bibtex"); + saveComboBoxOption(xProps, filterData, "BibtexStyle", "bibtex_style"); + + // Files + boolean bWrapLines = saveCheckBoxOption(xProps, "WrapLines"); + int nWrapLinesAfter = saveNumericOption(xProps, "WrapLinesAfter"); + if (!isLocked("wrap_lines_after")) { + if (bWrapLines) { + filterData.put("wrap_lines_after",Integer.toString(nWrapLinesAfter)); + } + else { + filterData.put("wrap_lines_after","0"); + } + } + + saveCheckBoxOption(xProps, filterData, "SplitLinkedSections", "split_linked_sections"); + saveCheckBoxOption(xProps, filterData, "SplitToplevelSections", "split_toplevel_sections"); + saveCheckBoxOption(xProps, filterData, "SaveImagesInSubdir", "save_images_in_subdir"); + + // Special content + saveListBoxOption(xProps, filterData, "Notes", "notes", NOTES_VALUES); + saveCheckBoxOption(xProps, filterData, "Metadata", "metadata"); + + // Figures and tables + saveCheckBoxOption(xProps, filterData, "OriginalImageSize", "original_image_size"); + + boolean bOptimizeSimpleTables = saveCheckBoxOption(xProps,"OptimizeSimpleTables"); + int nSimpleTableLimit = saveNumericOption(xProps,"SimpleTableLimit"); + if (!isLocked("simple_table_limit")) { + if (bOptimizeSimpleTables) { + filterData.put("simple_table_limit",Integer.toString(nSimpleTableLimit)); + } + else { + filterData.put("simple_table_limit","0"); + } + } + + saveCheckBoxOption(xProps, filterData, "FloatTables", "float_tables"); + saveCheckBoxOption(xProps, filterData, "FloatFigures", "float_figures"); + saveListBoxOption(xProps, filterData, "FloatOptions", "float_options", FLOATOPTIONS_VALUES); + + // AutoCorrect + saveCheckBoxOption(xProps, filterData, "IgnoreHardPageBreaks", "ignore_hard_page_breaks"); + saveCheckBoxOption(xProps, filterData, "IgnoreHardLineBreaks", "ignore_hard_line_breaks"); + saveCheckBoxOption(xProps, filterData, "IgnoreEmptyParagraphs", "ignore_empty_paragraphs"); + saveCheckBoxOption(xProps, filterData, "IgnoreDoubleSpaces", "ignore_double_spaces"); + } + + + // Implement XDialogEventHandler + public boolean callHandlerMethod(XDialog xDialog, Object event, String sMethod) { + if (sMethod.equals("ConfigChange")) { + updateLockedOptions(); + enableControls(); + } + else if (sMethod.equals("UseBibtexChange")) { + enableBibtexStyle(); + } + else if (sMethod.equals("WrapLinesChange")) { + enableWrapLinesAfter(); + } + else if (sMethod.equals("OptimizeSimpleTablesChange")) { + enableSimpleTableLimit(); + } + else if (sMethod.equals("FloatTablesChange")) { + enableFloatOptions(); + } + else if (sMethod.equals("FloatFiguresChange")) { + enableFloatOptions(); + } + return true; + } + + public String[] getSupportedMethodNames() { + String[] sNames = { "ConfigChange", "UseBibtexChange", "WrapLinesChange", + "OptimizeSimpleTablesChange", "FloatTablesChange", "FloatFiguresChange" }; + return sNames; + } + + protected boolean isLocked(String sOptionName) { + if ("backend".equals(sOptionName)) { + // backend must be pdf for pdfscreen + return getListBoxSelectedItem("Config")==4 || super.isLocked(sOptionName); + } + else if ("additional_symbols".equals(sOptionName)) { + // additional_symbols is disabled for custom config (where the 5 + // individual options can be set independently) + return getListBoxSelectedItem("Config")==5 || super.isLocked(sOptionName); + } + else if ("use_pifont".equals(sOptionName)) { + return isLocked("additional_symbols"); + } + else if ("use_ifsym".equals(sOptionName)) { + return isLocked("additional_symbols"); + } + else if ("use_wasysym".equals(sOptionName)) { + return isLocked("additional_symbols"); + } + else if ("use_eurosym".equals(sOptionName)) { + return isLocked("additional_symbols"); + } + else if ("use_tipa".equals(sOptionName)) { + return isLocked("additional_symbols"); + } + else { + return super.isLocked(sOptionName); + } + } + + private void enableControls() { + // General + setControlEnabled("BackendLabel",!isLocked("backend")); + setControlEnabled("Backend",!isLocked("backend")); + setControlEnabled("InputencodingLabel",!isLocked("inputencoding")); + setControlEnabled("Inputencoding",!isLocked("inputencoding")); + setControlEnabled("Multilingual",!isLocked("multilingual")); + setControlEnabled("GreekMath",!isLocked("greek_math")); + setControlEnabled("AdditionalSymbols",!isLocked("additional_symbols")); + + // Bibliography + setControlEnabled("UseBibtex",!isLocked("use_bibtex")); + boolean bUseBibtex = getCheckBoxStateAsBoolean("UseBibtex"); + setControlEnabled("BibtexStyleLabel",!isLocked("bibtex_style") && bUseBibtex); + setControlEnabled("BibtexStyle",!isLocked("bibtex_style") && bUseBibtex); + + // Files + setControlEnabled("WrapLines",!isLocked("wrap_lines_after")); + boolean bWrapLines = getCheckBoxStateAsBoolean("WrapLines"); + setControlEnabled("WrapLinesAfterLabel",!isLocked("wrap_lines_after") && bWrapLines); + setControlEnabled("WrapLinesAfter",!isLocked("wrap_lines_after") && bWrapLines); + setControlEnabled("SplitLinkedSections",!isLocked("split_linked_sections")); + setControlEnabled("SplitToplevelSections",!isLocked("split_toplevel_sections")); + setControlEnabled("SaveImagesInSubdir",!isLocked("save_images_in_subdir")); + + // Special content + setControlEnabled("NotesLabel",!isLocked("notes")); + setControlEnabled("Notes",!isLocked("notes")); + setControlEnabled("Metadata",!isLocked("metadata")); + + // Figures and tables + setControlEnabled("OriginalImageSize",!isLocked("original_image_size")); + setControlEnabled("OptimizeSimpleTables",!isLocked("simple_table_limit")); + boolean bOptimizeSimpleTables = getCheckBoxStateAsBoolean("OptimizeSimpleTables"); + setControlEnabled("SimpleTableLimitLabel",!isLocked("simple_table_limit") && bOptimizeSimpleTables); + setControlEnabled("SimpleTableLimit",!isLocked("simple_table_limit") && bOptimizeSimpleTables); + setControlEnabled("FloatTables",!isLocked("float_tables")); + setControlEnabled("FloatFigures",!isLocked("float_figures")); + boolean bFloat = getCheckBoxStateAsBoolean("FloatFigures") || + getCheckBoxStateAsBoolean("FloatTables"); + setControlEnabled("FloatOptionsLabel",!isLocked("float_options") && bFloat); + setControlEnabled("FloatOptions",!isLocked("float_options") && bFloat); + + // AutoCorrect + setControlEnabled("IgnoreHardPageBreaks",!isLocked("ignore_hard_page_breaks")); + setControlEnabled("IgnoreHardLineBreaks",!isLocked("ignore_hard_line_breaks")); + setControlEnabled("IgnoreEmptyParagraphs",!isLocked("ignore_empty_paragraphs")); + setControlEnabled("IgnoreDoubleSpaces",!isLocked("ignore_double_spaces")); + } + + private void enableBibtexStyle() { + if (!isLocked("bibtex_style")) { + boolean bState = getCheckBoxStateAsBoolean("UseBibtex"); + setControlEnabled("BibtexStyleLabel",bState); + setControlEnabled("BibtexStyle",bState); + } + } + + private void enableWrapLinesAfter() { + if (!isLocked("wrap_lines_after")) { + boolean bState = getCheckBoxStateAsBoolean("WrapLines"); + setControlEnabled("WrapLinesAfterLabel",bState); + setControlEnabled("WrapLinesAfter",bState); + } + } + + private void enableSimpleTableLimit() { + if (!isLocked("simple_table_limit")) { + boolean bState = getCheckBoxStateAsBoolean("OptimizeSimpleTables"); + setControlEnabled("SimpleTableLimitLabel",bState); + setControlEnabled("SimpleTableLimit",bState); + } + } + + private void enableFloatOptions() { + if (!isLocked("float_options")) { + boolean bState = getCheckBoxStateAsBoolean("FloatFigures") || + getCheckBoxStateAsBoolean("FloatTables"); + setControlEnabled("FloatOptionsLabel",bState); + setControlEnabled("FloatOptions",bState); + } + } + +} + + + diff --git a/source/java/org/openoffice/da/comp/writer2latex/W2LExportFilter.java b/source/java/org/openoffice/da/comp/writer2latex/W2LExportFilter.java new file mode 100644 index 0000000..330eaea --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2latex/W2LExportFilter.java @@ -0,0 +1,56 @@ +/************************************************************************ + * + * W2LExportFilter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-07-21) + * + */ + +package org.openoffice.da.comp.writer2latex; + +import com.sun.star.uno.XComponentContext; + +import org.openoffice.da.comp.w2lcommon.filter.ExportFilterBase; + + +/** This class implements the LaTeX and BibTeX export filter component + */ +public class W2LExportFilter extends ExportFilterBase { + + /** Service name for the component */ + public static final String __serviceName = "org.openoffice.da.comp.writer2latex.W2LExportFilter"; + + /** Implementation name for the component */ + public static final String __implementationName = "org.openoffice.da.comp.writer2latex.W2LExportFilter"; + + /** Filter name to include in error messages */ + public static final String __displayName = "Writer2LaTeX"; + + public W2LExportFilter(XComponentContext xComponentContext1) { + super(xComponentContext1); + xMSF = W2LRegistration.xMultiServiceFactory; + } + + +} + + + diff --git a/source/java/org/openoffice/da/comp/writer2latex/W2LRegistration.java b/source/java/org/openoffice/da/comp/writer2latex/W2LRegistration.java new file mode 100644 index 0000000..c67b7d7 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2latex/W2LRegistration.java @@ -0,0 +1,103 @@ +/************************************************************************ + * + * W2LRegistration.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-07-21) + * + */ + +package org.openoffice.da.comp.writer2latex; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.comp.loader.FactoryHelper; + +/** This class provides a static method to instantiate our uno components + * on demand (__getServiceFactory()), and a static method to give + * information about the components (__writeRegistryServiceInfo()). + * Furthermore, it saves the XMultiServiceFactory provided to the + * __getServiceFactory method for future reference by the componentes. + */ +public class W2LRegistration { + + public static XMultiServiceFactory xMultiServiceFactory; + + /** + * Returns a factory for creating the service. + * This method is called by the <code>JavaLoader</code> + * + * @return returns a <code>XSingleServiceFactory</code> for creating the + * component + * + * @param implName the name of the implementation for which a + * service is desired + * @param multiFactory the service manager to be used if needed + * @param regKey the registryKey + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, XRegistryKey regKey) { + xMultiServiceFactory = multiFactory; + XSingleServiceFactory xSingleServiceFactory = null; + if (implName.equals(W2LExportFilter.class.getName()) ) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(W2LExportFilter.class, + W2LExportFilter.__serviceName, + multiFactory, + regKey); + } + else if (implName.equals(LaTeXOptionsDialog.__implementationName)) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(LaTeXOptionsDialog.class, + LaTeXOptionsDialog.__serviceName, + multiFactory, + regKey); + } + else if (implName.equals(W2LStarMathConverter.__implementationName)) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(W2LStarMathConverter.class, + W2LStarMathConverter.__serviceName, + multiFactory, + regKey); + } + + return xSingleServiceFactory; + } + + /** + * Writes the service information into the given registry key. + * This method is called by the <code>JavaLoader</code> + * <p> + * @return returns true if the operation succeeded + * @param regKey the registryKey + * @see com.sun.star.comp.loader.JavaLoader + */ + public static boolean __writeRegistryServiceInfo(XRegistryKey regKey) { + return + FactoryHelper.writeRegistryServiceInfo(W2LExportFilter.__implementationName, + W2LExportFilter.__serviceName, regKey) & + FactoryHelper.writeRegistryServiceInfo(LaTeXOptionsDialog.__implementationName, + LaTeXOptionsDialog.__serviceName, regKey) & + FactoryHelper.writeRegistryServiceInfo(W2LStarMathConverter.__implementationName, + W2LStarMathConverter.__serviceName, regKey); + } +} + diff --git a/source/java/org/openoffice/da/comp/writer2latex/W2LStarMathConverter.java b/source/java/org/openoffice/da/comp/writer2latex/W2LStarMathConverter.java new file mode 100644 index 0000000..3b720a7 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2latex/W2LStarMathConverter.java @@ -0,0 +1,125 @@ +/************************************************************************ + * + * W2LStarMathConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + */ + +// Version 1.0 (2008-11-22) + +package org.openoffice.da.comp.writer2latex; + +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.uno.Type; +//import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; +import com.sun.star.lang.XServiceName; + +import writer2latex.api.ConverterFactory; +import writer2latex.api.StarMathConverter; + +// Import interface as defined in uno idl +import org.openoffice.da.writer2latex.XW2LStarMathConverter; + +/** This class provides a uno component which implements the interface + * org.openoffice.da.writer2latex.XW2LConverter + */ +public class W2LStarMathConverter implements + XW2LStarMathConverter, + XServiceName, + XServiceInfo, + XTypeProvider { + + /** The component will be registered under this name. + */ + public static final String __serviceName = "org.openoffice.da.writer2latex.W2LStarMathConverter"; + + public static final String __implementationName = "org.openoffice.da.comp.writer2latex.W2LStarMathConverter"; + + //private static XComponentContext xComponentContext = null; + private static StarMathConverter starMathConverter; + + public W2LStarMathConverter(XComponentContext xComponentContext1) { + starMathConverter = ConverterFactory.createStarMathConverter(); + } + + // Implementation of XW2LConverter: + public String convertFormula(String sStarMathFormula) { + return starMathConverter.convert(sStarMathFormula); + } + + public String getPreamble() { + return starMathConverter.getPreamble(); + } + + + // Implement methods from interface XTypeProvider + // Implementation of XTypeProvider + + public com.sun.star.uno.Type[] getTypes() { + Type[] typeReturn = {}; + + try { + typeReturn = new Type[] { + new Type( XW2LStarMathConverter.class ), + new Type( XTypeProvider.class ), + new Type( XServiceName.class ), + new Type( XServiceInfo.class ) }; + } + catch( Exception exception ) { + + } + + return( typeReturn ); + } + + + public byte[] getImplementationId() { + byte[] byteReturn = {}; + + byteReturn = new String( "" + this.hashCode() ).getBytes(); + + return( byteReturn ); + } + + // Implement method from interface XServiceName + public String getServiceName() { + return( __serviceName ); + } + + // Implement methods from interface XServiceInfo + public boolean supportsService(String stringServiceName) { + return( stringServiceName.equals( __serviceName ) ); + } + + public String getImplementationName() { + return( __implementationName ); + } + + public String[] getSupportedServiceNames() { + String[] stringSupportedServiceNames = { __serviceName }; + return( stringSupportedServiceNames ); + } + + +} + + + diff --git a/source/java/org/openoffice/da/comp/writer2xhtml/BatchConverter.java b/source/java/org/openoffice/da/comp/writer2xhtml/BatchConverter.java new file mode 100644 index 0000000..b150190 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2xhtml/BatchConverter.java @@ -0,0 +1,541 @@ +/************************************************************************ + * + * BatchConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-16) + * + */ + +package org.openoffice.da.comp.writer2xhtml; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; + +import com.sun.star.lib.uno.adapter.XInputStreamToInputStreamAdapter; +import com.sun.star.lib.uno.adapter.XOutputStreamToOutputStreamAdapter; +import com.sun.star.beans.PropertyValue; +import com.sun.star.beans.UnknownPropertyException; +import com.sun.star.beans.XPropertySet; +import com.sun.star.document.XDocumentInfoSupplier; +import com.sun.star.frame.XComponentLoader; +import com.sun.star.frame.XStorable; +import com.sun.star.io.NotConnectedException; +import com.sun.star.io.XInputStream; +import com.sun.star.io.XOutputStream; +import com.sun.star.lang.WrappedTargetException; +import com.sun.star.lang.XComponent; +import com.sun.star.lang.XServiceInfo; +import com.sun.star.lang.XServiceName; +import com.sun.star.lang.XTypeProvider; +import com.sun.star.sheet.XSpreadsheetDocument; +import com.sun.star.text.XTextDocument; +import com.sun.star.ucb.CommandAbortedException; +import com.sun.star.ucb.XSimpleFileAccess2; +import com.sun.star.uno.AnyConverter; +import com.sun.star.uno.Type; +import com.sun.star.uno.UnoRuntime; +import com.sun.star.uno.XComponentContext; + +//import writer2latex.api.BatchConverter; +//import writer2latex.api.BatchHandler; +//import writer2latex.api.Converter; +import writer2latex.api.ConverterFactory; +import writer2latex.api.IndexPageEntry; +import writer2latex.api.MIMETypes; +import writer2latex.api.OutputFile; + +// Import interfaces as defined in uno idl +import org.openoffice.da.writer2xhtml.XBatchConverter; +import org.openoffice.da.writer2xhtml.XBatchHandler; +import org.openoffice.da.comp.w2lcommon.helper.PropertyHelper; + +/** This class provides a uno component which implements the interface + * org.openoffice.da.writer2xhtml.XBatchConverter + */ +public class BatchConverter implements + XBatchConverter, + XServiceName, + XServiceInfo, + XTypeProvider { + + /** The component will be registered under this name. + */ + public static final String __serviceName = "org.openoffice.da.writer2xhtml.BatchConverter"; + + public static final String __implementationName = "org.openoffice.da.comp.writer2xhtml.BatchConverter"; + + private XComponentContext xComponentContext = null; + + private XSimpleFileAccess2 sfa2 = null; + private writer2latex.api.BatchConverter batchConverter = null; + + private XBatchHandler handler; + + // Based on convert arguments + private boolean bRecurse = true; + private String sWriterFilterName = "org.openoffice.da.writer2xhtml"; + private Object writerFilterData = null; + private String sCalcFilterName = "org.openoffice.da.calc2xhtml"; + private Object calcFilterData = null; + private boolean bIncludePdf = true; + private boolean bIncludeOriginal = false; + private boolean bUseTitle = true; + private boolean bUseDescription = true; + private String sUplink = ""; + private String sDirectoryIcon = ""; + private String sDocumentIcon = ""; + private String sTemplateURL = null; + + public BatchConverter(XComponentContext xComponentContext) { + this.xComponentContext = xComponentContext; + // Get the SimpleFileAccess service + try { + Object sfaObject = xComponentContext.getServiceManager().createInstanceWithContext( + "com.sun.star.ucb.SimpleFileAccess", xComponentContext); + sfa2 = (XSimpleFileAccess2) UnoRuntime.queryInterface(XSimpleFileAccess2.class, sfaObject); + } + catch (com.sun.star.uno.Exception e) { + // failed to get SimpleFileAccess service (should not happen) + } + } + + // Helper: Get a string from an any + private String getValue(Object any) { + if (AnyConverter.isString(any)) { + try { + return AnyConverter.toString(any); + } + catch (com.sun.star.lang.IllegalArgumentException e) { + return ""; + } + } + else { + return ""; + } + } + + // Implementation of XBatchConverter: + public void convert(String sSourceURL, String sTargetURL, PropertyValue[] lArguments, XBatchHandler handler) { + // Create batch converter (currently we don't need to set a converter) + batchConverter = ConverterFactory.createBatchConverter(MIMETypes.XHTML); + + this.handler = handler; + + // Read the arguments + int nSize = lArguments.length; + for (int i=0; i<nSize; i++) { + String sName = lArguments[i].Name; + Object value = lArguments[i].Value; + if ("Recurse".equals(sName)) { + bRecurse = !"false".equals(getValue(value)); + } + else if ("IncludePdf".equals(sName)) { + bIncludePdf = !"false".equals(getValue(value)); + } + else if ("IncludeOriginal".equals(sName)) { + bIncludeOriginal = "true".equals(getValue(value)); + + } + else if ("UseTitle".equals(sName)) { + bUseTitle = !"false".equals(getValue(value)); + } + else if ("UseDescription".equals(sName)) { + bUseDescription = !"false".equals(getValue(value)); + } + else if ("Uplink".equals(sName)) { + sUplink = getValue(value); + } + else if ("DirectoryIcon".equals(sName)) { + sDirectoryIcon = getValue(value); + } + else if ("DocumentIcon".equals(sName)) { + sDocumentIcon = getValue(value); + } + else if ("TemplateURL".equals(sName)) { + sTemplateURL = getValue(value); + } + else if ("WriterFilterName".equals(sName)) { + sWriterFilterName = getValue(value); + } + else if ("WriterFilterData".equals(sName)) { + writerFilterData = lArguments[i].Value; + } + else if ("CalcFilterName".equals(sName)) { + sCalcFilterName = getValue(value); + } + else if ("CalcFilterData".equals(sName)) { + calcFilterData = lArguments[i].Value; + } + } + + // Set arguments on batch converter + batchConverter.getConfig().setOption("uplink", sUplink); + batchConverter.getConfig().setOption("directory_icon", sDirectoryIcon); + batchConverter.getConfig().setOption("document_icon", sDocumentIcon); + if (sTemplateURL!=null) { + try { + XInputStream xis = sfa2.openFileRead(sTemplateURL); + XInputStreamToInputStreamAdapter isa = new XInputStreamToInputStreamAdapter(xis); + batchConverter.readTemplate(isa); + } + catch (IOException e) { + // The batchconverter failed to read the template + } + catch (CommandAbortedException e) { + // The sfa could not execute the command + } + catch (com.sun.star.uno.Exception e) { + // Unspecified uno exception + } + } + + // Convert the directory + handler.startConversion(); + convertDirectory(sSourceURL, sTargetURL, getName(sSourceURL)); + handler.endConversion(); + } + + // Convert a directory - return true if not cancelled + private boolean convertDirectory(String sSourceURL, String sTargetURL, String sHeading) { + handler.startDirectory(sSourceURL); + + // Step 1: Get the directory + String[] contents; + try { + contents = sfa2.getFolderContents(sSourceURL, true); + } + catch (CommandAbortedException e) { + handler.endDirectory(sSourceURL,false); + return true; + } + catch (com.sun.star.uno.Exception e) { + handler.endDirectory(sSourceURL,false); + return true; + } + int nLen = contents.length; + IndexPageEntry[] entries = new IndexPageEntry[nLen]; + + // Step 2: Traverse subdirectories, if allowed + if (bRecurse) { + String sUplink = batchConverter.getConfig().getOption("uplink"); + for (int i=0; i<nLen; i++) { + boolean bIsDirectory = false; + try { + bIsDirectory = sfa2.isFolder(contents[i]); + } + catch (CommandAbortedException e) { + // Considered non critical, ignore + } + catch (com.sun.star.uno.Exception e) { + // Considered non critical, ignore + } + if (bIsDirectory) { + batchConverter.getConfig().setOption("uplink","../index.html"); + String sNewTargetURL = ensureSlash(sTargetURL) + getName(contents[i]); + String sNewHeading = sHeading + " - " + decodeURL(getName(contents[i])); + boolean bResult = convertDirectory(contents[i],sNewTargetURL,sNewHeading); + batchConverter.getConfig().setOption("uplink", sUplink); + if (!bResult) { return false; } + // Create entry for this subdirectory + IndexPageEntry entry = new IndexPageEntry(ensureSlash(sNewTargetURL)+"index.html",true); + entry.setDisplayName(decodeURL(getName(contents[i]))); + entries[i]=entry; + } + } + } + + // Step 3: Traverse documents + for (int i=0; i<nLen; i++) { + boolean bIsFile = false; + try { + bIsFile = sfa2.exists(contents[i]) && !sfa2.isFolder(contents[i]); + } + catch (CommandAbortedException e) { + // Considered non critical, ignore + } + catch (com.sun.star.uno.Exception e) { + // Considered non critical, ignore + } + if (bIsFile) { + IndexPageEntry entry = convertFile(contents[i],sTargetURL); + if (entry!=null) { entries[i]=entry; } + if (handler.cancel()) { return false; } + } + } + + // Step 4: Create and write out the index file + OutputFile indexFile = batchConverter.createIndexFile(sHeading, entries); + + try { + if (!sfa2.exists(sTargetURL)) { sfa2.createFolder(sTargetURL); } + } + catch (CommandAbortedException e) { + handler.endDirectory(sSourceURL,false); + return true; + } + catch (com.sun.star.uno.Exception e) { + handler.endDirectory(sSourceURL,false); + return true; + } + + try { + // writeFile demands an InputStream, so we need a pipe + Object xPipeObj=xComponentContext.getServiceManager().createInstanceWithContext( + "com.sun.star.io.Pipe",xComponentContext); + XInputStream xInStream + = (XInputStream) UnoRuntime.queryInterface(XInputStream.class, xPipeObj ); + XOutputStream xOutStream + = (XOutputStream) UnoRuntime.queryInterface(XOutputStream.class, xPipeObj ); + OutputStream outStream = new XOutputStreamToOutputStreamAdapter(xOutStream); + // Feed the pipe with content... + indexFile.write(outStream); + outStream.flush(); + outStream.close(); + xOutStream.closeOutput(); + // ...and then write the content to the url + sfa2.writeFile(ensureSlash(sTargetURL)+"index.html",xInStream); + } + catch (IOException e) { + handler.endDirectory(sSourceURL,false); + return true; + } + catch (NotConnectedException e) { + handler.endDirectory(sSourceURL,false); + return true; + } + catch (com.sun.star.uno.Exception e) { + handler.endDirectory(sSourceURL,false); + return true; + } + + handler.endDirectory(sSourceURL, true); + + return !handler.cancel(); + } + + private IndexPageEntry convertFile(String sSourceFileURL, String sTargetURL) { + handler.startFile(sSourceFileURL); + + String sTargetFileURL = ensureSlash(sTargetURL) + getBaseName(sSourceFileURL) + ".html"; + + IndexPageEntry entry = new IndexPageEntry(getName(sTargetFileURL),false); + entry.setDisplayName(decodeURL(getBaseName(sTargetFileURL))); + + // Load component + XComponent xDocument; + try { + Object desktop = xComponentContext.getServiceManager().createInstanceWithContext( + "com.sun.star.frame.Desktop", xComponentContext); + + XComponentLoader xComponentLoader = (XComponentLoader) + UnoRuntime.queryInterface(XComponentLoader.class, desktop); + + PropertyValue[] fileProps = new PropertyValue[1]; + fileProps[0] = new PropertyValue(); + fileProps[0].Name = "Hidden"; + fileProps[0].Value = new Boolean(true); + + xDocument = xComponentLoader.loadComponentFromURL(sSourceFileURL, "_blank", 0, fileProps); + } + catch (com.sun.star.io.IOException e) { + handler.endFile(sSourceFileURL,false); + return null; + } + catch (com.sun.star.uno.Exception e) { + handler.endFile(sSourceFileURL,false); + return null; + } + + // Get the title and the description of the document + XDocumentInfoSupplier docInfo = (XDocumentInfoSupplier) UnoRuntime.queryInterface(XDocumentInfoSupplier.class, xDocument); + XPropertySet infoProps = (XPropertySet) UnoRuntime.queryInterface(XPropertySet.class, docInfo.getDocumentInfo()); + if (infoProps!=null) { + try { + Object loadedTitle = infoProps.getPropertyValue("Title"); + if (AnyConverter.isString(loadedTitle)) { + String sLoadedTitle = AnyConverter.toString(loadedTitle); + if (bUseTitle && sLoadedTitle.length()>0) { + entry.setDisplayName(sLoadedTitle); + } + } + + Object loadedDescription = infoProps.getPropertyValue("Description"); + if (AnyConverter.isString(loadedDescription)) { + String sLoadedDescription = AnyConverter.toString(loadedDescription); + if (bUseDescription && sLoadedDescription.length()>0) { + entry.setDescription(sLoadedDescription); + } + } + } + catch (UnknownPropertyException e) { + } + catch (WrappedTargetException e) { + } + catch (com.sun.star.lang.IllegalArgumentException e) { + } + } + + // Determine the type of the component + boolean bText = false; + boolean bSpreadsheet = false; + if (UnoRuntime.queryInterface(XTextDocument.class, xDocument)!=null) { bText=true; } + else if (UnoRuntime.queryInterface(XSpreadsheetDocument.class, xDocument)!=null) { bSpreadsheet=true; } + if (!bText && !bSpreadsheet) { + handler.endFile(sSourceFileURL,false); + xDocument.dispose(); + return null; + } + + // Convert using requested filter + boolean bHtmlSuccess = true; + + PropertyHelper exportProps = new PropertyHelper(); + exportProps.put("FilterName", bText ? sWriterFilterName : sCalcFilterName); + exportProps.put("Overwrite", new Boolean(true)); + if (bText && writerFilterData!=null) { exportProps.put("FilterData", writerFilterData); } + if (bSpreadsheet && calcFilterData!=null) { exportProps.put("FilterData", calcFilterData); } + + try { + XStorable xStore = (XStorable) UnoRuntime.queryInterface (XStorable.class, xDocument); + xStore.storeToURL (sTargetFileURL, exportProps.toArray()); + } + catch (com.sun.star.io.IOException e) { + // Failed to convert; continue anyway, but don't link to the file name + entry.setFile(null); + bHtmlSuccess = false; + } + + // Convet to pdf if requested + boolean bPdfSuccess = true; + + if (bIncludePdf) { + PropertyValue[] pdfProps = new PropertyValue[2]; + pdfProps[0] = new PropertyValue(); + pdfProps[0].Name = "FilterName"; + pdfProps[0].Value = bText ? "writer_pdf_Export" : "calc_pdf_Export"; + pdfProps[1] = new PropertyValue(); + pdfProps[1].Name = "Overwrite"; + pdfProps[1].Value = new Boolean(true); + + String sPdfFileURL = ensureSlash(sTargetURL) + getBaseName(sSourceFileURL) + ".pdf"; + + try { + XStorable xStore = (XStorable) UnoRuntime.queryInterface (XStorable.class, xDocument); + xStore.storeToURL (sPdfFileURL, pdfProps); + entry.setPdfFile(sPdfFileURL); + } + catch (com.sun.star.io.IOException e) { + // Not critical, continue without pdf + bPdfSuccess = false; + } + } + + xDocument.dispose(); + + // Include original document if required + if (bIncludeOriginal) { + // TODO + } + + // Report a failure to the user if either of the exports failed + handler.endFile(sSourceFileURL,bHtmlSuccess && bPdfSuccess); + // But return the index entry even if only one succeded + if (bHtmlSuccess || bPdfSuccess) { return entry; } + else { return null; } + } + + private String getName(String sURL) { + int n = sURL.lastIndexOf("/"); + return n>-1 ? sURL.substring(n+1) : sURL; + } + + private String getBaseName(String sURL) { + String sName = getName(sURL); + int n = sName.lastIndexOf("."); + return n>-1 ? sName.substring(0,n) : sName; + } + + private String ensureSlash(String sURL) { + return sURL.endsWith("/") ? sURL : sURL+"/"; + } + + private String decodeURL(String sURL) { + try { + return new URI(sURL).getPath(); + } + catch (URISyntaxException e) { + return sURL; + } + } + + // Implement methods from interface XTypeProvider + + public com.sun.star.uno.Type[] getTypes() { + Type[] typeReturn = {}; + try { + typeReturn = new Type[] { + new Type( XBatchConverter.class ), + new Type( XTypeProvider.class ), + new Type( XServiceName.class ), + new Type( XServiceInfo.class ) }; + } + catch( Exception exception ) { + + } + + return( typeReturn ); + } + + + public byte[] getImplementationId() { + byte[] byteReturn = {}; + + byteReturn = new String( "" + this.hashCode() ).getBytes(); + + return( byteReturn ); + } + + // Implement method from interface XServiceName + public String getServiceName() { + return( __serviceName ); + } + + // Implement methods from interface XServiceInfo + public boolean supportsService(String stringServiceName) { + return( stringServiceName.equals( __serviceName ) ); + } + + public String getImplementationName() { + return( __implementationName ); + } + + public String[] getSupportedServiceNames() { + String[] stringSupportedServiceNames = { __serviceName }; + return( stringSupportedServiceNames ); + } + + +} + + + diff --git a/source/java/org/openoffice/da/comp/writer2xhtml/BatchHandlerAdapter.java b/source/java/org/openoffice/da/comp/writer2xhtml/BatchHandlerAdapter.java new file mode 100644 index 0000000..21d3519 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2xhtml/BatchHandlerAdapter.java @@ -0,0 +1,72 @@ +/************************************************************************ + * + * BatchHandlerAdapter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-10-05) + * + */ + +package org.openoffice.da.comp.writer2xhtml; + +import writer2latex.api.BatchHandler; +import org.openoffice.da.writer2xhtml.XBatchHandler; + +/** The uno interface provides an XBatchHandler implementation, the java + * interface requires a BatchHandler implementation. This simple class + * implements the latter using an instance of the former. + */ +public class BatchHandlerAdapter implements BatchHandler { + + private XBatchHandler unoHandler; + + public BatchHandlerAdapter(XBatchHandler unoHandler) { + this.unoHandler = unoHandler; + } + + public void startConversion() { + unoHandler.startConversion(); + } + + public void endConversion() { + unoHandler.endConversion(); + } + + public void startDirectory(String sName) { + unoHandler.startDirectory(sName); + } + + public void endDirectory(String sName, boolean bSuccess) { + unoHandler.endDirectory(sName, bSuccess); + } + + public void startFile(String sName) { + unoHandler.startFile(sName); + } + + public void endFile(String sName, boolean bSuccess) { + unoHandler.endFile(sName, bSuccess); + } + + public boolean cancel() { + return unoHandler.cancel(); + } + +} diff --git a/source/java/org/openoffice/da/comp/writer2xhtml/W2XExportFilter.java b/source/java/org/openoffice/da/comp/writer2xhtml/W2XExportFilter.java new file mode 100644 index 0000000..0278890 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2xhtml/W2XExportFilter.java @@ -0,0 +1,56 @@ +/************************************************************************ + * + * W2XExportFilter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-07-21) + * + */ + +package org.openoffice.da.comp.writer2xhtml; + +import com.sun.star.uno.XComponentContext; + +import org.openoffice.da.comp.w2lcommon.filter.ExportFilterBase; + + +/** This class implements the xhtml export filter component + */ +public class W2XExportFilter extends ExportFilterBase { + + /** Service name for the component */ + public static final String __serviceName = "org.openoffice.da.comp.writer2xhtml.W2XExportFilter"; + + /** Implementation name for the component */ + public static final String __implementationName = "org.openoffice.da.comp.writer2xhtml.W2XExportFilter"; + + /** Filter name to include in error messages */ + public static final String __displayName = "Writer2xhtml"; + + public W2XExportFilter(XComponentContext xComponentContext1) { + super(xComponentContext1); + xMSF = W2XRegistration.xMultiServiceFactory; + } + + +} + + + diff --git a/source/java/org/openoffice/da/comp/writer2xhtml/W2XRegistration.java b/source/java/org/openoffice/da/comp/writer2xhtml/W2XRegistration.java new file mode 100644 index 0000000..23efa86 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2xhtml/W2XRegistration.java @@ -0,0 +1,119 @@ +/************************************************************************ + * + * W2XRegistration.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-10-04) + * + */ + +package org.openoffice.da.comp.writer2xhtml; + +import com.sun.star.lang.XMultiServiceFactory; +import com.sun.star.lang.XSingleServiceFactory; +import com.sun.star.registry.XRegistryKey; + +import com.sun.star.comp.loader.FactoryHelper; + +/** This class provides a static method to instantiate our uno components + * on demand (__getServiceFactory()), and a static method to give + * information about the components (__writeRegistryServiceInfo()). + * Furthermore, it saves the XMultiServiceFactory provided to the + * __getServiceFactory method for future reference by the componentes. + */ +public class W2XRegistration { + + public static XMultiServiceFactory xMultiServiceFactory; + + /** + * Returns a factory for creating the service. + * This method is called by the <code>JavaLoader</code> + * + * @return returns a <code>XSingleServiceFactory</code> for creating the + * component + * + * @param implName the name of the implementation for which a + * service is desired + * @param multiFactory the service manager to be used if needed + * @param regKey the registryKey + * + * @see com.sun.star.comp.loader.JavaLoader + */ + public static XSingleServiceFactory __getServiceFactory(String implName, + XMultiServiceFactory multiFactory, XRegistryKey regKey) { + xMultiServiceFactory = multiFactory; + XSingleServiceFactory xSingleServiceFactory = null; + if (implName.equals(W2XExportFilter.class.getName()) ) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(W2XExportFilter.class, + W2XExportFilter.__serviceName, + multiFactory, + regKey); + } + else if (implName.equals(BatchConverter.__implementationName) ) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(BatchConverter.class, + BatchConverter.__serviceName, + multiFactory, + regKey); + } + else if (implName.equals(XhtmlOptionsDialog.__implementationName)) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(XhtmlOptionsDialog.class, + XhtmlOptionsDialog.__serviceName, + multiFactory, + regKey); + } + else if (implName.equals(XhtmlOptionsDialogXsl.__implementationName)) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(XhtmlOptionsDialogXsl.class, + XhtmlOptionsDialogXsl.__serviceName, + multiFactory, + regKey); + } + else if (implName.equals(XhtmlOptionsDialogCalc.__implementationName)) { + xSingleServiceFactory = FactoryHelper.getServiceFactory(XhtmlOptionsDialogCalc.class, + XhtmlOptionsDialogCalc.__serviceName, + multiFactory, + regKey); + } + + return xSingleServiceFactory; + } + + /** + * Writes the service information into the given registry key. + * This method is called by the <code>JavaLoader</code> + * <p> + * @return returns true if the operation succeeded + * @param regKey the registryKey + * @see com.sun.star.comp.loader.JavaLoader + */ + public static boolean __writeRegistryServiceInfo(XRegistryKey regKey) { + return + FactoryHelper.writeRegistryServiceInfo(BatchConverter.__implementationName, + BatchConverter.__serviceName, regKey) & + FactoryHelper.writeRegistryServiceInfo(W2XExportFilter.__implementationName, + W2XExportFilter.__serviceName, regKey) & + FactoryHelper.writeRegistryServiceInfo(XhtmlOptionsDialog.__implementationName, + XhtmlOptionsDialog.__serviceName, regKey) & + FactoryHelper.writeRegistryServiceInfo(XhtmlOptionsDialogXsl.__implementationName, + XhtmlOptionsDialogXsl.__serviceName, regKey) & + FactoryHelper.writeRegistryServiceInfo(XhtmlOptionsDialogCalc.__implementationName, + XhtmlOptionsDialogCalc.__serviceName, regKey); + } +} + diff --git a/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialog.java b/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialog.java new file mode 100644 index 0000000..ea982d0 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialog.java @@ -0,0 +1,216 @@ +/************************************************************************ + * + * XhtmlOptionsDialog.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-16) + * + */ + +package org.openoffice.da.comp.writer2xhtml; + +import com.sun.star.awt.XDialog; +import com.sun.star.beans.XPropertySet; +import com.sun.star.uno.XComponentContext; + +import org.openoffice.da.comp.w2lcommon.helper.PropertyHelper; +import org.openoffice.da.comp.w2lcommon.filter.OptionsDialogBase; + +/** This class provides a uno component which implements a filter ui for the + * Xhtml export + */ +public class XhtmlOptionsDialog extends OptionsDialogBase { + + /** The component will be registered under this name. + */ + public static String __serviceName = "org.openoffice.da.writer2xhtml.XhtmlOptionsDialog"; + + /** The component should also have an implementation name. + */ + public static String __implementationName = "org.openoffice.da.comp.writer2xhtml.XhtmlOptionsDialog"; + + public String getDialogLibraryName() { return "W2XDialogs"; } + + /** Return the name of the dialog within the library + */ + public String getDialogName() { return "XhtmlOptions"; } + + /** Return the name of the registry path + */ + public String getRegistryPath() { + return "/org.openoffice.da.Writer2xhtml.Options/XhtmlOptions"; + } + + /** Create a new XhtmlOptionsDialog */ + public XhtmlOptionsDialog(XComponentContext xContext) { + super(xContext); + xMSF = W2XRegistration.xMultiServiceFactory; + } + + /** Load settings from the registry to the dialog */ + protected void loadSettings(XPropertySet xProps) { + // Style + loadConfig(xProps); + loadCheckBoxOption(xProps, "ConvertToPx"); + loadNumericOption(xProps, "Scaling"); + loadNumericOption(xProps, "ColumnScaling"); + loadCheckBoxOption(xProps, "OriginalImageSize"); + + // Special content + loadCheckBoxOption(xProps, "Notes"); + loadCheckBoxOption(xProps, "UseDublinCore"); + + // AutoCorrect + loadCheckBoxOption(xProps, "IgnoreHardLineBreaks"); + loadCheckBoxOption(xProps, "IgnoreEmptyParagraphs"); + loadCheckBoxOption(xProps, "IgnoreDoubleSpaces"); + + // Files + loadCheckBoxOption(xProps, "Split"); + loadListBoxOption(xProps, "SplitLevel"); + loadListBoxOption(xProps, "RepeatLevels"); + loadCheckBoxOption(xProps, "SaveImagesInSubdir"); + loadTextFieldOption(xProps, "XsltPath"); + + updateLockedOptions(); + enableControls(); + } + + /** Save settings from the dialog to the registry and create FilterData */ + protected void saveSettings(XPropertySet xProps, PropertyHelper helper) { + // Style + short nConfig = saveConfig(xProps, helper); + String[] sCoreStyles = { "Chocolate", "Midnight", "Modernist", + "Oldstyle", "Steely", "Swiss", "Traditional", "Ultramarine" }; + switch (nConfig) { + case 0: helper.put("ConfigURL","*default.xml"); break; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: helper.put("ConfigURL","*cleanxhtml.xml"); + helper.put("custom_stylesheet", + "http://www.w3.org/StyleSheets/Core/"+sCoreStyles[nConfig-1]); + break; + case 9: helper.put("ConfigURL","$(user)/writer2xhtml.xml"); + helper.put("AutoCreate","true"); + } + + saveCheckBoxOption(xProps, helper, "ConvertToPx", "convert_to_px"); + saveNumericOptionAsPercentage(xProps, helper, "Scaling", "scaling"); + saveNumericOptionAsPercentage(xProps, helper, "ColumnScaling", "column_scaling"); + saveCheckBoxOption(xProps, helper, "OriginalImageSize", "original_image_size"); + + // Special content + saveCheckBoxOption(xProps, helper, "Notes", "notes"); + saveCheckBoxOption(xProps, helper, "UseDublinCore", "use_dublin_core"); + + // AutoCorrect + saveCheckBoxOption(xProps, helper, "IgnoreHardLineBreaks", "ignore_hard_line_breaks"); + saveCheckBoxOption(xProps, helper, "IgnoreEmptyParagraphs", "ignore_empty_paragraphs"); + saveCheckBoxOption(xProps, helper, "IgnoreDoubleSpaces", "ignore_double_spaces"); + + // Files + boolean bSplit = saveCheckBoxOption(xProps, "Split"); + short nSplitLevel = saveListBoxOption(xProps, "SplitLevel"); + short nRepeatLevels = saveListBoxOption(xProps, "RepeatLevels"); + if (!isLocked("split_level")) { + if (bSplit) { + helper.put("split_level",Integer.toString(nSplitLevel+1)); + helper.put("repeat_levels",Integer.toString(nRepeatLevels)); + } + else { + helper.put("split_level","0"); + } + } + + saveCheckBoxOption(xProps, helper, "SaveImagesInSubdir", "save_images_in_subdir"); + saveTextFieldOption(xProps, helper, "XsltPath", "xslt_path"); + + } + + + // Implement XDialogEventHandler + public boolean callHandlerMethod(XDialog xDialog, Object event, String sMethod) { + if (sMethod.equals("ConfigChange")) { + updateLockedOptions(); + enableControls(); + } + else if (sMethod.equals("SplitChange")) { + enableSplitLevel(); + } + return true; + } + + public String[] getSupportedMethodNames() { + String[] sNames = { "ConfigChange", "SplitChange" }; + return sNames; + } + + private void enableControls() { + // Style + setControlEnabled("ScalingLabel",!isLocked("scaling")); + setControlEnabled("Scaling",!isLocked("scaling")); + setControlEnabled("ColumnScalingLabel",!isLocked("column_scaling")); + setControlEnabled("ColumnScaling",!isLocked("column_scaling")); + setControlEnabled("ConvertToPx",!isLocked("convert_to_px")); + setControlEnabled("OriginalImageSize",!isLocked("original_image_size")); + + // Special content + setControlEnabled("Notes",!isLocked("notes")); + setControlEnabled("UseDublinCore",!isLocked("use_dublin_core")); + + // AutoCorrect + setControlEnabled("IgnoreHardLineBreaks",!isLocked("ignore_hard_line_breaks")); + setControlEnabled("IgnoreEmptyParagraphs",!isLocked("ignore_empty_paragraphs")); + setControlEnabled("IgnoreDoubleSpaces",!isLocked("ignore_double_spaces")); + + // Files + boolean bSplit = getCheckBoxStateAsBoolean("Split"); + setControlEnabled("Split",!isLocked("split_level")); + setControlEnabled("SplitLevelLabel",!isLocked("split_level") && bSplit); + setControlEnabled("SplitLevel",!isLocked("split_level") && bSplit); + setControlEnabled("RepeatLevelsLabel",!isLocked("repeat_levels") && !isLocked("split_level") && bSplit); + setControlEnabled("RepeatLevels",!isLocked("repeat_levels") && !isLocked("split_level") && bSplit); + setControlEnabled("SaveImagesInSubdir",!isLocked("save_images_in_subdir")); + setControlEnabled("XsltPathLabel",(this instanceof XhtmlOptionsDialogXsl) && !isLocked("xslt_path")); + setControlEnabled("XsltPath",(this instanceof XhtmlOptionsDialogXsl) && !isLocked("xslt_path")); + } + + private void enableSplitLevel() { + if (!isLocked("split_level")) { + boolean bState = getCheckBoxStateAsBoolean("Split"); + setControlEnabled("SplitLevelLabel",bState); + setControlEnabled("SplitLevel",bState); + if (!isLocked("repeat_levels")) { + setControlEnabled("RepeatLevelsLabel",bState); + setControlEnabled("RepeatLevels",bState); + } + } + } + + +} + + + diff --git a/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogCalc.java b/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogCalc.java new file mode 100644 index 0000000..874d6d1 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogCalc.java @@ -0,0 +1,174 @@ +/************************************************************************ + * + * XhtmlOptionsDialogCalc.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-18) + * + */ + +package org.openoffice.da.comp.writer2xhtml; + +import com.sun.star.awt.XDialog; +import com.sun.star.beans.XPropertySet; +import com.sun.star.uno.XComponentContext; + +import org.openoffice.da.comp.w2lcommon.helper.PropertyHelper; +import org.openoffice.da.comp.w2lcommon.filter.OptionsDialogBase; + +/** This class provides a uno component which implements a filter ui for the + * Xhtml export in Calc + */ +public class XhtmlOptionsDialogCalc extends OptionsDialogBase { + + /** The component will be registered under this name. + */ + public static String __serviceName = "org.openoffice.da.writerxhtml.XhtmlOptionsDialogCalc"; + + /** The component should also have an implementation name. + */ + public static String __implementationName = "org.openoffice.da.comp.writer2xhtml.XhtmlOptionsDialogCalc"; + + public String getDialogLibraryName() { return "W2XDialogs"; } + + /** Return the name of the dialog within the library + */ + public String getDialogName() { return "XhtmlOptionsCalc"; } + + /** Return the name of the registry path + */ + public String getRegistryPath() { + return "/org.openoffice.da.Writer2xhtml.Options/XhtmlOptionsCalc"; + } + + /** Create a new XhtmlOptionsDialogCalc */ + public XhtmlOptionsDialogCalc(XComponentContext xContext) { + super(xContext); + xMSF = W2XRegistration.xMultiServiceFactory; + } + + /** Load settings from the registry to the dialog */ + protected void loadSettings(XPropertySet xProps) { + // Style + loadConfig(xProps); + loadCheckBoxOption(xProps, "ConvertToPx"); + loadNumericOption(xProps, "Scaling"); + loadNumericOption(xProps, "ColumnScaling"); + loadCheckBoxOption(xProps, "OriginalImageSize"); + + // Special content + loadCheckBoxOption(xProps, "Notes"); + loadCheckBoxOption(xProps, "UseDublinCore"); + + // Sheets + loadCheckBoxOption(xProps, "DisplayHiddenSheets"); + loadCheckBoxOption(xProps, "DisplayHiddenRowsCols"); + loadCheckBoxOption(xProps, "DisplayFilteredRowsCols"); + loadCheckBoxOption(xProps, "ApplyPrintRanges"); + loadCheckBoxOption(xProps, "UseTitleAsHeading"); + loadCheckBoxOption(xProps, "UseSheetNamesAsHeadings"); + + // Files + loadCheckBoxOption(xProps, "CalcSplit"); + loadCheckBoxOption(xProps, "SaveImagesInSubdir"); + + updateLockedOptions(); + enableControls(); + } + + /** Save settings from the dialog to the registry and create FilterData */ + protected void saveSettings(XPropertySet xProps, PropertyHelper helper) { + // Style + short nConfig = saveConfig(xProps, helper); + if (nConfig==0) { + helper.put("ConfigURL","*default.xml"); + } + else if (nConfig==1) { + helper.put("ConfigURL","$(user)/writer2xhtml.xml"); + helper.put("AutoCreate","true"); + } + + saveCheckBoxOption(xProps, helper, "ConvertToPx", "convert_to_px"); + saveNumericOptionAsPercentage(xProps, helper, "Scaling", "scaling"); + saveNumericOptionAsPercentage(xProps, helper, "ColumnScaling", "column_scaling"); + saveCheckBoxOption(xProps, helper, "OriginalImageSize", "original_image_size"); + + // Special content + saveCheckBoxOption(xProps, helper, "Notes", "notes"); + saveCheckBoxOption(xProps, helper, "UseDublinCore", "use_dublin_core"); + + // Sheets + saveCheckBoxOption(xProps, helper, "DisplayHiddenSheets", "display_hidden_sheets"); + saveCheckBoxOption(xProps, helper, "DisplayHiddenRowsCols", "display_hidden_rows_cols"); + saveCheckBoxOption(xProps, helper, "DisplayFilteredRowsCols", "display_filtered_rows_cols"); + saveCheckBoxOption(xProps, helper, "ApplyPrintRanges", "apply_print_ranges"); + saveCheckBoxOption(xProps, helper, "UseTitleAsHeading", "use_title_as_heading"); + saveCheckBoxOption(xProps, helper, "UseSheetNamesAsHeadings", "use_sheet_names_as_headings"); + + // Files + saveCheckBoxOption(xProps, helper, "CalcSplit", "calc_split"); + saveCheckBoxOption(xProps, helper, "SaveImagesInSubdir", "save_images_in_subdir"); + + } + + // Implement XDialogEventHandler + public boolean callHandlerMethod(XDialog xDialog, Object event, String sMethod) { + if (sMethod.equals("ConfigChange")) { + updateLockedOptions(); + enableControls(); + } + return true; + } + + public String[] getSupportedMethodNames() { + String[] sNames = { "ConfigChange" }; + return sNames; + } + + private void enableControls() { + // Style + setControlEnabled("ConvertToPx",!isLocked("convert_to_px")); + setControlEnabled("ScalingLabel",!isLocked("scaling")); + setControlEnabled("Scaling",!isLocked("scaling")); + setControlEnabled("ColumnScalingLabel",!isLocked("column_scaling")); + setControlEnabled("ColumnScaling",!isLocked("column_scaling")); + setControlEnabled("OriginalImageSize",!isLocked("original_image_size")); + + // Special content + setControlEnabled("Notes",!isLocked("notes")); + setControlEnabled("UseDublinCore",!isLocked("use_dublin_core")); + + // Sheets + setControlEnabled("DisplayHiddenSheets", !isLocked("display_hidden_sheets")); + setControlEnabled("DisplayHiddenRowsCols", !isLocked("display_hidden_rows_cols")); + setControlEnabled("DisplayFilteredRowsCols", !isLocked("display_filtered_rows_cols")); + setControlEnabled("ApplyPrintRanges", !isLocked("apply_print_ranges")); + setControlEnabled("UseTitleAsHeading", !isLocked("use_title_as_heading")); + setControlEnabled("UseSheetNamesAsHeadings", !isLocked("use_sheet_names_as_headings")); + + // Files + setControlEnabled("CalcSplit",!isLocked("calc_split")); + setControlEnabled("SaveImagesInSubdir",!isLocked("save_images_in_subdir")); + } + +} + + + diff --git a/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogXsl.java b/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogXsl.java new file mode 100644 index 0000000..df6bda3 --- /dev/null +++ b/source/java/org/openoffice/da/comp/writer2xhtml/XhtmlOptionsDialogXsl.java @@ -0,0 +1,52 @@ +/************************************************************************ + * + * XhtmlOptionsDialogXsl.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-11) + * + */ + +package org.openoffice.da.comp.writer2xhtml; + +import com.sun.star.uno.XComponentContext; + +/** This class provides a uno component which implements a filter ui for the + * Xhtml export (xsl variant) + * This variant of the dialog has the XsltPath setting enabled + */ +public class XhtmlOptionsDialogXsl extends XhtmlOptionsDialog { + /** The component will be registered under this name. + */ + public static String __serviceName = "org.openoffice.da.writer2xhtml.XhtmlOptionsDialogXsl"; + + /** The component should also have an implementation name. + */ + public static String __implementationName = "org.openoffice.da.comp.writer2xhtml.XhtmlOptionsDialogXsl"; + + /** Create a new XhtmlOptionsDialogXsl */ + public XhtmlOptionsDialogXsl(XComponentContext xContext) { + super(xContext); + } + +} + + + diff --git a/source/java/writer2latex/Application.java b/source/java/writer2latex/Application.java new file mode 100644 index 0000000..2e8f213 --- /dev/null +++ b/source/java/writer2latex/Application.java @@ -0,0 +1,382 @@ +/************************************************************************ + * + * Application.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; +//import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import writer2latex.api.BatchConverter; +import writer2latex.api.Converter; +import writer2latex.api.ConverterFactory; +import writer2latex.api.ConverterResult; +import writer2latex.api.MIMETypes; +//import writer2latex.api.OutputFile; + +import writer2latex.util.Misc; + +/** + * <p>Commandline utility to convert an OpenOffice.org Writer XML file into XHTML/LaTeX/BibTeX</p> + * <p>The utility is invoked with the following command line:</p> + * <pre>java -jar writer2latex.jar [options] source [target]</pre> + * <p>Where the available options are + * <ul> + * <li><code>-latex</code>, <code>-bibtex</code>, <code>-xhtml</code>, + <code>-xhtml+mathml</code>, <code>-xhtml+mathml+xsl</code> + * <li><code>-recurse</code> + * <li><code>-ultraclean</code>, <code>-clean</code>, <code>-pdfscreen</code>, + * <code>-pdfprint</code>, <code>-cleanxhtml</code> + * <li><code>-config[=]filename</code> + * <li><code>-template[=]filename</code> + * <li><code>-option[=]value</code> + * </ul> + * <p>where <code>option</code> can be any simple option known to Writer2LaTeX + * (see documentation for the configuration file).</p> + */ +public final class Application { + + /* Based on command-line parameters. */ + private String sTargetMIME = MIMETypes.LATEX; + private boolean bRecurse = false; + private Vector configFileNames = new Vector(); + private String sTemplateFileName = null; + private Hashtable options = new Hashtable(); + private String sSource = null; + private String sTarget = null; + + /** + * Main method + * + * @param args The argument passed on the command line. + */ + public static final void main (String[] args){ + try { + Application app = new Application(); + app.parseCommandLine(args); + app.doConversion(); + } catch (IllegalArgumentException ex) { + String msg = ex.getMessage(); + showUsage(msg); + } + } + + // Convert the directory or file + private void doConversion() { + // Step 1: Say hello... + String sOutputFormat; + if (MIMETypes.LATEX.equals(sTargetMIME)) { sOutputFormat = "LaTeX"; } + else if (MIMETypes.BIBTEX.equals(sTargetMIME)) { sOutputFormat = "BibTeX"; } + else { sOutputFormat = "xhtml"; } + System.out.println(); + System.out.println("This is Writer2" + sOutputFormat + + ", Version " + ConverterFactory.getVersion() + + " (" + ConverterFactory.getDate() + ")"); + System.out.println(); + System.out.println("Starting conversion..."); + + // Step 2: Examine source + File source = new File(sSource); + if (!source.exists()) { + System.out.println("I'm sorry, I can't find "+sSource); + System.exit(1); + } + if (!source.canRead()) { + System.out.println("I'm sorry, I can't read "+sSource); + System.exit(1); + } + boolean bBatch = source.isDirectory(); + + // Step 3: Examine target + File target; + if (bBatch) { + if (sTarget==null) { + target=source; + } + else { + target = new File(sTarget); + } + } + else { + if (sTarget==null) { + target = new File(source.getParent(),Misc.removeExtension(source.getName())); + } + else { + target = new File(sTarget); + if (sTarget.endsWith(File.separator)) { + target = new File(target,Misc.removeExtension(source.getName())); + } + } + } + + // Step 4: Create converters + Converter converter = ConverterFactory.createConverter(sTargetMIME); + if (converter==null) { + System.out.println("Failed to create converter for "+sTargetMIME); + System.exit(1); + } + + BatchConverter batchCv = null; + if (bBatch) { + batchCv = ConverterFactory.createBatchConverter(MIMETypes.XHTML); + if (batchCv==null) { + System.out.println("Failed to create batch converter"); + System.exit(1); + } + batchCv.setConverter(converter); + } + + // Step 5: Read template + if (sTemplateFileName!=null) { + try { + System.out.println("Reading template "+sTemplateFileName); + byte [] templateBytes = Misc.inputStreamToByteArray(new FileInputStream(sTemplateFileName)); + converter.readTemplate(new ByteArrayInputStream(templateBytes)); + if (batchCv!=null) { + // Currently we use the same template for the directory and the files + batchCv.readTemplate(new ByteArrayInputStream(templateBytes)); + } + } + catch (FileNotFoundException e) { + System.out.println("--> This file does not exist!"); + System.out.println(" "+e.getMessage()); + } + catch (IOException e) { + System.out.println("--> Failed to read the template file!"); + System.out.println(" "+e.getMessage()); + } + } + + // Step 6: Read config + for (int i=0; i<configFileNames.size(); i++) { + String sConfigFileName = (String) configFileNames.get(i); + if (sConfigFileName.startsWith("*")) { + sConfigFileName = sConfigFileName.substring(1); + System.out.println("Reading default configuration "+sConfigFileName); + try { + converter.getConfig().readDefaultConfig(sConfigFileName); + } + catch (IllegalArgumentException e) { + System.err.println("--> This configuration is unknown!"); + System.out.println(" "+e.getMessage()); + } + } + else { + System.out.println("Reading configuration file "+sConfigFileName); + try { + byte[] configBytes = Misc.inputStreamToByteArray(new FileInputStream(sConfigFileName)); + converter.getConfig().read(new ByteArrayInputStream(configBytes)); + if (bBatch) { + // Currently we use the same configuration for the directory and the files + batchCv.getConfig().read(new ByteArrayInputStream(configBytes)); + } + } + catch (IOException e) { + System.err.println("--> Failed to read the configuration!"); + System.out.println(" "+e.getMessage()); + } + } + } + + // Step 7: Set options from command line + Enumeration keys = options.keys(); + while (keys.hasMoreElements()) { + String sKey = (String) keys.nextElement(); + String sValue = (String) options.get(sKey); + converter.getConfig().setOption(sKey,sValue); + if (batchCv!=null) { + batchCv.getConfig().setOption(sKey,sValue); + } + } + + // Step 8: Perform conversion + if (bBatch) { + batchCv.convert(source,target,bRecurse, new BatchHandlerImpl()); + } + else { + System.out.println("Converting "+source.getPath()); + ConverterResult dataOut = null; + + try { + dataOut = converter.convert(source,target.getName()); + } + catch (FileNotFoundException e) { + System.out.println("--> The file "+source.getPath()+" does not exist!"); + System.out.println(" "+e.getMessage()); + System.exit(1); + } + catch (IOException e) { + System.out.println("--> Failed to convert the file "+source.getPath()+"!"); + System.out.println(" "+e.getMessage()); + System.exit(1); + } + + // TODO: Should do some further checking on the feasability of writing + // the directory and the files. + File targetDir = target.getParentFile(); + if (targetDir!=null && !targetDir.exists()) { targetDir.mkdirs(); } + try { + dataOut.write(targetDir); + } + catch (IOException e) { + System.out.println("--> Error writing out file!"); + System.out.println(" "+e.getMessage()); + System.exit(1); + } + + } + + // Step 9: Say goodbye! + System.out.println("Done!"); + } + + + /** + * Display usage. + */ + private static void showUsage(String msg) { + System.out.println(); + System.out.println("This is Writer2LaTeX, Version " + ConverterFactory.getVersion() + + " (" + ConverterFactory.getDate() + ")"); + System.out.println(); + if (msg != null) System.out.println(msg); + System.out.println(); + System.out.println("Usage:"); + System.out.println(" java -jar <path>/writer2latex.jar <options> <source file/directory> [<target file/directory>]"); + System.out.println("where the available options are:"); + System.out.println(" -latex"); + System.out.println(" -bibtex"); + System.out.println(" -xhtml"); + System.out.println(" -xhtml+mathml"); + System.out.println(" -xhtml+mathml+xsl"); + System.out.println(" -recurse"); + System.out.println(" -template[=]<template file>"); + System.out.println(" -ultraclean"); + System.out.println(" -clean"); + System.out.println(" -pdfprint"); + System.out.println(" -pdfscreen"); + System.out.println(" -cleanxhtml"); + System.out.println(" -config[=]<configuration file>"); + System.out.println(" -<configuration option>[=]<value>"); + System.out.println("See the documentation for the available configuration options"); + } + + /** + * Parse command-line arguments. + * + * @param args Array of command line arguments. + * + * @throws IllegalArgumentException If an argument is invalid. + */ + private void parseCommandLine(String sArgs[]) + throws IllegalArgumentException { + + int i = 0; + + while (i<sArgs.length) { + String sArg = getArg(i++,sArgs); + if (sArg.startsWith("-")) { // found an option + if ("-latex".equals(sArg)) { sTargetMIME = MIMETypes.LATEX; } + else if ("-bibtex".equals(sArg)) { sTargetMIME = MIMETypes.BIBTEX; } + else if ("-xhtml".equals(sArg)) { sTargetMIME = MIMETypes.XHTML; } + else if ("-xhtml+mathml".equals(sArg)) { sTargetMIME = MIMETypes.XHTML_MATHML; } + else if ("-xhtml+mathml+xsl".equals(sArg)) { sTargetMIME = MIMETypes.XHTML_MATHML_XSL; } + else if ("-recurse".equals(sArg)) { bRecurse = true; } + else if ("-ultraclean".equals(sArg)) { configFileNames.add("*ultraclean.xml"); } + else if ("-clean".equals(sArg)) { configFileNames.add("*clean.xml"); } + else if ("-pdfprint".equals(sArg)) { configFileNames.add("*pdfprint.xml"); } + else if ("-pdfscreen".equals(sArg)) { configFileNames.add("*pdfscreen.xml"); } + else if ("-cleanxhtml".equals(sArg)) { configFileNames.add("*cleanxhtml.xml"); } + else { // option with argument + int j=sArg.indexOf("="); + String sArg2; + if (j>-1) { // argument is separated by = + sArg2 = sArg.substring(j+1); + sArg = sArg.substring(0,j); + } + else { // argument is separated by space + sArg2 = getArg(i++,sArgs); + } + if ("-config".equals(sArg)) { configFileNames.add(sArg2); } + else if ("-template".equals(sArg)) { sTemplateFileName = sArg2; } + else { // configuration option + options.put(sArg.substring(1),sArg2); + } + } + } + else { // not an option, so this must be the source + sSource = sArg; + // Possibly followed by the target + if (i<sArgs.length) { + String sArgument = getArg(i++,sArgs); + if (sArgument.length()>0) { sTarget = sArgument; } + } + // Skip any trailing empty arguments and signal an error if there's more + while (i<sArgs.length) { + String sArgument = getArg(i++,sArgs); + if (sArgument.length()>0) { + throw new IllegalArgumentException("I didn't expect "+sArgument+"?"); + } + } + } + } + if (sSource==null) { + throw new IllegalArgumentException("Please specify a source document/directory!"); + } + // Parsing of command line ended successfully! + } + + + /** + * Extract the next argument from the array, while checking to see + * that the array size is not exceeded. Throw a friendly error + * message in case the arg is missing. + * + * @param i Argument index. + * @param args Array of command line arguments. + * + * @return The argument with the specified index. + * + * @throws IllegalArgumentException If an argument is invalid. + */ + private String getArg(int i, String args[]) + throws IllegalArgumentException { + + if (i < args.length) { + return args[i]; + } + else throw new + IllegalArgumentException("I'm sorry, the commandline ended abnormally"); + } + + +} diff --git a/source/java/writer2latex/BatchHandlerImpl.java b/source/java/writer2latex/BatchHandlerImpl.java new file mode 100644 index 0000000..b4e62a5 --- /dev/null +++ b/source/java/writer2latex/BatchHandlerImpl.java @@ -0,0 +1,94 @@ +/************************************************************************ + * + * BatchHandlerImpl.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-10-03) + * + */ + +package writer2latex; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; + +import writer2latex.api.BatchHandler; + +/** This class implements a <code>BatchHandler</code> for command line usage + */ +public class BatchHandlerImpl implements BatchHandler { + private int nIndent = 0; + + private void writeMessage(String sMsg) { + for (int i=0; i<nIndent; i++) { + System.out.print(" "); + } + System.out.println(sMsg); + } + + public void startConversion() { + System.out.println("Press Enter to cancel the conversion"); + } + + public void endConversion() { + // No message + } + + public void startDirectory(String sName) { + writeMessage("Converting directory "+sName); + nIndent++; + } + + public void endDirectory(String sName, boolean bSuccess) { + nIndent--; + if (!bSuccess) { + writeMessage("--> Conversion of the directory "+sName+" failed!"); + } + } + + public void startFile(String sName) { + writeMessage("Converting file "+sName); + nIndent++; + } + + public void endFile(String sName, boolean bSuccess) { + nIndent--; + if (!bSuccess) { + writeMessage("--> Conversion of the file "+sName+" failed!"); + } + } + + public boolean cancel() { + try { + if (System.in.available()>0) { + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + in.readLine(); + System.out.print("Do you want to cancel the conversion (y/n)? "); + String s = in.readLine(); + if (s!= null && s.toLowerCase().startsWith("y")) { return true; } + } + } + catch (IOException e) { + } + return false; + } + +} diff --git a/source/java/writer2latex/api/BatchConverter.java b/source/java/writer2latex/api/BatchConverter.java new file mode 100644 index 0000000..e990592 --- /dev/null +++ b/source/java/writer2latex/api/BatchConverter.java @@ -0,0 +1,96 @@ +/************************************************************************ + * + * BatchConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +import java.io.File; +import java.io.InputStream; +import java.io.IOException; + +/** This is an interface for a converter, which offers conversion of + * all OpenDocument (or OpenOffice.org 1.x) documents in a directory + * (and optionally subdirectories), creating index pages in a specific format. + * Instances of this interface are created using the + * {@link ConverterFactory} + */ +public interface BatchConverter { + + /** Get the configuration interface for this batch converter + * + * @return the configuration + */ + public Config getConfig(); + + /** Define a <code>Converter</code> implementation to use for + * conversion of the individual documents. + * If no converter is given, the <code>convert</code> method cannot + * convert documents (but can still create index pages). + * + * @param converter the <code>Converter</code> to use + */ + public void setConverter(Converter converter); + + /** Read a template to use as a base for the index pages. + * The format of the template depends on the <code>BatchConverter</code> + * implementation. + * + * @param is an <code>InputStream</code> from which to read the template + * @throws IOException if some exception occurs while reading the template + */ + public void readTemplate(InputStream is) throws IOException; + + /** Read a template to use as a base for the index pages. + * The format of the template depends on the <code>BatchConverter</code> + * implementation. + * + * @param file the file from which to read the template + * @throws IOException if the file does not exist or some exception occurs + * while reading the template + */ + public void readTemplate(File file) throws IOException; + + /** Create an index page with specific entries + * + * @param sHeading a heading describing the index page + * @param entries an array of <code>IndexPageEntry</code> objects (null entries + * are allowed, and will be ignored) describing the individual directories + * and documents + */ + public OutputFile createIndexFile(String sHeading, IndexPageEntry[] entries); + + /** Convert a directory using the given <code>Converter</code> (if none is given, + * all files will be ignored). + * This method fails silently if you haven't set a converter. + * + * @param source a <code>File</code> representing the directory to convert + * @param target a <code>File</code> representing the directory to contain + * the converted documents + * @param bRecurse determines wether or not to recurse into subdirectories + * @param handler a </code>BatchHandler</code> + */ + public void convert(File source, File target, boolean bRecurse, BatchHandler handler); + +} diff --git a/source/java/writer2latex/api/BatchHandler.java b/source/java/writer2latex/api/BatchHandler.java new file mode 100644 index 0000000..647ae5d --- /dev/null +++ b/source/java/writer2latex/api/BatchHandler.java @@ -0,0 +1,77 @@ +/************************************************************************ + * + * BatchHandler.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +/** This is a call back interface to handle user interaction during a + * batch conversion with a {@link BatchConverter} + */ +public interface BatchHandler { + + /** Notification that the conversion is started */ + public void startConversion(); + + /** Notification that the conversion has finished */ + public void endConversion(); + + /** Notification that a directory conversion starts + * + * @param sName the name of the directory to convert + */ + public void startDirectory(String sName); + + /** Notification that a directory conversion has finished + * + * @param sName the name of the directory + * @param bSuccess true if the conversion was successful (this only means + * that the index page was created with success, not that the conversion + * of files and subdirectories was successful) + */ + public void endDirectory(String sName, boolean bSuccess); + + /** Notification that a file conversion starts + * + * @param sName the name of the file to convert + */ + public void startFile(String sName); + + /** Notification that a file conversion has finished + * + * @param sName the name of the file + * @param bSuccess true if the conversion of this file was successful + */ + public void endFile(String sName, boolean bSuccess); + + /** Notification that the conversion may be cancelled. The + * {@link BatchConverter} fires this event once per document. + * Cancelling the conversion does not delete files that was already + * converted + * + * @return true if the handler wants to cancel the conversion + */ + public boolean cancel(); + +} diff --git a/source/java/writer2latex/api/Config.java b/source/java/writer2latex/api/Config.java new file mode 100644 index 0000000..3ce8864 --- /dev/null +++ b/source/java/writer2latex/api/Config.java @@ -0,0 +1,99 @@ +/************************************************************************ + * + * Config.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.IllegalArgumentException; + +/** This is an interface for configuration of a {@link Converter}. + * A configuration always supports simple name/value options. + * In addition, you can read and write configurations using streams + * or abstract file names. The format depends on the {@link Converter} + * implementation, cf. the user's manual. + */ +public interface Config { + + /** Read a default configuration: The available configurations depend on the + * {@link Converter} implementation + * + * @param sName the name of the configuration + * @throws IllegalArgumentException if the configuration does not exist + */ + public void readDefaultConfig(String sName) throws IllegalArgumentException; + + /** Read a configuration (stream based version) + * + * @param is the <code>InputStream</code> to read from + * @throws IOException if an error occurs reading the stream, or the data + * is not in the right format + */ + public void read(InputStream is) throws IOException; + + /** Read a configuration (file based version) + * + * @param file the <code>File</code> to read from + * @throws IOException if the file does not exist, an error occurs reading + * the file, or the data is not in the right format + */ + public void read(File file) throws IOException; + + /** Write the configuration (stream based version) + * + * @param os the <code>OutputStream</code> to write to + * @throws IOException if an error occurs writing to the stream + */ + public void write(OutputStream os) throws IOException; + + /** Write the configuration (file based version) + * + * @param file the <code>File</code> to write to + * @throws IOException if an error occurs writing to the file + */ + public void write(File file) throws IOException; + + /** Set a name/value option. Options that are not defined by the + * {@link Converter} implementation as well as null values are + * silently ignored + * + * @param sName the name of the option + * @param sValue the value of the option + */ + public void setOption(String sName, String sValue); + + /** Get a named option + * + * @param sName the name of the option + * @return the value of the option, or <code>null</code> if the option does + * not exist or the given name is null + */ + public String getOption(String sName); + +} + diff --git a/source/java/writer2latex/api/Converter.java b/source/java/writer2latex/api/Converter.java new file mode 100644 index 0000000..2b3315f --- /dev/null +++ b/source/java/writer2latex/api/Converter.java @@ -0,0 +1,99 @@ +/************************************************************************ + * + * Converter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; + +/** This is an interface for a converter, which offers conversion of + * OpenDocument (or OpenOffice.org 1.x) documents into a specific format. + * Instances of this interface are created using the + * <code>ConverterFactory</code> + */ +public interface Converter { + + /** Get the interface for the configuration of this converter + * + * @return the configuration + */ + public Config getConfig(); + + /** Define a <code>GraphicConverter</code> implementation to use for + * conversion of graphic files. If no converter is specified, graphic + * files will not be converted into other formats. + * + * @param gc the <code>GraphicConverter</code> to use + */ + public void setGraphicConverter(GraphicConverter gc); + + /** Read a template to use as a base for the converted document. + * The format of the template depends on the <code>Converter</code> + * implementation. + * + * @param is an <code>InputStream</code> from which to read the template + * @throws IOException if some exception occurs while reading the template + */ + public void readTemplate(InputStream is) throws IOException; + + /** Read a template to use as a base for the converted document. + * The format of the template depends on the <code>Converter</code> + * implementation. + * + * @param file a file from which to read the template + * @throws IOException if the file does not exist or some exception occurs + * while reading the template + */ + public void readTemplate(File file) throws IOException; + + /** Convert a document + * + * @param is an <code>InputStream</code> from which to read the source document. + * @param sTargetFileName the file name to use for the converted document + * (if the converted document is a compound document consisting consisting + * of several files, this name will be used for the master document) + * @return a <code>ConverterResult</code> containing the converted document + * @throws IOException if some exception occurs while reading the document + */ + public ConverterResult convert(InputStream is, String sTargetFileName) + throws IOException; + + /** Convert a document + * + * @param source a <code>File</code> from which to read the source document. + * @param sTargetFileName the file name to use for the converted document + * (if the converted document is a compound document consisting consisting + * of several files, this name will be used for the master document) + * @return a <code>ConverterResult</code> containing the converted document + * @throws FileNotFoundException if the file does not exist + * @throws IOException if some exception occurs while reading the document + */ + public ConverterResult convert(File source, String sTargetFileName) + throws FileNotFoundException, IOException; + +} diff --git a/source/java/writer2latex/api/ConverterFactory.java b/source/java/writer2latex/api/ConverterFactory.java new file mode 100644 index 0000000..3ae2478 --- /dev/null +++ b/source/java/writer2latex/api/ConverterFactory.java @@ -0,0 +1,126 @@ +/************************************************************************ + * + * ConverterFactory.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-18) + * + */ + +package writer2latex.api; + +/** This is a factory class which provides static methods to create converters + * for documents in OpenDocument (or OpenOffice.org 1.x) format into a specific MIME type + */ +public class ConverterFactory { + + // Version information + private static final String VERSION = "0.9.4"; + private static final String DATE = "2008-02-19"; + + /** Return version information + * @return the Writer2LaTeX version in the form + * (major version).(minor version).(development version).(patch level) + */ + public static String getVersion() { return VERSION; } + + /** Return date information + * @return the release date for this Writer2LaTeX version + */ + public static String getDate() { return DATE; } + + /** <p>Create a <code>Converter</code> implementation which supports + * conversion into the specified MIME type.</p> + * <p>Currently supported MIME types are:</p> + * <ul> + * <li><code>application/x-latex</code> for LaTeX format</li> + * <li><code>application/x-bibtex</code> for BibTeX format</li> + * <li><code>text/html</code> for XHTML 1.0 strict format</li> + * <li><code>application/xhtml+xml</code> for XHTML+MathML</li> + * <li><code>application/xml</code> for XHTML+MathML using stylesheets from w3c's + * math working group</li> + * </ul> + * + * @param sMIME the MIME type of the target format + * @return the required <code>Converter</code> or null if a converter for + * the requested MIME type could not be created + */ + public static Converter createConverter(String sMIME) { + Object converter = null; + if (MIMETypes.LATEX.equals(sMIME)) { + converter = createInstance("writer2latex.latex.ConverterPalette"); + } + else if (MIMETypes.BIBTEX.equals(sMIME)) { + converter = createInstance("writer2latex.bibtex.Converter"); + } + else if (MIMETypes.XHTML.equals(sMIME)) { + converter = createInstance("writer2latex.xhtml.Xhtml10Converter"); + } + else if (MIMETypes.XHTML_MATHML.equals(sMIME)) { + converter = createInstance("writer2latex.xhtml.XhtmlMathMLConverter"); + } + else if (MIMETypes.XHTML_MATHML_XSL.equals(sMIME)) { + converter = createInstance("writer2latex.xhtml.XhtmlMathMLXSLConverter"); + } + return converter instanceof Converter ? (Converter) converter : null; + } + + /** <p>Create a <code>BatchConverter</code> implementation which supports + * conversion into the specified MIME type</p> + * <p>The only currently supported MIME type is <code>text/html</code> + * (XHTML 1.0 strict)</p> + * + * @param sMIME the MIME type of the target format + * @return the required <code>BatchConverter</code> or null if a converter + * for the requested MIME type could not be created + */ + public static BatchConverter createBatchConverter(String sMIME) { + Object converter = null; + if (MIMETypes.XHTML.equals(sMIME)) { + converter = createInstance("writer2latex.xhtml.BatchConverterImpl"); + } + return converter instanceof BatchConverter ? (BatchConverter) converter : null; + } + + /** Create a <code>StarMathConverter</code> implementation + * + * @return the converter + */ + public static StarMathConverter createStarMathConverter() { + Object converter = createInstance("writer2latex.latex.StarMathConverter"); + return converter instanceof StarMathConverter ? (StarMathConverter) converter : null; + } + + private static Object createInstance(String sClassName) { + try { + return Class.forName(sClassName).newInstance(); + } + catch (java.lang.ClassNotFoundException e) { + return null; + } + catch (java.lang.InstantiationException e) { + return null; + } + catch (java.lang.IllegalAccessException e) { + return null; + } + } + +} diff --git a/source/java/writer2latex/api/ConverterResult.java b/source/java/writer2latex/api/ConverterResult.java new file mode 100644 index 0000000..eba2e97 --- /dev/null +++ b/source/java/writer2latex/api/ConverterResult.java @@ -0,0 +1,59 @@ +/************************************************************************ + * + * ConverterResult.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-24) + * + */ + +package writer2latex.api; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +/** A <code>ConverterResult</code> represent a document, which is the result + * of a conversion performed by a <code>Converter</code>implementation. + */ +public interface ConverterResult { + + /** Get the master document + * @return <code>OutputFile</code> the master document + */ + public OutputFile getMasterDocument(); + + /** Gets an <code>Iterator</code> to access all files in the + * <code>ConverterResult</code>. This <em>includes</em> the master document. + * @return an <code>Iterator</code> of all files + */ + public Iterator iterator(); + + /** Write all files of the <code>ConverterResult</code> to a directory. + * Subdirectories are created as required by the individual + * <code>OutputFile</code>s. + * @param dir the directory to write to (this directory must exist). + If the parameter is null, the default directory is used + * @throws IOException if the directory does not exist or one or more files + * could not be written + */ + public void write(File dir) throws IOException; + +} diff --git a/source/java/writer2latex/api/GraphicConverter.java b/source/java/writer2latex/api/GraphicConverter.java new file mode 100644 index 0000000..8ccb426 --- /dev/null +++ b/source/java/writer2latex/api/GraphicConverter.java @@ -0,0 +1,58 @@ +/************************************************************************ + * + * GraphicConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +/** A simple interface for a graphic converter which converts between various + * graphics formats + */ +public interface GraphicConverter { + + /** Check whether a certain conversion is supported by the converter + * + * @param sSourceMime a string containing the source Mime type + * @param sTargetMime a string containing the target Mime type + * @param bCrop true if the target graphic should be cropped + * @param bResize true if the target graphic should be resized + * (the last two parameters are for future use) + * @return true if the conversion is supported + */ + public boolean supportsConversion(String sSourceMime, String sTargetMime, boolean bCrop, boolean bResize); + + /** Convert a graphics file from one format to another + * + * @param source a byte array containing the source graphic + * @param sSourceMime a string containing the Mime type of the source + * @param sTargetMime a string containing the desired Mime type of the target + * @return a byte array containing the converted graphic. Returns null + * if the conversion failed. + */ + public byte[] convert(byte[] source, String sSourceMime, String sTargetMime); + +} + + + diff --git a/source/java/writer2latex/api/IndexPageEntry.java b/source/java/writer2latex/api/IndexPageEntry.java new file mode 100644 index 0000000..6d28423 --- /dev/null +++ b/source/java/writer2latex/api/IndexPageEntry.java @@ -0,0 +1,141 @@ +/************************************************************************ + * + * IndexPageEntry.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-08) + * + */ + +package writer2latex.api; + +/** This class represents a single entry on an index page created by a batch converter + */ +public class IndexPageEntry { + + private String sFile; + private String sDisplayName; + private String sDescription = null; + private String sPdfFile = null; + private String sOriginalFile = null; + private boolean bIsDirectory; + + /** Construct a new <code>IndexPageEntry</code> based on a file name. + * The file name is also used as display name. + * + * @param sFile the file name for this entry + * @param bIsDirectory true if this is a directory, false if it is a file + */ + public IndexPageEntry(String sFile, boolean bIsDirectory) { + this.sFile = sFile; + this.sDisplayName = sFile; + this.bIsDirectory = bIsDirectory; + } + + /** Set the file name + * + * @param sFile the file name + */ + public void setFile(String sFile) { + this.sFile = sFile; + } + + /** Set the display name for this entry. The display name is the + * name presented on the index page. + * + * @param sDisplayName the display name + */ + public void setDisplayName(String sDisplayName) { + this.sDisplayName = sDisplayName; + } + + /** Set the description of this file (additional information about the file) + * + * @param sDescription the description + */ + public void setDescription(String sDescription) { + this.sDescription = sDescription; + } + + /** Set the file name for a pdf file associated with this file + * + * @param sPdfFile the file name + */ + public void setPdfFile(String sPdfFile) { + this.sPdfFile = sPdfFile; + } + + /** Set the file name for the original file + * + * @param sOriginalFile the origianl file name + */ + public void setOriginalFile(String sOriginalFile) { + this.sOriginalFile = sOriginalFile; + } + + /** Get the file name + * + * @return the file name + */ + public String getFile() { + return sFile; + } + + /** Get the display name + * + * @return the display name + */ + public String getDisplayName() { + return sDisplayName; + } + + /** Get the description + * + * @return the description, or null if there is no description + */ + public String getDescription() { + return sDescription; + } + + /** Get the pdf file name + * + * @return the file name or null if there is none + */ + public String getPdfFile() { + return sPdfFile; + } + + /** Get the original file name + * + * @return the file name or null if there is none + */ + public String getOriginalFile() { + return sOriginalFile; + } + + /** Check whether this is a file or a directory + * + * @return true for a directory, false for a file + */ + public boolean isDirectory() { + return bIsDirectory; + } + +} diff --git a/source/java/writer2latex/api/MIMETypes.java b/source/java/writer2latex/api/MIMETypes.java new file mode 100644 index 0000000..6f27fed --- /dev/null +++ b/source/java/writer2latex/api/MIMETypes.java @@ -0,0 +1,55 @@ +/************************************************************************ + * + * MIMETypes.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +/* Some helpers to handle the MIME types used by OOo + */ + +public class MIMETypes { + // Various graphics formats, see + // http://api.openoffice.org/docs/common/ref/com/sun/star/graphic/MediaProperties.html#MimeType + public static final String PNG="image/png"; + public static final String JPEG="image/jpeg"; + public static final String GIF="image/gif"; + public static final String TIFF="image/tiff"; + public static final String BMP="image/bmp"; + public static final String WMF="image/x-wmf"; + public static final String EPS="image/x-eps"; + // MIME type for SVM has changed + //public static final String SVM="image/x-svm"; + public static final String SVM="application/x-openoffice-gdimetafile;windows_formatname=\"GDIMetaFile\""; + public static final String PDF="application/pdf"; + + // Desitination formats + public static final String XHTML="text/html"; + public static final String XHTML_MATHML="application/xhtml+xml"; + public static final String XHTML_MATHML_XSL="application/xml"; + public static final String LATEX="application/x-latex"; + public static final String BIBTEX="application/x-bibtex"; + public static final String TEXT="text"; + +} \ No newline at end of file diff --git a/source/java/writer2latex/api/OutputFile.java b/source/java/writer2latex/api/OutputFile.java new file mode 100644 index 0000000..78c94a5 --- /dev/null +++ b/source/java/writer2latex/api/OutputFile.java @@ -0,0 +1,53 @@ +/************************************************************************ + * + * OutputFile.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +import java.io.OutputStream; +import java.io.IOException; + +/** An <code>OutputFile</code> represent a single file in a + * {@link ConverterResult}, which is output from a {@link Converter} + * implementation. + */ +public interface OutputFile { + + /** Writes the <code>OutputFile</code> to an <code>OutputStream</code>. + * + * @param os <code>OutputStream</code> to which the content should be written + * @throws IOException if any I/O error occurs + */ + public void write(OutputStream os) throws IOException; + + /** Returns the file name of the <code>OutputFile</code>. This includes + * the file extension and may also include a relative path, always using + * / as separator. + * + * @return the file name of this <code>OutputFile</code> + */ + public String getFileName(); + +} diff --git a/source/java/writer2latex/api/Package.html b/source/java/writer2latex/api/Package.html new file mode 100644 index 0000000..9dbd1da --- /dev/null +++ b/source/java/writer2latex/api/Package.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.api</title> +</head> + +<body> +<p>This package contains the api for using Writer2LaTeX.</p> +<p>The api consitst of a factory class and a number of interfaces, and is +used by the command line application as well as by the filters. +</body> +</html> diff --git a/source/java/writer2latex/api/StarMathConverter.java b/source/java/writer2latex/api/StarMathConverter.java new file mode 100644 index 0000000..1492a62 --- /dev/null +++ b/source/java/writer2latex/api/StarMathConverter.java @@ -0,0 +1,61 @@ +/************************************************************************ + * + * StarMathConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.api; + +//import java.io.InputStream; +//import java.io.IOException; + +/** This is an interface for a converter, which offers conversion of + * a StarMath formula into LaTeX + * Instances of this interface are created using the + * {@link ConverterFactory} + */ +public interface StarMathConverter { + + /** Get the configuration used when converting. + * + * @return the configuration used by this converter + */ + public Config getConfig(); + + /** Convert a StarMath formula + * + * @param sStarMathFormula is a string containing the StarMath formula + * @return a string containing the converted LaTeX formula + */ + public String convert(String sStarMathFormula); + + /** Create a suitable LaTeX preamble to process the formulas converted so far + * + * @return a string containg the entire LaTeX preamble + */ + public String getPreamble(); + +} + + + diff --git a/source/java/writer2latex/base/BatchConverterBase.java b/source/java/writer2latex/base/BatchConverterBase.java new file mode 100644 index 0000000..cebf051 --- /dev/null +++ b/source/java/writer2latex/base/BatchConverterBase.java @@ -0,0 +1,182 @@ +/************************************************************************ + * + * BatchConverterBase.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-10-15) + * + */ + +package writer2latex.base; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +import writer2latex.api.BatchConverter; +import writer2latex.api.BatchHandler; +import writer2latex.api.Converter; +import writer2latex.api.ConverterResult; +import writer2latex.api.IndexPageEntry; +import writer2latex.api.OutputFile; + +import writer2latex.util.Misc; + +/** + * Abstract base implementation of <code>writer2latex.api.BatchConverter</code>. + * The base implementation handles the traversal of directories and files, and + * leaves the handling of indexpages to the subclass. + */ +public abstract class BatchConverterBase implements BatchConverter { + + private Converter converter; + + public BatchConverterBase() { + converter = null; + } + + // Partial implementation of the interface + + public void setConverter(Converter converter) { + this.converter = converter; + } + + public void convert(File source, File target, boolean bRecurse, BatchHandler handler) { + handler.startConversion(); + convertDirectory(source, target, bRecurse, source.getName(), handler); + handler.endConversion(); + } + + protected abstract String getIndexFileName(); + + // Convert files and directories in the directory indir + // (return false if conversion has been cancelled by the BatchHandler) + private boolean convertDirectory(File indir, File outdir, boolean bRecurse, String sHeading, BatchHandler handler) { + handler.startDirectory(indir.getPath()); + + // Step 1: Get the directory + File[] contents = indir.listFiles(); + int nLen = contents.length; + IndexPageEntry[] entries = new IndexPageEntry[nLen]; + + // Step 2: Traverse subdirectories, if allowed + if (bRecurse) { + String sUplink = getConfig().getOption("uplink"); + for (int i=0; i<nLen; i++) { + if (contents[i].isDirectory()) { + getConfig().setOption("uplink","../"+getIndexFileName()); + File newOutdir = new File(outdir,contents[i].getName()); + String sNewHeading = sHeading + " - " + contents[i].getName(); + boolean bResult = convertDirectory(contents[i],newOutdir,bRecurse,sNewHeading,handler); + getConfig().setOption("uplink", sUplink); + if (!bResult) { return false; } + // Create entry for this subdirectory + IndexPageEntry entry = new IndexPageEntry(Misc.makeHref(contents[i].getName()+"/"+getIndexFileName()),true); + entry.setDisplayName(contents[i].getName()); + entries[i]=entry; + } + } + } + + // Step 3: Traverse documents, if we have a converter + if (converter!=null) { + String sUplink = getConfig().getOption("uplink"); + for (int i=0; i<nLen; i++) { + if (contents[i].isFile()) { + getConfig().setOption("uplink",getIndexFileName()); + String sLinkFile = convertFile(contents[i],outdir,handler); + getConfig().setOption("uplink", sUplink); + if (sLinkFile!=null) { + // Create entry for this file + IndexPageEntry entry = new IndexPageEntry(Misc.makeHref(sLinkFile),false); + entry.setDisplayName(Misc.removeExtension(sLinkFile)); + entries[i]=entry; + if (handler.cancel()) { return false; } + } + } + } + } + + // Step 4: Create and write out the index file + OutputFile indexFile = createIndexFile(sHeading, entries); + + if (!outdir.exists()) { outdir.mkdirs(); } + + boolean bSuccess = true; + File outfile = new File(outdir,indexFile.getFileName()); + try { + FileOutputStream fos = new FileOutputStream(outfile); + indexFile.write(fos); + fos.flush(); + fos.close(); + } catch (Exception writeExcept) { + bSuccess = false; + } + + handler.endDirectory(indir.getPath(), bSuccess); + + return !handler.cancel(); + } + + // Convert a single file, returning the name of the master file + // Returns null if conversion fails + private String convertFile(File infile, File outdir, BatchHandler handler) { + handler.startFile(infile.getPath()); + + // Currently we discriminate based on file extension + if (!(infile.getName().endsWith(".odt") || infile.getName().endsWith(".ods") || infile.getName().endsWith(".odp"))) { + handler.endFile(infile.getPath(),false); + return null; + } + + // Do conversion + ConverterResult dataOut = null; + try { + // The target file name is always the same as the source + dataOut = converter.convert(infile,Misc.removeExtension(infile.getName())); + } + catch (FileNotFoundException e) { + handler.endFile(infile.getPath(),false); + return null; + } + catch (IOException e) { + handler.endFile(infile.getPath(),false); + return null; + } + + // Write out files + if (!outdir.exists()) { outdir.mkdirs(); } + + try { + dataOut.write(outdir); + } + catch (IOException e) { + handler.endFile(infile.getPath(),false); + return null; + } + + handler.endFile(infile.getPath(),true); + + return dataOut.getMasterDocument().getFileName(); + } + + +} diff --git a/source/java/writer2latex/base/BooleanOption.java b/source/java/writer2latex/base/BooleanOption.java new file mode 100644 index 0000000..83bb885 --- /dev/null +++ b/source/java/writer2latex/base/BooleanOption.java @@ -0,0 +1,44 @@ +/************************************************************************ + * + * BooleanOption.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.base; + +// A BooleanOption interprets the values as booleans +public class BooleanOption extends Option { + private boolean bValue; + + public boolean getValue() { return bValue; } + + public void setString(String sValue) { + super.setString(sValue); + bValue = "true".equals(sValue); + } + + public BooleanOption(String sName, String sDefaultValue) { + super(sName,sDefaultValue); + } +} + diff --git a/source/java/writer2latex/base/ConfigBase.java b/source/java/writer2latex/base/ConfigBase.java new file mode 100644 index 0000000..6cc4223 --- /dev/null +++ b/source/java/writer2latex/base/ConfigBase.java @@ -0,0 +1,170 @@ +/************************************************************************ + * + * ConfigBase.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.base; + +/** Base implementation of writer2latex.api.Config +*/ + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.DOMImplementation; + +import writer2latex.xmerge.NewDOMDocument; + +public abstract class ConfigBase implements writer2latex.api.Config { + + protected abstract int getOptionCount(); + protected abstract String getDefaultConfigPath(); + + protected Option[] options; + + public ConfigBase() { + options = new Option[getOptionCount()]; + } + + public void setOption(String sName,String sValue) { + if (sName!=null && sValue!=null) { + for (int j=0; j<getOptionCount(); j++) { + if (sName.equals(options[j].getName())) { + options[j].setString(sValue); + break; + } + } + } + } + + public String getOption(String sName) { + if (sName!=null) { + for (int j=0; j<getOptionCount(); j++) { + if (sName.equals(options[j].getName())) { + return options[j].getString(); + } + } + } + return null; + } + + public void readDefaultConfig(String sName) throws IllegalArgumentException { + InputStream is = this.getClass().getResourceAsStream(getDefaultConfigPath()+sName); + if (is==null) { + throw new IllegalArgumentException("The internal configuration '"+sName+ "' does not exist"); + } + try { + read(is); + } + catch (IOException e) { + // This would imply a bug in the configuration file! + throw new IllegalArgumentException("The internal configuration '"+sName+ "' is invalid"); + } + } + + + /** <p>Read configuration from a specified input stream</p> + * @param is the input stream to read the configuration from + */ + public void read(InputStream is) throws IOException { + NewDOMDocument doc = new NewDOMDocument("config",".xml"); + doc.read(is); // may throw an IOException + Document dom = doc.getContentDOM(); + if (dom==null) { + throw new IOException("Failed to parse configuration"); + } + + Node root = dom.getDocumentElement(); + Node child = root.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + Element elm = (Element)child; + if (elm.getTagName().equals("option")) { + String sName = elm.getAttribute("name"); + String sValue = elm.getAttribute("value"); + if (sName!="") { setOption(sName,sValue); } + } + else { + readInner(elm); + } + } + child = child.getNextSibling(); + } + } + + public void read(File file) throws IOException { + read(new FileInputStream(file)); + } + + /** Read configuration information from an xml element. + * The subclass must define this to read richer configuration data + */ + protected abstract void readInner(Element elm); + + public void write(OutputStream os) throws IOException { + NewDOMDocument doc = new NewDOMDocument("config",".xml"); + Document dom = null; + try { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + DOMImplementation domImpl = builder.getDOMImplementation(); + dom = domImpl.createDocument("","config",null); + } + catch (Throwable t) { + t.printStackTrace(); + } + Element rootElement = dom.getDocumentElement(); + + for (int i=0; i<getOptionCount(); i++) { + Element optionNode = dom.createElement("option"); + optionNode.setAttribute("name",options[i].getName()); + optionNode.setAttribute("value",options[i].getString()); + rootElement.appendChild(optionNode); + } + + writeInner(dom); + + doc.setContentDOM(dom); + doc.write(os); // may throw an IOException + } + + public void write(File file) throws IOException { + write(new FileOutputStream(file)); + } + + /** Write configuration information to an xml document. + * The subclass must define this to write richer configuration data + */ + protected abstract void writeInner(Document dom); + +} + diff --git a/source/java/writer2latex/base/ConverterBase.java b/source/java/writer2latex/base/ConverterBase.java new file mode 100644 index 0000000..073d5d3 --- /dev/null +++ b/source/java/writer2latex/base/ConverterBase.java @@ -0,0 +1,117 @@ +/************************************************************************ + * + * ConverterBase.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.base; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.IOException; + +import writer2latex.api.GraphicConverter; +import writer2latex.api.Converter; +import writer2latex.api.ConverterResult; +import writer2latex.api.OutputFile; +import writer2latex.office.ImageLoader; +import writer2latex.office.MetaData; +import writer2latex.office.OfficeReader; +import writer2latex.xmerge.EmbeddedObject; +import writer2latex.xmerge.ConvertData; +import writer2latex.xmerge.OfficeDocument; + +/**<p>Abstract base implementation of <code>writer2latex.api.Converter</code></p> + */ +public abstract class ConverterBase implements Converter { + + // Helper + protected GraphicConverter graphicConverter; + + // The source document + protected OfficeDocument odDoc; + protected OfficeReader ofr; + protected MetaData metaData; + protected ImageLoader imageLoader; + + // The output file(s) + protected String sTargetFileName; + protected ConvertData convertData; + + // Constructor + public ConverterBase() { + graphicConverter = null; + convertData = new ConvertData(); + } + + // Implement the interface + public void setGraphicConverter(GraphicConverter graphicConverter) { + this.graphicConverter = graphicConverter; + } + + // Provide a do noting fallback method + public void readTemplate(InputStream is) throws IOException { } + + // Provide a do noting fallback method + public void readTemplate(File file) throws IOException { } + + public ConverterResult convert(File source, String sTargetFileName) throws FileNotFoundException,IOException { + return convert(new FileInputStream(source), sTargetFileName); + } + + public ConverterResult convert(InputStream is, String sTargetFileName) throws IOException { + // Read document + odDoc = new OfficeDocument("InFile"); + odDoc.read(is); + ofr = new OfficeReader(odDoc,false); + metaData = new MetaData(odDoc); + imageLoader = new ImageLoader(odDoc,sTargetFileName,true); + imageLoader.setGraphicConverter(graphicConverter); + + // Prepare output + this.sTargetFileName = sTargetFileName; + convertData.reset(); + + convertInner(); + + return convertData; + } + + // The subclass must provide the implementation + public abstract void convertInner() throws IOException; + + public MetaData getMetaData() { return metaData; } + + public ImageLoader getImageLoader() { return imageLoader; } + + public void addDocument(OutputFile doc) { convertData.addDocument(doc); } + + public EmbeddedObject getEmbeddedObject(String sHref) { + return odDoc.getEmbeddedObject(sHref); + } + + + +} \ No newline at end of file diff --git a/source/java/writer2latex/base/IntegerOption.java b/source/java/writer2latex/base/IntegerOption.java new file mode 100644 index 0000000..3fb59a3 --- /dev/null +++ b/source/java/writer2latex/base/IntegerOption.java @@ -0,0 +1,40 @@ +/************************************************************************ + * + * IntegerOption.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.base; + + +// An IntegerOption must always be subclassed (must override setString) +public abstract class IntegerOption extends Option { + protected int nValue; + + public int getValue() { return nValue; } + + public IntegerOption(String sName, String sDefaultValue) { + super(sName,sDefaultValue); + } +} + diff --git a/source/java/writer2latex/base/Option.java b/source/java/writer2latex/base/Option.java new file mode 100644 index 0000000..458ef50 --- /dev/null +++ b/source/java/writer2latex/base/Option.java @@ -0,0 +1,45 @@ +/************************************************************************ + * + * Option.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.base; + +// The mother of all options; reads and writes string values +public class Option { + protected String sValue; + private String sName; + + public void setString(String sValue) { this.sValue = sValue; } + + public String getString() { return sValue; } + + public String getName() { return sName; } + + public Option(String sName, String sDefaultValue) { + this.sName = sName; + setString(sDefaultValue); + } +} + diff --git a/source/java/writer2latex/base/Package.html b/source/java/writer2latex/base/Package.html new file mode 100644 index 0000000..e57ff2b --- /dev/null +++ b/source/java/writer2latex/base/Package.html @@ -0,0 +1,17 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.base</title> +</head> + +<body> +<p>This package contains (abstract) base implementations of some of the +interfaces in writer2latex.api</p> + +<p>They are intended to be subclassed by converters into specific formats +e.g. LaTeX, xhtml</p> + + +</body> +</html> diff --git a/source/java/writer2latex/bibtex/BibTeXDocument.java b/source/java/writer2latex/bibtex/BibTeXDocument.java new file mode 100644 index 0000000..8411de1 --- /dev/null +++ b/source/java/writer2latex/bibtex/BibTeXDocument.java @@ -0,0 +1,238 @@ +/************************************************************************ + * + * BibTeXDocument.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-17) + * + */ + +package writer2latex.bibtex; + +import writer2latex.xmerge.Document; + +import java.util.Hashtable; +import java.util.Enumeration; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import writer2latex.api.ConverterFactory; +import writer2latex.latex.LaTeXConfig; +import writer2latex.latex.i18n.ClassicI18n; +import writer2latex.latex.i18n.I18n; +import writer2latex.util.ExportNameCollection; +//import writer2latex.util.Misc; +import writer2latex.office.BibMark; + +/** + * <p>Class representing a BibTeX document.</p> + * + */ +public class BibTeXDocument implements Document { + private static final String FILE_EXTENSION = ".bib"; + + private String sName; + private Hashtable entries = new Hashtable(); + private ExportNameCollection exportNames = new ExportNameCollection(true); + private I18n i18n; + + /** + * <p>Constructs a new BibTeX Document.</p> + * + * <p>This new document is empty. Bibliographic data must added + * using the <code>put</code> method.</p> + * + * @param sName The name of the <code>BibTeXDocument</code>. + */ + public BibTeXDocument(String sName) { + this.sName = trimDocumentName(sName); + // Use default config (only ascii, no extra font packages) + i18n = new ClassicI18n(new LaTeXConfig()); + } + + /** + * <p>This method is supposed to read <code>byte</code> data from the InputStream. + * Currently it does nothing, since we don't need it.</p> + * + * @param is InputStream containing a BibTeX data file. + * + * @throws IOException In case of any I/O errors. + */ + public void read(InputStream is) throws IOException { + // Do nothing. + } + + + /** + * <p>Returns the <code>Document</code> name with no file extension.</p> + * + * @return The <code>Document</code> name with no file extension. + */ + public String getName() { + return sName; + } + + + /** + * <p>Returns the <code>Document</code> name with file extension.</p> + * + * @return The <code>Document</code> name with file extension. + */ + public String getFileName() { + return new String(sName + FILE_EXTENSION); + } + + + /** + * <p>Writes out the <code>Document</code> content to the specified + * <code>OutputStream</code>.</p> + * + * <p>This method may not be thread-safe. + * Implementations may or may not synchronize this + * method. User code (i.e. caller) must make sure that + * calls to this method are thread-safe.</p> + * + * @param os <code>OutputStream</code> to write out the + * <code>Document</code> content. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + // BibTeX files are plain ascii + OutputStreamWriter osw = new OutputStreamWriter(os,"ASCII"); + osw.write("%% This file was converted to BibTeX by Writer2BibTeX ver. "+ConverterFactory.getVersion()+".\n"); + osw.write("%% See http://www.hj-gym.dk/~hj/writer2latex for more info.\n"); + osw.write("\n"); + Enumeration enumeration = entries.elements(); + while (enumeration.hasMoreElements()) { + BibMark entry = (BibMark) enumeration.nextElement(); + osw.write("@"); + osw.write(entry.getEntryType().toUpperCase()); + osw.write("{"); + osw.write(exportNames.getExportName(entry.getIdentifier())); + osw.write(",\n"); + for (int i=0; i<BibMark.FIELD_COUNT; i++) { + String sValue = entry.getField(i); + if (sValue!=null) { + if (i==BibMark.AUTHOR || i==BibMark.EDITOR) { + // OOo uses ; to separate authors and editors - BibTeX uses and + sValue = sValue.replaceAll(";" , " and "); + } + osw.write(" "); + osw.write(getFieldName(i).toUpperCase()); + osw.write(" = {"); + for (int j=0; j<sValue.length(); j++) { + String s = i18n.convert(Character.toString(sValue.charAt(j)),false,"en"); + if (s.charAt(0)=='\\') { osw.write("{"); } + osw.write(s); + if (s.charAt(0)=='\\') { osw.write("}"); } + } + osw.write("},\n"); + } + } + osw.write("}\n\n"); + } + osw.flush(); + osw.close(); + } + + /** + * <p> Return BibTeX name of field </p> + */ + public static final String getFieldName(int nField) { + switch (nField) { + case BibMark.ADDRESS: return "address"; + case BibMark.ANNOTE: return "annote"; + case BibMark.AUTHOR: return "author"; + case BibMark.BOOKTITLE: return "booktitle"; + case BibMark.CHAPTER: return "chapter"; + // case BibMark.CROSSREF: return "croosref"; // not in OOo + case BibMark.EDITION: return "edition"; + case BibMark.EDITOR: return "editor"; + case BibMark.HOWPUBLISHED: return "howpublished"; + case BibMark.INSTITUTION: return "institution"; + case BibMark.JOURNAL: return "journal"; + // case BibMark.KEY: return "key"; // not in OOo + case BibMark.MONTH: return "month"; + case BibMark.NOTE: return "note"; + case BibMark.NUMBER: return "number"; + case BibMark.ORGANIZATIONS: return "organization"; + case BibMark.PAGES: return "pages"; + case BibMark.PUBLISHER: return "publisher"; + case BibMark.SCHOOL: return "school"; + case BibMark.SERIES: return "series"; + case BibMark.TITLE: return "title"; + case BibMark.REPORT_TYPE: return "type"; + case BibMark.VOLUME: return "volume"; + case BibMark.YEAR: return "year"; + case BibMark.URL: return "url"; + case BibMark.CUSTOM1: return "custom1"; + case BibMark.CUSTOM2: return "custom2"; + case BibMark.CUSTOM3: return "custom3"; + case BibMark.CUSTOM4: return "custom4"; + case BibMark.CUSTOM5: return "custom5"; + case BibMark.ISBN: return "isbn"; + default: return null; + } + } + + + /* + * <p>Check if this entry exists</p> + */ + public boolean containsKey(String sIdentifier) { + return entries.containsKey(sIdentifier); + } + + /* + * <p>Add an entry</p> + */ + public void put(BibMark entry) { + entries.put(entry.getIdentifier(),entry); + exportNames.addName(entry.getIdentifier()); + } + + /* + * <p>Get export name for an identifier</p> + */ + public String getExportName(String sIdentifier) { + return exportNames.getExportName(sIdentifier); + } + + /* + * Utility method to make sure the document name is stripped of any file + * extensions before use. + */ + private String trimDocumentName(String name) { + String temp = name.toLowerCase(); + + if (temp.endsWith(FILE_EXTENSION)) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - FILE_EXTENSION.length(); + name = name.substring(0,endIndex); + } + + return name; + } +} + \ No newline at end of file diff --git a/source/java/writer2latex/bibtex/Converter.java b/source/java/writer2latex/bibtex/Converter.java new file mode 100644 index 0000000..b2b3b2e --- /dev/null +++ b/source/java/writer2latex/bibtex/Converter.java @@ -0,0 +1,91 @@ +/************************************************************************ + * + * Converter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2001-2008 by Henrik Just + * + * All Rights Reserved. + * + * version 1.0 (2008-11-22) + * + */ + +package writer2latex.bibtex; + +import writer2latex.api.Config; +//import writer2latex.api.ConverterResult; +import writer2latex.base.ConverterBase; +import writer2latex.latex.LaTeXConfig; +import writer2latex.office.BibMark; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; + +//import writer2latex.xmerge.ConvertData; +//import writer2latex.xmerge.OfficeDocument; + +import java.io.IOException; + +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +/** + * <p>BibTeX export</p> + * + * <p>This class extracts bibliographic information from an OpenDocument text file to a BibTeX data file.</p> + * + */ +public final class Converter extends ConverterBase { + + // Configuration - TODO: Doesn't really use it - should use some fake config + private LaTeXConfig config; + + public Config getConfig() { return config; } + + // Constructor + public Converter() { + super(); + config = new LaTeXConfig(); + } + + /** + * <p>Convert the data passed into the <code>InputStream</code> + * into BibTeX format.</p> + * + * @throws IOException If any I/O error occurs. + */ + public void convertInner() throws IOException { + sTargetFileName = Misc.trimDocumentName(sTargetFileName,".bib"); + + BibTeXDocument bibDoc = new BibTeXDocument(sTargetFileName); + + // Collect all text:bibliography-mark elements from the content + Element doc = ofr.getContent(); + NodeList list; + list = doc.getElementsByTagName(XMLString.TEXT_BIBLIOGRAPHY_MARK); + int nLen = list.getLength(); + for (int i=0; i<nLen; i++) { + String sIdentifier = Misc.getAttribute(list.item(i),XMLString.TEXT_IDENTIFIER); + if (sIdentifier!=null && !bibDoc.containsKey(sIdentifier)) { + bibDoc.put(new BibMark(list.item(i))); + } + } + + // Add result + convertData.addDocument(bibDoc); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/bibtex/Package.html b/source/java/writer2latex/bibtex/Package.html new file mode 100644 index 0000000..652abe9 --- /dev/null +++ b/source/java/writer2latex/bibtex/Package.html @@ -0,0 +1,14 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.bibtex</title> +</head> + +<body> +<p>This package contains BibTeX specific code.</p> +<p>It contains a <code>writerlatex.api.Converter</code> implementation for +conversion into BibTeX, as well as code to convert to BibTeX as part of a +conversion into LaTeX.</p> +</body> +</html> diff --git a/source/java/writer2latex/latex/BibConverter.java b/source/java/writer2latex/latex/BibConverter.java new file mode 100644 index 0000000..3f344c4 --- /dev/null +++ b/source/java/writer2latex/latex/BibConverter.java @@ -0,0 +1,157 @@ +/************************************************************************ + * + * BibConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; + +import writer2latex.bibtex.BibTeXDocument; + +import writer2latex.latex.util.Context; + +import writer2latex.office.BibMark; +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; + +import writer2latex.util.Misc; + +/** + * This class handles the bibliography. The result depends on these + * configuration options. The citations will be treated like this: + * <ul> + * <li><code>use_bibtex</code>: If true, citations will be exported as \cite + * commands. If false, citations will be exported as static text</li> + * </ul> + * The bibliography will be treated like this: + * <ul> + * <li><code>use_index</code>: If false, the bibliography will be omitted</li> + * <li><code>use_bibtex</code> true and <code>external_bibtex_files</code> + * empty: The citations will be exported to a BibTeX file, which will be used + * for the bibliography</li> + * <li><code>use_bibtex</code> true and <code>external_bibtex_files</code> + * non-empty: The citations will be not be exported to a BibTeX file, the + * files referred to by the option will be used instead</li> + * <li><code>use_bibtex</code> false: The bibliography will be exported as + * static text. + * <li><code>bibtex_style</code> If BibTeX is used, this style will be applied + * </ul> + */ +public class BibConverter extends ConverterHelper { + + private BibTeXDocument bibDoc; + + /** Construct a new BibConverter. + * @param config the configuration to use + * @param palette the ConverterPalette to use + */ + public BibConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + /** Append declarations needed by the <code>BibConverter</code> to + * the preamble. + * @param pack the LaTeXDocumentPortion to which + * declarations of packages should be added (\\usepackage). + * @param decl the LaTeXDocumentPortion to which + * other declarations should be added. + */ + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + // Currently nothing; may add support for eg. natbib later + } + + /** Process a bibliography (text:bibliography tag) + * @param node The element containing the Bibliography + * @param ldp the LaTeXDocumentPortion to which LaTeX code should be added + * @param oc the current context + */ + public void handleBibliography (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.noIndex()) { return; } + if (config.useBibtex()) { + // Use the style given in the configuration + // TODO: Create a bst file from the settings of the text:bibliography + ldp.append("\\bibliographystyle{") + .append(config.bibtexStyle()) + .append("}").nl(); + + // Use BibTeX file from configuration, or exported BibTeX file + if (config.externalBibtexFiles().length()>0) { + ldp.append("\\bibliography{") + .append(config.externalBibtexFiles()) + .append("}").nl(); + } + else { + if (bibDoc==null) { bibDoc = new BibTeXDocument(palette.getOutFileName()); } + ldp.append("\\bibliography{") + .append(bibDoc.getName()) + .append("}").nl(); + } + } + else { // typeset current content + Element body = Misc.getChildByTagName(node,XMLString.TEXT_INDEX_BODY); + if (body!=null) { + Element title = Misc.getChildByTagName(body,XMLString.TEXT_INDEX_TITLE); + if (title!=null) { palette.getBlockCv().traverseBlockText(title,ldp,oc); } + palette.getBlockCv().traverseBlockText(body,ldp,oc); + } + } + } + + /** Process a Bibliography Mark (text:bibliography-mark tag) + * @param node The element containing the Mark + * @param ldp the LaTeXDocumentPortion to which LaTeX code should be added + * @param oc the current context + */ + public void handleBibliographyMark(Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.useBibtex()) { + String sIdentifier = node.getAttribute(XMLString.TEXT_IDENTIFIER); + if (sIdentifier!=null) { + if (config.externalBibtexFiles().length()==0) { + if (bibDoc==null) { bibDoc = new BibTeXDocument(palette.getOutFileName()); } + if (!bibDoc.containsKey(sIdentifier)) { + bibDoc.put(new BibMark(node)); + } + } + ldp.append("\\cite{") + .append(bibDoc.getExportName(sIdentifier)) + .append("}"); + } + } + else { // use current value + palette.getInlineCv().traverseInlineText(node,ldp,oc); + } + } + + /** Get the BibTeX document, if any (the document is only created if it's + * specified in the configuration *and* the document contains bibliographic + * data *and* the configuration does not specify external BibTeX files + * @return the BiBTeXDocument, or null if it does not exist). + */ + public BibTeXDocument getBibTeXDocument () { + return bibDoc; + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/BlockConverter.java b/source/java/writer2latex/latex/BlockConverter.java new file mode 100644 index 0000000..182c8c1 --- /dev/null +++ b/source/java/writer2latex/latex/BlockConverter.java @@ -0,0 +1,378 @@ +/************************************************************************ + * + * BlockConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; +//import writer2latex.latex.util.HeadingMap; +import writer2latex.latex.util.StyleMap; +import writer2latex.office.ListStyle; +import writer2latex.office.OfficeReader; +//import writer2latex.office.TableReader; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; + +/** + * <p>This class handles basic block content, including the main text body, + * sections, tables, lists, headings and paragraphs.</p> + */ +public class BlockConverter extends ConverterHelper { + + public BlockConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + // currently do nothing.. + } + + + /** <p> Traverse block text (eg. content of body, section, list item). + * This is traversed in logical order and dedicated handlers take care of + * each block element.</p> + * <p> (Note: As a rule, all handling of block level elements should add a + * newline to the LaTeX document at the end of the block)</p> + * @param node The element containing the block text + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void traverseBlockText(Element node, LaTeXDocumentPortion ldp, Context oc) { + Context ic = (Context) oc.clone(); + + // The current paragraph block: + StyleMap blockMap = config.getParBlockStyleMap(); + String sBlockName = null; + + if (node.hasChildNodes()) { + NodeList list = node.getChildNodes(); + int nLen = list.getLength(); + + for (int i = 0; i < nLen; i++) { + Node childNode = list.item(i); + + if (childNode.getNodeType() == Node.ELEMENT_NODE) { + Element child = (Element)childNode; + String sTagName = child.getTagName(); + + // Start/End a paragraph block (not in tables) + if (!ic.isInTable()) { + if (sTagName.equals(XMLString.TEXT_P)) { + String sStyleName = ofr.getParStyles().getDisplayName(child.getAttribute(XMLString.TEXT_STYLE_NAME)); + if (sBlockName!=null && !blockMap.isNext(sBlockName,sStyleName)) { + // end current block + String sAfter = blockMap.getAfter(sBlockName); + if (sAfter.length()>0) ldp.append(sAfter).nl(); + sBlockName = null; + ic.setVerbatim(false); + } + if (sBlockName==null && blockMap.contains(sStyleName)) { + // start a new block + sBlockName = sStyleName; + String sBefore = blockMap.getBefore(sBlockName); + if (sBefore.length()>0) ldp.append(sBefore).nl(); + ic.setVerbatim(blockMap.getVerbatim(sStyleName)); + } + } + else if (sBlockName!=null) { + // non-paragraph: end current block + String sAfter = blockMap.getAfter(sBlockName); + if (sAfter.length()>0) ldp.append(sAfter).nl(); + sBlockName = null; + ic.setVerbatim(false); + } + } + + palette.getFieldCv().flushReferenceMarks(ldp,ic); + palette.getIndexCv().flushIndexMarks(ldp,ic); + + palette.getInfo().addDebugInfo(child,ldp); + + // Basic block content; handle by this class + if (sTagName.equals(XMLString.TEXT_P)) { + // is this a caption? + String sSequence = ofr.getSequenceName(child); + if (ofr.isFigureSequenceName(sSequence)) { + palette.getDrawCv().handleCaption(child,ldp,ic); + } + else if (ofr.isTableSequenceName(sSequence)) { + // Next node *should* be a table + if (i+1<nLen && Misc.isElement(list.item(i+1),XMLString.TABLE_TABLE)) { + // Found table with caption above + palette.getTableCv().handleTable((Element)list.item(++i),child,true,ldp,ic); + } + else { + // Found lonely caption + palette.getTableCv().handleCaption(child,ldp,ic); + } + } + else { + palette.getParCv().handleParagraph(child,ldp,ic,i==nLen-1); + } + } + + else if(sTagName.equals(XMLString.TEXT_H)) { + palette.getHeadingCv().handleHeading(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_LIST)) { // oasis + handleList(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_UNORDERED_LIST)) { + handleList(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_ORDERED_LIST)) { + handleList(child,ldp,ic); + } + else if (sTagName.equals(XMLString.TABLE_TABLE)) { + // Next node *could* be a caption + if (i+1<nLen && Misc.isElement(list.item(i+1),XMLString.TEXT_P) && + ofr.isTableSequenceName(ofr.getSequenceName((Element)list.item(i+1)))) { + // Found table with caption below + palette.getTableCv().handleTable(child,(Element)list.item(++i),false,ldp,oc); + } + else { + // Found table without caption + palette.getTableCv().handleTable(child,null,false,ldp,oc); + } + } + + else if (sTagName.equals(XMLString.TABLE_SUB_TABLE)) { + palette.getTableCv().handleTable(child,null,true,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_SECTION)) { + palette.getSectionCv().handleSection(child,ldp,ic); + } + + // Draw elements may appear in block context if they are + // anchored to page + else if (sTagName.startsWith("draw:")) { + palette.getDrawCv().handleDrawElement(child,ldp,ic); + } + + // Indexes + else if (sTagName.equals(XMLString.TEXT_TABLE_OF_CONTENT)) { + palette.getIndexCv().handleTOC(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_ILLUSTRATION_INDEX)) { + palette.getIndexCv().handleLOF(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_TABLE_INDEX)) { + palette.getIndexCv().handleLOT(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_OBJECT_INDEX)) { + palette.getIndexCv().handleObjectIndex(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_USER_INDEX)) { + palette.getIndexCv().handleUserIndex(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_ALPHABETICAL_INDEX)) { + palette.getIndexCv().handleAlphabeticalIndex(child,ldp,ic); + } + + else if (sTagName.equals(XMLString.TEXT_BIBLIOGRAPHY)) { + palette.getBibCv().handleBibliography(child,ldp,ic); + } + + // Sequence declarations appear in the main text body (before the actual content) + else if (sTagName.equals(XMLString.TEXT_SEQUENCE_DECLS)) { + palette.getFieldCv().handleSequenceDecls(child); + } + // other tags are ignored + } + } + } + + if (!oc.isInTable() && sBlockName!=null) { + // end current block + String sAfter = blockMap.getAfter(sBlockName); + if (sAfter.length()>0) ldp.append(sAfter).nl(); + sBlockName = null; + } + palette.getFieldCv().flushReferenceMarks(ldp,ic); + palette.getIndexCv().flushIndexMarks(ldp,ic); + + + } + + + /** <p> Process a list (text:ordered-lst or text:unordered-list tag)</p> + * @param node The element containing the list + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleList(Element node, LaTeXDocumentPortion ldp, Context oc) { + // Set up new context + Context ic = (Context) oc.clone(); + ic.incListLevel(); + + // Get the style name, if we don't know it already + if (ic.getListStyleName()==null) { + ic.setListStyleName(node.getAttribute(XMLString.TEXT_STYLE_NAME)); + } + + // Use the style to determine the type of list + ListStyle style = ofr.getListStyle(ic.getListStyleName()); + boolean bOrdered = style!=null && style.isNumber(ic.getListLevel()); + + // If the list contains headings, ignore it! + if (ic.isIgnoreLists() || listContainsHeadings(node)) { + ic.setIgnoreLists(true); + traverseList(node,ldp,ic); + return; + } + + // Apply the style + BeforeAfter ba = new BeforeAfter(); + palette.getListSc().applyListStyle(ic.getListStyleName(),ic.getListLevel(), + bOrdered,"true".equals(node.getAttribute(XMLString.TEXT_CONTINUE_NUMBERING)), + ba); + + // Export the list + if (ba.getBefore().length()>0) { ldp.append(ba.getBefore()).nl(); } + traverseList(node,ldp,ic); + if (ba.getAfter().length()>0) { ldp.append(ba.getAfter()).nl(); } + } + + /* + * Process the contents of a list + */ + private void traverseList (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (node.hasChildNodes()) { + NodeList list = node.getChildNodes(); + int nLen = list.getLength(); + + for (int i = 0; i < nLen; i++) { + Node child = list.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + palette.getInfo().addDebugInfo((Element)child,ldp); + + if (nodeName.equals(XMLString.TEXT_LIST_ITEM)) { + handleListItem((Element)child,ldp,oc); + } + if (nodeName.equals(XMLString.TEXT_LIST_HEADER)) { + handleListItem((Element)child,ldp,oc); + } + } + } + } + } + + private void handleListItem(Element node, LaTeXDocumentPortion ldp, Context oc) { + // Are we ignoring this list? + if (oc.isIgnoreLists()) { + traverseBlockText(node,ldp,oc); + return; + } + + // Apply the style + BeforeAfter ba = new BeforeAfter(); + palette.getListSc().applyListItemStyle( + oc.getListStyleName(), oc.getListLevel(), + node.getNodeName().equals(XMLString.TEXT_LIST_HEADER), + "true".equals(node.getAttribute(XMLString.TEXT_RESTART_NUMBERING)), + Misc.getPosInteger(node.getAttribute(XMLString.TEXT_START_VALUE),1)-1, + ba); + + // export the list item + if (ba.getBefore().length()>0) { + ldp.append(ba.getBefore()); + if (config.formatting()>=LaTeXConfig.CONVERT_MOST) { ldp.nl(); } + } + traverseBlockText(node,ldp,oc); + if (ba.getAfter().length()>0) { ldp.append(ba.getAfter()).nl(); } + } + + /* + * Helper: Check to see, if this list contains headings + * (in that case we will ignore the list!) + */ + private boolean listContainsHeadings (Node node) { + if (node.hasChildNodes()) { + NodeList nList = node.getChildNodes(); + int len = nList.getLength(); + for (int i = 0; i < len; i++) { + Node child = nList.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + if (nodeName.equals(XMLString.TEXT_LIST_ITEM)) { + if (listItemContainsHeadings(child)) return true; + } + if (nodeName.equals(XMLString.TEXT_LIST_HEADER)) { + if (listItemContainsHeadings(child)) return true; + } + } + } + } + return false; + } + + private boolean listItemContainsHeadings(Node node) { + if (node.hasChildNodes()) { + NodeList nList = node.getChildNodes(); + int len = nList.getLength(); + for (int i = 0; i < len; i++) { + Node child = nList.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + if(nodeName.equals(XMLString.TEXT_H)) { + return true; + } + if (nodeName.equals(XMLString.TEXT_LIST)) { + if (listContainsHeadings(child)) return true; + } + if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) { + if (listContainsHeadings(child)) return true; + } + if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) { + if (listContainsHeadings(child)) return true; + } + } + } + } + return false; + } + + + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/CaptionConverter.java b/source/java/writer2latex/latex/CaptionConverter.java new file mode 100644 index 0000000..75f48cf --- /dev/null +++ b/source/java/writer2latex/latex/CaptionConverter.java @@ -0,0 +1,163 @@ +/************************************************************************ + * + * CaptionConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.latex.util.Context; +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; + +/** + * <p>This class converts captions (for figures and tables) to LaTeX.</p> + * <p>Packages: + * <ul><li>caption.sty is used implement non-floating captions</li></ul> + * <p>Options: + * <ul><li>use_caption is a boolean option to determine whether or not + * to use caption.sty. If this option is set to false, a simple definition of + * \captionof (borrowed from capt-of.sty) is inserted in the preamble</li></ul> + * <p>TODO: Implement formatting of captions using the features of caption.sty + * (only if formatting>=CONVERT_BASIC) + */ +public class CaptionConverter extends ConverterHelper { + + private boolean bNeedCaptionOf = false; + + private Element seqField = null; // the sequence field within the current caption + + public CaptionConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bNeedCaptionOf) { + if (config.useCaption()) { + pack.append("\\usepackage{caption}").nl(); + } + else { // use definition borrowed from capt-of.sty + decl.append("% Non-floating captions").nl() + .append("\\makeatletter").nl() + .append("\\newcommand\\captionof[1]{\\def\\@captype{#1}\\caption}").nl() + .append("\\makeatother").nl(); + } + } + } + + /** + * <p> Process content of a text:p tag as a caption body (inluding label)</p> + * @param node The text:p element node containing the caption + * @param ldp The <code>LaTeXDocumentPortion</code> to add LaTeX code to + * @param oc The current context + * @param bIsCaptionOf true if this is caption uses captionof + */ + public void handleCaptionBody(Element node,LaTeXDocumentPortion ldp, Context oc, boolean bIsCaptionOf) { + bNeedCaptionOf|=bIsCaptionOf; + + // Get rid of the caption label before converting + removeCaptionLabel(node,0); + Element label = seqField; + seqField = null; + + // Get the stylename of the paragraph and push the font used + String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getParStyle(sStyleName))); + + if (palette.getHeadingCv().containsElements(node)) { + ldp.append("["); + palette.getInlineCv().traversePlainInlineText(node,ldp,oc); + ldp.append("]"); + } + // Update context before traversing text + Context ic = (Context) oc.clone(); + ldp.append("{"); + palette.getInlineCv().traverseInlineText(node,ldp,ic); + ldp.append("}").nl(); + + // Insert label + palette.getFieldCv().handleSequence(label,ldp,oc); + + // Flush any index marks + palette.getIndexCv().flushIndexMarks(ldp,oc); + + // pop the font name + palette.getI18n().popSpecialTable(); + + } + + // In OpenDocument a caption is an ordinary paragraph with a text:seqence + // element. For example + // Table <text:sequence>3</text:sequence>: Caption text + // The first part is the caption label which is autogenerated by LaTeX. + // Before converting, we remove this in 3 steps: + // nStep = 0: Remove all text before the text:sequence + // nStep = 1: Remove all text up to the first alphanumeric character + // after the text:sequence + // nStep = 2: Finished! + private int removeCaptionLabel(Element node, int nStep) { + if (nStep==2) { return 2; } + + Node removeMe = null; + + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + if (nStep==0 && child.getNodeName().equals(XMLString.TEXT_SEQUENCE)) { + removeMe = child; + seqField = (Element) child; // remember me... + nStep = 1; + } + else if (nStep<2 && !OfficeReader.isDrawElement(child)) { + // draw elements (frames) should not be touched.. + nStep = removeCaptionLabel((Element)child,nStep); + } + } + else if (child.getNodeType()==Node.TEXT_NODE) { + if (nStep==0) { + child.setNodeValue(""); + } + else if (nStep==1) { + String s = child.getNodeValue(); + int n = s.length(); + for (int j=0; j<n; j++) { + if (Character.isLetterOrDigit(s.charAt(j))) { + child.setNodeValue(s.substring(j)); + nStep = 2; + break; + } + } + } + } + child = child.getNextSibling(); + } + + if (removeMe!=null) { node.removeChild(removeMe); } + return nStep; + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/CharStyleConverter.java b/source/java/writer2latex/latex/CharStyleConverter.java new file mode 100644 index 0000000..f3f1305 --- /dev/null +++ b/source/java/writer2latex/latex/CharStyleConverter.java @@ -0,0 +1,507 @@ +/************************************************************************ + * + * CharStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-12-03) + * + */ + +package writer2latex.latex; + +import java.util.Hashtable; + +import writer2latex.util.*; +import writer2latex.office.*; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; +import writer2latex.latex.util.StyleMap; + +/** This class creates LaTeX code from OOo character formatting + Character formatting in OOo includes font, font effects/decorations and color. + In addition it includes color and language/country information, this is however handled + by the classes <code>writer2latex.latex.ColorConverter</code> and + <code>writer2latex.latex.style.I18n</code> + */ +public class CharStyleConverter extends StyleConverter { + + // Cache of converted font declarations + private Hashtable fontDecls = new Hashtable(); + + // Which formatting should we export? + private boolean bIgnoreHardFontsize; + private boolean bIgnoreFontsize; + private boolean bIgnoreFont; + private boolean bIgnoreAll; + private boolean bUseUlem; + // Do we need actually use ulem.sty or \textsubscript? + private boolean bNeedUlem = false; + private boolean bNeedSubscript = false; + + /** <p>Constructs a new <code>CharStyleConverter</code>.</p> + */ + public CharStyleConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + + bUseUlem = config.useUlem(); + + // No character formatting at all: + bIgnoreAll = config.formatting()==LaTeXConfig.IGNORE_ALL; + // No font family or size: + bIgnoreFont = config.formatting()<=LaTeXConfig.IGNORE_MOST; + // No fontsize: + bIgnoreFontsize = config.formatting()<=LaTeXConfig.CONVERT_BASIC; + // No hard fontsize + bIgnoreHardFontsize = config.formatting()<=LaTeXConfig.CONVERT_MOST; + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bNeedUlem) { + pack.append("\\usepackage[normalem]{ulem}").nl(); + } + if (bNeedSubscript && !config.getTextAttributeStyleMap().contains("subscript")) { + decl.append("\\newcommand\\textsubscript[1]{\\ensuremath{{}_{\\text{#1}}}}").nl(); + } + if (!styleNames.isEmpty()) { + decl.append("% Text styles").nl().append(declarations); + } + } + + /** <p>Use a text style in LaTeX.</p> + * @param sName the name of the text style + * @param ba a <code>BeforeAfter</code> to put code into + */ + public void applyTextStyle(String sName, BeforeAfter ba, Context context) { + if (sName==null) { return; } + String sDisplayName = ofr.getTextStyles().getDisplayName(sName); + + if (bIgnoreAll) { + // Even if all is ignored, we still apply style maps from config.. + StyleMap sm = config.getTextStyleMap(); + if (sm.contains(sDisplayName)) { + ba.add(sm.getBefore(sDisplayName),sm.getAfter(sDisplayName)); + } + return; + } + + // Style already converted? + if (styleMap.contains(sName)) { + ba.add(styleMap.getBefore(sName),styleMap.getAfter(sName)); + context.updateFormattingFromStyle(ofr.getTextStyle(sName)); + // it's verbatim if specified as such in the configuration + StyleMap sm = config.getTextStyleMap(); + boolean bIsVerbatim = sm.contains(sDisplayName) && sm.getVerbatim(sDisplayName); + context.setVerbatim(bIsVerbatim); + context.setNoLineBreaks(bIsVerbatim); + return; + } + + // The style may already be declared in the configuration: + StyleMap sm = config.getTextStyleMap(); + if (sm.contains(sDisplayName)) { + styleMap.put(sName,sm.getBefore(sDisplayName),sm.getAfter(sDisplayName)); + applyTextStyle(sName,ba,context); + return; + } + + // Get the style, if it exists: + StyleWithProperties style = ofr.getTextStyle(sName); + if (style==null) { + styleMap.put(sName,"",""); + applyTextStyle(sName,ba,context); + return; + } + + // Convert automatic style + if (style.isAutomatic()) { + palette.getI18n().applyLanguage(style,false,true,ba); + applyFont(style,false,true,ba,context); + applyFontEffects(style,true,ba); + context.updateFormattingFromStyle(ofr.getTextStyle(sName)); + return; + } + + // Convert soft style: + // This must be converted relative to a blank context! + BeforeAfter baText = new BeforeAfter(); + palette.getI18n().applyLanguage(style,false,true,baText); + applyFont(style,false,true,baText,new Context()); + applyFontEffects(style,true,baText); + // declare the text style (\newcommand) + String sTeXName = styleNames.getExportName(ofr.getTextStyles().getDisplayName(sName)); + styleMap.put(sName,"\\textstyle"+sTeXName+"{","}"); + declarations.append("\\newcommand\\textstyle") + .append(sTeXName).append("[1]{") + .append(baText.getBefore()).append("#1").append(baText.getAfter()) + .append("}").nl(); + applyTextStyle(sName,ba,context); + } + + public String getFontName(StyleWithProperties style) { + if (style!=null) { + String sName = style.getProperty(XMLString.STYLE_FONT_NAME); + if (sName!=null) { + FontDeclaration fd = ofr.getFontDeclaration(sName); + if (fd!=null) { + return fd.getFontFamily(); + } + } + } + return null; + } + + // Get the font name from a char style + public String getFontName(String sStyleName) { + return getFontName(ofr.getTextStyle(sStyleName)); + } + + /** <p>Apply hard character formatting (no inheritance).</p> + * <p>This is used in sections and {foot|end}notes</p> + * @param style the style to use + * @param ba the <code>BeforeAfter</code> to add LaTeX code to + */ + public void applyHardCharFormatting(StyleWithProperties style, BeforeAfter ba) { + palette.getI18n().applyLanguage(style,true,false,ba); + applyFont(style,true,false,ba,new Context()); + if (!ba.isEmpty()) { ba.add(" ",""); } + } + + /** <p>Apply all font attributes (family, series, shape, size and color).</p> + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + public void applyFont(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) { + // Note: if bDecl is true, nothing will be put in the "after" part of ba. + if (style==null) { return; } + applyNfssSize(style,bDecl,bInherit,ba,context); + applyNfssFamily(style,bDecl,bInherit,ba,context); + applyNfssSeries(style,bDecl,bInherit,ba,context); + applyNfssShape(style,bDecl,bInherit,ba,context); + palette.getColorCv().applyColor(style,bDecl,bInherit,ba,context); + } + + /** <p>Reset to normal font, size and color.</p> + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + public void applyNormalFont(BeforeAfter ba) { + ba.add("\\normalfont\\normalsize",""); + palette.getColorCv().applyNormalColor(ba); + } + + /** <p>Apply default font attributes (family, series, shape, size and color).</p> + * @param style the OOo style to read attributesfrom + * @param ldp the <code>LaTeXDocumentPortion</code> to add LaTeX code to. + */ + public void applyDefaultFont(StyleWithProperties style, LaTeXDocumentPortion ldp) { + if (style==null) { return; } + + String s = convertFontDeclaration(style.getProperty(XMLString.STYLE_FONT_NAME)); + if (s!=null){ + ldp.append("\\renewcommand\\familydefault{\\") + .append(s).append("default}").nl(); + } // TODO: Else read props directly from the style + + s = nfssSeries(style.getProperty(XMLString.FO_FONT_WEIGHT)); + if (s!=null) { + ldp.append("\\renewcommand\\seriesdefault{\\") + .append(s).append("default}").nl(); + } + + s = nfssShape(style.getProperty(XMLString.FO_FONT_VARIANT), + style.getProperty(XMLString.FO_FONT_STYLE)); + if (s!=null) { + ldp.append("\\renewcommand\\shapedefault{\\") + .append(s).append("default}").nl(); + } + + palette.getColorCv().setNormalColor(style.getProperty(XMLString.FO_COLOR),ldp); + } + + /** <p>Apply font effects (position, underline, crossout, change case.</p> + * @param style the OOo style to read attributesfrom + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + public void applyFontEffects(StyleWithProperties style, boolean bInherit, BeforeAfter ba) { + if (style==null) { return; } + applyTextPosition(style, bInherit, ba); + applyUnderline(style, bInherit, ba); + applyCrossout(style, bInherit, ba); + applyChangeCase(style, bInherit, ba); + } + + // Remaining methods are private + + /** <p>Apply font family.</p> + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyNfssFamily(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) { + // Note: if bDecl is true, nothing will be put in the "after" part of ba. + if (style==null || bIgnoreFont) { return; } + String sFontName=style.getProperty(XMLString.STYLE_FONT_NAME,bInherit); + if (sFontName!=null){ + String sFamily = convertFontDeclaration(sFontName); + if (sFamily==null) { return; } + if (sFamily.equals(convertFontDeclaration(context.getFontName()))) { return; } + if (bDecl) { ba.add("\\"+sFamily+"family",""); } + else { ba.add("\\text"+sFamily+"{","}"); } + } // TODO: Else read props directly from the style + } + + /** <p>Apply font series.</p> + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyNfssSeries(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) { + // Note: if bDecl is true, nothing will be put in the "after" part of ba. + if (style!=null && !bIgnoreAll) { + String sSeries = nfssSeries(style.getProperty(XMLString.FO_FONT_WEIGHT,bInherit)); + if (sSeries!=null) { + // Temporary: Support text-attribute style maps for this particular case + // TODO: Reimplement the CharStyleConverter to properly support this... + if (!bDecl && "bf".equals(sSeries) && config.getTextAttributeStyleMap().contains("bold")) { + ba.add(config.getTextAttributeStyleMap().getBefore("bold"), + config.getTextAttributeStyleMap().getAfter("bold")); + } + else { + if (style.isAutomatic()) { // optimize hard formatting + if (sSeries.equals(nfssSeries(context.getFontWeight()))) { return; } + if (context.getFontWeight()==null && sSeries.equals("md")) { return; } + } + if (bDecl) { ba.add("\\"+sSeries+"series",""); } + else { ba.add("\\text"+sSeries+"{","}"); } + } + } + } + } + + /** <p>Apply font shape.</p> + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyNfssShape(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) { + // Note: if bDecl is true, nothing will be put in the "after" part of ba. + if (style!=null && !bIgnoreAll) { + String sVariant = style.getProperty(XMLString.FO_FONT_VARIANT, bInherit); + String sStyle = style.getProperty(XMLString.FO_FONT_STYLE, bInherit); + String sShape = nfssShape(sVariant,sStyle); + if (sShape!=null) { + // Temporary: Support text-attribute style maps for this particular case + // TODO: Reimplement the CharStyleConverter to properly support this... + if (!bDecl && "sc".equals(sShape) && config.getTextAttributeStyleMap().contains("small-caps")) { + ba.add(config.getTextAttributeStyleMap().getBefore("small-caps"), + config.getTextAttributeStyleMap().getAfter("small-caps")); + } + else if (!bDecl && "it".equals(sShape) && config.getTextAttributeStyleMap().contains("italic")) { + ba.add(config.getTextAttributeStyleMap().getBefore("italic"), + config.getTextAttributeStyleMap().getAfter("italic")); + } + else { + if (style.isAutomatic()) { // optimize hard formatting + if (sShape.equals(nfssShape(context.getFontVariant(),context.getFontStyle()))) return; + if (context.getFontVariant()==null && context.getFontStyle()==null && sShape.equals("up")) return; + } + if (bDecl) ba.add("\\"+sShape+"shape",""); + else ba.add("\\text"+sShape+"{","}"); + } + } + } + } + + /** <p>Apply font size.</p> + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyNfssSize(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) { + // Note: if bDecl is true, nothing will be put in the "after" part of ba. + if (style==null|| bIgnoreFontsize || (bIgnoreHardFontsize && style.isAutomatic())) { return; } + if (style.getProperty(XMLString.FO_FONT_SIZE, bInherit)==null) { return; } + String sSize = nfssSize(style.getAbsoluteProperty(XMLString.FO_FONT_SIZE)); + if (sSize==null) { return; } + if (sSize.equals(nfssSize(context.getFontSize()))) { return; } + if (bDecl) { ba.add(sSize,""); } + else { ba.add("{"+sSize+" ","}"); } + } + + // Remaining methods are not context-sensitive + + /** <p>Apply text position.</p> + * @param style the OOo style to read attributesfrom + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyTextPosition(StyleWithProperties style, boolean bInherit, BeforeAfter ba) { + if (style!=null && !bIgnoreAll) { + String s = textPosition(style.getProperty(XMLString.STYLE_TEXT_POSITION, bInherit)); + // Temporary: Support text-attribute style maps for this particular case + // TODO: Reimplement the CharStyleConverter to properly support this... + if (config.getTextAttributeStyleMap().contains("superscript") && "\\textsuperscript".equals(s)) { + ba.add(config.getTextAttributeStyleMap().getBefore("superscript"), + config.getTextAttributeStyleMap().getAfter("superscript")); + } + else if (config.getTextAttributeStyleMap().contains("subscript") && "\\textsubscript".equals(s)) { + ba.add(config.getTextAttributeStyleMap().getBefore("subscript"), + config.getTextAttributeStyleMap().getAfter("subscript")); + } + else if (s!=null) { + ba.add(s+"{","}"); + } + } + } + + /** <p>Apply text underline.</p> + * @param style the OOo style to read attributesfrom + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyUnderline(StyleWithProperties style, boolean bInherit, BeforeAfter ba) { + if (style==null || !bUseUlem) { return; } + if (bIgnoreAll) { return; } + String sTag = ofr.isOpenDocument() ? + XMLString.STYLE_TEXT_UNDERLINE_STYLE : + XMLString.STYLE_TEXT_UNDERLINE; + String s = underline(style.getProperty(sTag, bInherit)); + if (s!=null) { bNeedUlem = true; ba.add(s+"{","}"); } + } + + /** <p>Apply text crossout.</p> + * @param style the OOo style to read attributesfrom + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyCrossout(StyleWithProperties style, boolean bInherit, BeforeAfter ba) { + if (style==null || !bUseUlem) { return; } + if (bIgnoreAll) { return; } + String sTag = ofr.isOpenDocument() ? + XMLString.STYLE_TEXT_LINE_THROUGH_STYLE : + XMLString.STYLE_TEXT_CROSSING_OUT; + String s = crossout(style.getProperty(sTag, bInherit)); + if (s!=null) { bNeedUlem = true; ba.add(s+"{","}"); } + } + + /** <p>Apply change case.</p> + * @param style the OOo style to read attributesfrom + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + private void applyChangeCase(StyleWithProperties style, boolean bInherit, BeforeAfter ba) { + if (style==null) { return; } + if (bIgnoreAll) { return; } + String s = changeCase(style.getProperty(XMLString.FO_TEXT_TRANSFORM)); + if (s!=null) { ba.add(s+"{","}"); } + } + + /** <p>Convert font declarations to LaTeX.</p> + * <p>It returns a generic LaTeX font family (rm, tt, sf).</p> + * <p>It returns null if the font declaration doesn't exist.</p> + * @param sName the name of the font declaration + * @return <code>String</code> with a LaTeX generic fontfamily + */ + private String convertFontDeclaration(String sName) { + FontDeclaration fd = ofr.getFontDeclaration(sName); + if (fd==null) { return null; } + if (!fontDecls.containsKey(sName)) { + String sFontFamily = fd.getFontFamily(); + String sFontPitch = fd.getFontPitch(); + String sFontFamilyGeneric = fd.getFontFamilyGeneric(); + fontDecls.put(sName,nfssFamily(sFontFamily,sFontFamilyGeneric,sFontPitch)); + } + return (String) fontDecls.get(sName); + } + + // The remaining methods are static helpers to convert single style properties + + // Font change. These methods return the declaration form if the paramater + // bDecl is true, and otherwise the command form + + private static final String nfssFamily(String sFontFamily, String sFontFamilyGeneric, + String sFontPitch){ + // Note: Defaults to rm + // TODO: What about decorative, script, system? + if ("fixed".equals(sFontPitch)) return "tt"; + else if ("modern".equals(sFontFamilyGeneric)) return "tt"; + else if ("swiss".equals(sFontFamilyGeneric)) return "sf"; + else return "rm"; + } + + private static final String nfssSeries(String sFontWeight){ + if (sFontWeight==null) return null; + if ("bold".equals(sFontWeight)) return "bf"; + else return "md"; + } + + private static final String nfssShape(String sFontVariant, String sFontStyle){ + if (sFontVariant==null && sFontStyle==null) return null; + if ("small-caps".equals(sFontVariant)) return "sc"; + else if ("italic".equals(sFontStyle)) return "it"; + else if ("oblique".equals(sFontStyle)) return "sl"; + else return "up"; + } + + private static final String nfssSize(String sFontSize){ + if (sFontSize==null) return null; + return "\\fontsize{"+sFontSize+"}{"+Misc.multiply("120%",sFontSize)+"}\\selectfont"; + } + + // other character formatting + + private final String textPosition(String sTextPosition){ + if (sTextPosition==null) return null; + if (sTextPosition.startsWith("super")) return "\\textsuperscript"; + if (sTextPosition.startsWith("sub") || sTextPosition.startsWith("-")) { + bNeedSubscript = true; + return "\\textsubscript"; + } + if (sTextPosition.startsWith("0%")) return null; + return "\\textsuperscript"; + } + + private static final String underline(String sUnderline) { + if (sUnderline==null) { return null; } + if (sUnderline.equals("none")) { return null; } + if (sUnderline.indexOf("wave")>=0) { return "\\uwave"; } + return "\\uline"; + } + + private static final String crossout(String sCrossout) { + if (sCrossout==null) { return null; } + if (sCrossout.equals("X")) { return "\\xout"; } + if (sCrossout.equals("slash")) { return "\\xout"; } + return "\\sout"; + } + + private static final String changeCase(String sTextTransform){ + if ("lowercase".equals(sTextTransform)) return "\\MakeLowercase"; + if ("uppercase".equals(sTextTransform)) return "\\MakeUppercase"; + return null; + } + +} diff --git a/source/java/writer2latex/latex/ColorConverter.java b/source/java/writer2latex/latex/ColorConverter.java new file mode 100644 index 0000000..173fe64 --- /dev/null +++ b/source/java/writer2latex/latex/ColorConverter.java @@ -0,0 +1,202 @@ +/************************************************************************ + * + * ColorConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; +import writer2latex.office.OfficeReader; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; + + +/** This class converts color + */ +public class ColorConverter extends ConverterHelper { + + private static final int RED = 0; + private static final int GREEN = 1; + private static final int BLUE = 2; + + private boolean bUseColor; + + /** <p>Constructs a new <code>CharStyleConverter</code>.</p> + */ + public ColorConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + + // We use color if requested in the configuration, however ignoring + // all formatting overrides this + bUseColor = config.useColor() && config.formatting()>LaTeXConfig.IGNORE_ALL; + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bUseColor) { + pack.append("\\usepackage{color}").nl(); + } + } + + public void setNormalColor(String sColor, LaTeXDocumentPortion ldp) { + if (bUseColor && sColor!=null) { + ldp.append("\\renewcommand\\normalcolor{\\color") + .append(color(sColor)).append("}").nl(); + } + } + + public void applyNormalColor(BeforeAfter ba) { + if (bUseColor) { ba.add("\\normalcolor",""); } + } + + /** <p>Apply foreground color.</p> + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + * @param context the current context + */ + public void applyColor(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba, Context context) { + // Note: if bDecl is true, nothing will be put in the "after" part of ba. + if (bUseColor && style!=null) { + String sColor = style.getProperty(XMLString.FO_COLOR,bInherit); + if (sColor!=null) { + if (!sColor.equals(context.getFontColor())) { + // Convert color if it differs from the current font color + context.setFontColor(sColor); + applyColor(sColor, bDecl, ba, context); + } + } + else { + // No color; maybe automatic color? + String sAutomatic = style.getProperty(XMLString.STYLE_USE_WINDOW_FONT_COLOR,bInherit); + if (sAutomatic==null && bInherit) { + // We may need to inherit this property from the default style + StyleWithProperties defaultStyle = ofr.getDefaultParStyle(); + if (defaultStyle!=null) { + sAutomatic = defaultStyle.getProperty(XMLString.STYLE_USE_WINDOW_FONT_COLOR,bInherit); + } + } + if ("true".equals(sAutomatic)) { + // Automatic color based on background + if (context.getBgColor()!=null) { applyAutomaticColor(ba,bDecl,context); } + } + } + } + } + + /** <p>Apply a specific foreground color.</p> + * @param sColor the rgb color to use + * @param bDecl true if declaration form is required + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + public void applyColor(String sColor, boolean bDecl, BeforeAfter ba, Context context) { + // Note: if bDecl is true, nothing will be put in the "after" part of ba. + if (bUseColor && sColor!=null) { + // If there's a background color, allow all colors + String s = context.getBgColor()!=null ? fullcolor(sColor) : color(sColor); + if (s!=null) { + if (bDecl) { ba.add("\\color"+s,""); } + else { ba.add("\\textcolor"+s+"{","}"); } + } + } + } + + public void applyBgColor(String sCommand, String sColor, BeforeAfter ba, Context context) { + // Note: Will only fill "before" part of ba + if (sColor!=null && !"transparent".equals(sColor)) { + String s = fullcolor(sColor); + if (bUseColor && s!=null) { + context.setBgColor(sColor); + ba.add(sCommand+s,""); + } + } + } + + public void applyAutomaticColor(BeforeAfter ba, boolean bDecl, Context context) { + String s = automaticcolor(context.getBgColor()); + if (s!=null) { + if (bDecl) { ba.add("\\color"+s,""); } + else { ba.add("\\textcolor"+s+"{","}"); } + } + } + + private static final String automaticcolor(String sBgColor) { + if (sBgColor!=null && sBgColor.length()==7) { + float[] rgb = getRgb(sBgColor); + if (rgb[RED]+rgb[GREEN]+rgb[BLUE]<0.6) { + // Dark background + return "{white}"; + } + } + return "{black}"; + } + + private static final String color(String sColor){ + if ("#000000".equalsIgnoreCase(sColor)) { return "{black}"; } + else if ("#ff0000".equalsIgnoreCase(sColor)) { return "{red}"; } + else if ("#00ff00".equalsIgnoreCase(sColor)) { return "{green}"; } + else if ("#0000ff".equalsIgnoreCase(sColor)) { return "{blue}"; } + else if ("#ffff00".equalsIgnoreCase(sColor)) { return "{yellow}"; } + else if ("#ff00ff".equalsIgnoreCase(sColor)) { return "{magenta}"; } + else if ("#00ffff".equalsIgnoreCase(sColor)) { return "{cyan}"; } + //no white, since we don't have background colors: + //else if ("#ffffff".equalsIgnoreCase(sColor)) { return "{white}"; } + else { + if (sColor==null || sColor.length()!=7) return null; + float[] rgb = getRgb(sColor); + // avoid very bright colors (since we don't have background colors): + if (rgb[RED]+rgb[GREEN]+rgb[BLUE]>2.7) { return "{black}"; } + else { return "[rgb]{"+rgb[RED]+","+rgb[GREEN]+","+rgb[BLUE]+"}"; } + } + } + + private static final String fullcolor(String sColor){ + if ("#000000".equalsIgnoreCase(sColor)) { return "{black}"; } + else if ("#ff0000".equalsIgnoreCase(sColor)) { return "{red}"; } + else if ("#00ff00".equalsIgnoreCase(sColor)) { return "{green}"; } + else if ("#0000ff".equalsIgnoreCase(sColor)) { return "{blue}"; } + else if ("#ffff00".equalsIgnoreCase(sColor)) { return "{yellow}"; } + else if ("#ff00ff".equalsIgnoreCase(sColor)) { return "{magenta}"; } + else if ("#00ffff".equalsIgnoreCase(sColor)) { return "{cyan}"; } + else if ("#ffffff".equalsIgnoreCase(sColor)) { return "{white}"; } + else { + // This could mean transparent: + if (sColor==null || sColor.length()!=7) return null; + float[] rgb = getRgb(sColor); + return "[rgb]{"+rgb[RED]+","+rgb[GREEN]+","+rgb[BLUE]+"}"; + } + } + + private static final float[] getRgb(String sColor) { + float[] rgb = new float[3]; + rgb[RED] = (float)Misc.getIntegerFromHex(sColor.substring(1,3),0)/255; + rgb[GREEN] = (float)Misc.getIntegerFromHex(sColor.substring(3,5),0)/255; + rgb[BLUE] = (float)Misc.getIntegerFromHex(sColor.substring(5,7),0)/255; + return rgb; + } + + +} diff --git a/source/java/writer2latex/latex/ContentHandlingOption.java b/source/java/writer2latex/latex/ContentHandlingOption.java new file mode 100644 index 0000000..166afb0 --- /dev/null +++ b/source/java/writer2latex/latex/ContentHandlingOption.java @@ -0,0 +1,43 @@ +/************************************************************************ + * + * ContentHandlingOption.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.latex; + +import writer2latex.base.IntegerOption; + +public class ContentHandlingOption extends IntegerOption { + public ContentHandlingOption(String sName, String sDefaultValue) { + super(sName,sDefaultValue); + } + + public void setString(String sValue) { + super.setString(sValue); + if ("accept".equals(sValue)) nValue = LaTeXConfig.ACCEPT; + else if ("ignore".equals(sValue)) nValue = LaTeXConfig.IGNORE; + else if ("warning".equals(sValue)) nValue = LaTeXConfig.WARNING; + else if ("error".equals(sValue)) nValue = LaTeXConfig.ERROR; + } +} diff --git a/source/java/writer2latex/latex/ConverterHelper.java b/source/java/writer2latex/latex/ConverterHelper.java new file mode 100644 index 0000000..56eeeb8 --- /dev/null +++ b/source/java/writer2latex/latex/ConverterHelper.java @@ -0,0 +1,49 @@ +/************************************************************************ + * + * ConverterHelper.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.latex; + +import writer2latex.office.OfficeReader; + +/** + * <p>This is an abstract superclass for converter helpers.</p> + */ +public abstract class ConverterHelper { + + protected OfficeReader ofr; + protected LaTeXConfig config; + protected ConverterPalette palette; + + protected ConverterHelper(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + this.ofr = ofr; + this.config = config; + this.palette = palette; + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/ConverterPalette.java b/source/java/writer2latex/latex/ConverterPalette.java new file mode 100644 index 0000000..82896fa --- /dev/null +++ b/source/java/writer2latex/latex/ConverterPalette.java @@ -0,0 +1,303 @@ +/************************************************************************ + * + * ConverterPalette.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-17) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; + +import java.io.IOException; +import java.util.LinkedList; + +import writer2latex.api.Config; +import writer2latex.api.ConverterFactory; +//import writer2latex.api.ConverterResult; +import writer2latex.base.ConverterBase; +import writer2latex.latex.i18n.ClassicI18n; +import writer2latex.latex.i18n.I18n; +import writer2latex.latex.i18n.XeTeXI18n; +import writer2latex.latex.util.Context; +import writer2latex.latex.util.Info; +import writer2latex.util.CSVList; +import writer2latex.util.ExportNameCollection; +import writer2latex.util.Misc; +import writer2latex.office.MIMETypes; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; + +/** + * <p>This class converts a Writer XML file to a LaTeX file<.</p> + */ +public final class ConverterPalette extends ConverterBase { + + // Configuration + private LaTeXConfig config; + + public Config getConfig() { return config; } + + // The main outfile + private LaTeXDocument texDoc; + + // Various data used in conversion + private Context mainContext; // main context + private CSVList globalOptions; // global options + + // The helpers (the "colors" of the palette) + private I18n i18n; + private ColorConverter colorCv; + private CharStyleConverter charSc; + private ListStyleConverter listSc; + private PageStyleConverter pageSc; + private BlockConverter blockCv; + private ParConverter parCv; + private HeadingConverter headingCv; + private IndexConverter indexCv; + private BibConverter bibCv; + private SectionConverter sectionCv; + private TableConverter tableCv; + private NoteConverter noteCv; + private CaptionConverter captionCv; + private InlineConverter inlineCv; + private FieldConverter fieldCv; + private DrawConverter drawCv; + private MathmlConverter mathmlCv; + private Info info; + + // Constructor + public ConverterPalette() { + super(); + config = new LaTeXConfig(); + } + + // Accessor methods for data + + public String getOutFileName() { return sTargetFileName; } + + public Context getMainContext() { return mainContext; } + + public void addGlobalOption(String sOption) { + globalOptions.addValue(sOption); + } + + // Accessor methods for helpers + public I18n getI18n() { return i18n; } + public ColorConverter getColorCv() { return colorCv; } + public CharStyleConverter getCharSc() { return charSc; } + public ListStyleConverter getListSc() { return listSc; } + public PageStyleConverter getPageSc() { return pageSc; } + public BlockConverter getBlockCv() { return blockCv; } + public ParConverter getParCv() { return parCv; } + public HeadingConverter getHeadingCv() { return headingCv; } + public IndexConverter getIndexCv() { return indexCv; } + public BibConverter getBibCv() { return bibCv; } + public SectionConverter getSectionCv() { return sectionCv; } + public TableConverter getTableCv() { return tableCv; } + public NoteConverter getNoteCv() { return noteCv; } + public CaptionConverter getCaptionCv() { return captionCv; } + public InlineConverter getInlineCv() { return inlineCv; } + public FieldConverter getFieldCv() { return fieldCv; } + public DrawConverter getDrawCv() { return drawCv; } + public MathmlConverter getMathmlCv() { return mathmlCv; } + public Info getInfo() { return info; } + + + // fill out inner converter method + public void convertInner() throws IOException { + sTargetFileName = Misc.trimDocumentName(sTargetFileName,".tex"); + imageLoader.setOutFileName(new ExportNameCollection(true).getExportName(sTargetFileName)); + + imageLoader.setUseSubdir(config.saveImagesInSubdir()); + + // Set graphics formats depending on backend + if (config.getBackend()==LaTeXConfig.PDFTEX || config.getBackend()==LaTeXConfig.XETEX) { + imageLoader.setDefaultFormat(MIMETypes.PNG); + imageLoader.setDefaultVectorFormat(MIMETypes.PDF); + imageLoader.addAcceptedFormat(MIMETypes.JPEG); + } + else if (config.getBackend()==LaTeXConfig.DVIPS) { + imageLoader.setDefaultFormat(MIMETypes.EPS); + } + // Other values: keep original format + + // Inject user sequence names for tables and figures into OfficeReader + if (config.getTableSequenceName().length()>0) { + ofr.addTableSequenceName(config.getTableSequenceName()); + } + if (config.getFigureSequenceName().length()>0) { + ofr.addFigureSequenceName(config.getFigureSequenceName()); + } + + // Create helpers + if (config.getBackend()!=LaTeXConfig.XETEX) { + i18n = new ClassicI18n(ofr,config,this); + } + else { + i18n = new XeTeXI18n(ofr,config,this); + } + colorCv = new ColorConverter(ofr,config,this); + charSc = new CharStyleConverter(ofr,config,this); + listSc = new ListStyleConverter(ofr,config,this); + pageSc = new PageStyleConverter(ofr,config,this); + blockCv = new BlockConverter(ofr,config,this); + parCv = new ParConverter(ofr,config,this); + headingCv = new HeadingConverter(ofr,config,this); + indexCv = new IndexConverter(ofr,config,this); + bibCv = new BibConverter(ofr,config,this); + sectionCv = new SectionConverter(ofr,config,this); + tableCv = new TableConverter(ofr,config,this); + noteCv = new NoteConverter(ofr,config,this); + captionCv = new CaptionConverter(ofr,config,this); + inlineCv = new InlineConverter(ofr,config,this); + fieldCv = new FieldConverter(ofr,config,this); + drawCv = new DrawConverter(ofr,config,this); + mathmlCv = new MathmlConverter(ofr,config,this); + info = new Info(ofr,config,this); + + // Create master document and add this + this.texDoc = new LaTeXDocument(sTargetFileName,config.getWrapLinesAfter()); + if (config.getBackend()!=LaTeXConfig.XETEX) { + texDoc.setEncoding(ClassicI18n.writeJavaEncoding(config.getInputencoding())); + } + else { + texDoc.setEncoding("UTF-8"); + + } + convertData.addDocument(texDoc); + + // Create other data + globalOptions = new CSVList(','); + + // Setup context. + // The default language is specified in the default paragraph style: + mainContext = new Context(); + mainContext.resetFormattingFromStyle(ofr.getDefaultParStyle()); + mainContext.setInMulticols(pageSc.isTwocolumn()); + + // Create main LaTeXDocumentPortions + LaTeXDocumentPortion packages = new LaTeXDocumentPortion(false); + LaTeXDocumentPortion declarations = new LaTeXDocumentPortion(false); + LaTeXDocumentPortion body = new LaTeXDocumentPortion(true); + + // Traverse the content + Element content = ofr.getContent(); + blockCv.traverseBlockText(content,body,mainContext); + noteCv.insertEndnotes(body); + + // Add declarations from our helpers + i18n.appendDeclarations(packages,declarations); + colorCv.appendDeclarations(packages,declarations); + charSc.appendDeclarations(packages,declarations); + headingCv.appendDeclarations(packages,declarations); + parCv.appendDeclarations(packages,declarations); + listSc.appendDeclarations(packages,declarations); + pageSc.appendDeclarations(packages,declarations); + blockCv.appendDeclarations(packages,declarations); + indexCv.appendDeclarations(packages,declarations); + bibCv.appendDeclarations(packages,declarations); + sectionCv.appendDeclarations(packages,declarations); + tableCv.appendDeclarations(packages,declarations); + noteCv.appendDeclarations(packages,declarations); + captionCv.appendDeclarations(packages,declarations); + inlineCv.appendDeclarations(packages,declarations); + fieldCv.appendDeclarations(packages,declarations); + drawCv.appendDeclarations(packages,declarations); + mathmlCv.appendDeclarations(packages,declarations); + + // Add custom preamble + LinkedList customPreamble = config.getCustomPreamble(); + int nCPLen = customPreamble.size(); + for (int i=0; i<nCPLen; i++) { + declarations.append( (String) customPreamble.get(i) ).nl(); + } + + // Set \title, \author and \date (for \maketitle) + createMeta("title",metaData.getTitle(),declarations); + if (config.metadata()) { + createMeta("author",metaData.getCreator(),declarations); + // According to the spec, the date has the format YYYY-MM-DDThh:mm:ss + String sDate = metaData.getDate(); + if (sDate.length()==19 && sDate.charAt(10)=='T') { + sDate = sDate.substring(0,10); + } + createMeta("date",sDate,declarations); + } + + // Create options for documentclass + if (config.formatting()>=LaTeXConfig.CONVERT_MOST) { + StyleWithProperties dpStyle = ofr.getDefaultParStyle(); + if (dpStyle!=null) { + String s = dpStyle.getProperty(XMLString.FO_FONT_SIZE); + if ("10pt".equals(s)) { globalOptions.addValue("10pt"); } + if ("11pt".equals(s)) { globalOptions.addValue("11pt"); } + if ("12pt".equals(s)) { globalOptions.addValue("12pt"); } + } + } + + // Temp solution. TODO: Fix when new CSVList is implemented + if (config.getGlobalOptions().length()>0) { + globalOptions.addValue(config.getGlobalOptions()); + } + + // Assemble the document + LaTeXDocumentPortion result = texDoc.getContents(); + + if (!config.noPreamble()) { + // Create document class declaration + result.append("% This file was converted to LaTeX by Writer2LaTeX ver. "+ConverterFactory.getVersion()).nl() + .append("% see http://www.hj-gym.dk/~hj/writer2latex for more info").nl(); + result.append("\\documentclass"); + if (!globalOptions.isEmpty()) { + result.append("[").append(globalOptions.toString()).append("]"); + } + result.append("{").append(config.getDocumentclass()).append("}").nl(); + + result.append(packages) + .append(declarations) + .append("\\begin{document}").nl(); + } + + result.append(body); + + if (!config.noPreamble()) { + result.append("\\end{document}").nl(); + } + else { + result.append("\\endinput").nl(); + } + + // Add BibTeX document if there's any bibliographic data + if (bibCv.getBibTeXDocument()!=null) { + convertData.addDocument(bibCv.getBibTeXDocument()); + } + } + + private void createMeta(String sName, String sValue,LaTeXDocumentPortion ldp) { + if (sValue==null) { return; } + // Meta data is assumed to be in the default language: + ldp.append("\\"+sName+"{"+i18n.convert(sValue,false,mainContext.getLang())+"}").nl(); + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/DrawConverter.java b/source/java/writer2latex/latex/DrawConverter.java new file mode 100644 index 0000000..77d126f --- /dev/null +++ b/source/java/writer2latex/latex/DrawConverter.java @@ -0,0 +1,496 @@ +/************************************************************************ + * + * DrawConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-17) + * + */ + +package writer2latex.latex; + +import java.util.LinkedList; +import java.util.Stack; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +//import org.w3c.dom.Node; + +import writer2latex.xmerge.EmbeddedObject; +import writer2latex.xmerge.EmbeddedXMLObject; + +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; +//import writer2latex.office.ImageLoader; +import writer2latex.office.MIMETypes; +import writer2latex.office.OfficeReader; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; +import writer2latex.util.Misc; +import writer2latex.xmerge.BinaryGraphicsDocument; + +/** + * <p>This class handles draw elements.</p> + */ +public class DrawConverter extends ConverterHelper { + + private boolean bNeedGraphicx = false; + private boolean bNeedOOoLaTeXPreamble = false; + + // Keep track of floating frames (images, textboxes...) + private Stack floatingFramesStack = new Stack(); + + private Element getFrame(Element onode) { + if (ofr.isOpenDocument()) return (Element) onode.getParentNode(); + else return onode; + } + + public DrawConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + floatingFramesStack.push(new LinkedList()); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bNeedGraphicx) { + pack.append("\\usepackage"); + if (config.getBackend()==LaTeXConfig.PDFTEX) pack.append("[pdftex]"); + //else if (config.getBackend()==LaTeXConfig.XETEX) pack.append("[xetex]"); + else if (config.getBackend()==LaTeXConfig.DVIPS) pack.append("[dvips]"); + pack.append("{graphicx}").nl(); + } + if (bNeedOOoLaTeXPreamble) { + // The preamble may be stored in the description + String sDescription = palette.getMetaData().getDescription(); + int nStart = sDescription.indexOf("%%% OOoLatex Preamble %%%%%%%%%%%%%%"); + int nEnd = sDescription.indexOf("%%% End OOoLatex Preamble %%%%%%%%%%%%"); + if (nStart>-1 && nEnd>nStart) { + decl.append("% OOoLaTeX preamble").nl() + .append(sDescription.substring(nStart+37,nEnd)); + } + // TODO: Otherwise try the user settings... + } + } + + public void handleCaption(Element node, LaTeXDocumentPortion ldp, Context oc) { + // Floating frames should be positioned *above* the label, hence + // we use a separate ldp for the paragraphs and add this later + LaTeXDocumentPortion capLdp = new LaTeXDocumentPortion(true); + + // Convert the caption + if (oc.isInFigureFloat()) { // float + capLdp.append("\\caption"); + palette.getCaptionCv().handleCaptionBody(node,capLdp,oc,false); + } + else { // nonfloat + capLdp.append("\\captionof{figure}"); + palette.getCaptionCv().handleCaptionBody(node,capLdp,oc,true); + } + + flushFloatingFrames(ldp,oc); + ldp.append(capLdp); + } + + public void handleDrawElement(Element node, LaTeXDocumentPortion ldp, Context oc) { + // node must be an elment in the draw namespace + String sName = node.getTagName(); + if (sName.equals(XMLString.DRAW_OBJECT)) { + handleDrawObject(node,ldp,oc); + } + else if (sName.equals(XMLString.DRAW_OBJECT_OLE)) { + handleDrawObject(node,ldp,oc); + } + else if ((!oc.isInHeaderFooter()) && sName.equals(XMLString.DRAW_IMAGE)) { + handleDrawImage(node,ldp,oc); + } + else if ((!oc.isInHeaderFooter()) && sName.equals(XMLString.DRAW_TEXT_BOX)) { + handleDrawTextBox(node,ldp,oc); + } + else if (sName.equals(XMLString.DRAW_A)) { + // we handle this like text:a + palette.getFieldCv().handleAnchor(node,ldp,oc); + } + else if (sName.equals(XMLString.DRAW_FRAME)) { + // OpenDocument: Get the actual draw element in the frame + handleDrawElement(Misc.getFirstChildElement(node),ldp,oc); + } + else { + // Other drawing objects (eg. shapes) are currently not supported + ldp.append("[Warning: Draw object ignored]"); + } + } + + //----------------------------------------------------------------- + // handle draw:object elements (OOo objects such as Chart, Math,...) + + private void handleDrawObject(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sHref = Misc.getAttribute(node,XMLString.XLINK_HREF); + + if (sHref!=null) { // Embedded object in package or linked object + if (ofr.isInPackage(sHref)) { // Embedded object in package + if (sHref.startsWith("#")) { sHref=sHref.substring(1); } + if (sHref.startsWith("./")) { sHref=sHref.substring(2); } + EmbeddedObject object = palette.getEmbeddedObject(sHref); + if (object!=null) { + if (MIMETypes.MATH.equals(object.getType()) || MIMETypes.ODF.equals(object.getType())) { // Formula! + try { + Document settings = ((EmbeddedXMLObject) object).getSettingsDOM(); + Document formuladoc = ((EmbeddedXMLObject) object).getContentDOM(); + Element formula = Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH); + ldp.append(" $") + .append(palette.getMathmlCv().convert(settings,formula)) + .append("$"); + if (Character.isLetterOrDigit(OfficeReader.getNextChar(node))) { ldp.append(" "); } + } + catch (org.xml.sax.SAXException e) { + e.printStackTrace(); + } + catch (java.io.IOException e) { + e.printStackTrace(); + } + } + else { // unsupported object + boolean bIgnore = true; + if (ofr.isOpenDocument()) { // look for replacement image + Element replacementImage = Misc.getChildByTagName(getFrame(node),XMLString.DRAW_IMAGE); + if (replacementImage!=null) { + handleDrawImage(replacementImage,ldp,oc); + bIgnore = false; + } + } + if (bIgnore) { + ldp.append("[Warning: object ignored]"); + } + } + + } + } + } + else { // flat xml, object is contained in node + Element formula = Misc.getChildByTagName(node,XMLString.MATH_MATH); + if (formula!=null) { + ldp.append(" $") + .append(palette.getMathmlCv().convert(null,formula)) + .append("$"); + if (Character.isLetterOrDigit(OfficeReader.getNextChar(node))) { ldp.append(" "); } + } + else { // unsupported object + boolean bIgnore = true; + if (ofr.isOpenDocument()) { // look for replacement image + Element replacementImage = Misc.getChildByTagName(getFrame(node),XMLString.DRAW_IMAGE); + if (replacementImage!=null) { + handleDrawImage(replacementImage,ldp,oc); + bIgnore = false; + } + } + if (bIgnore) { + ldp.append("[Warning: object ignored]"); + } + } + + } + + } + + //-------------------------------------------------------------------------- + // Create float environment + private void applyFigureFloat(BeforeAfter ba, Context oc) { + // todo: check context... + if (config.floatFigures() && !oc.isInFrame() && !oc.isInTable()) { + if (oc.isInMulticols()) { + ba.add("\\begin{figure*}","\\end{figure*}\n"); + } + else { + ba.add("\\begin{figure}","\\end{figure}\n"); + } + if (config.getFloatOptions().length()>0) { + ba.add("["+config.getFloatOptions()+"]",""); + } + ba.add("\n",""); + oc.setInFigureFloat(true); + } + if (!oc.isInFrame() && config.alignFrames()) { + // Avoid nesting center environment + ba.add("\\begin{center}\n","\n\\end{center}\n"); + } + + } + + //-------------------------------------------------------------------------- + // Handle draw:image elements + + private void handleDrawImage(Element node, LaTeXDocumentPortion ldp, Context oc) { + // Include graphics if allowed by the configuration + switch (config.imageContent()) { + case LaTeXConfig.IGNORE: + // Ignore graphics silently + return; + case LaTeXConfig.WARNING: + System.err.println("Warning: Images are not allowed"); + return; + case LaTeXConfig.ERROR: + ldp.append("% Error in document: An image was ignored"); + return; + } + + Element frame = getFrame(node); + //String sName = frame.getAttribute(XMLString.DRAW_NAME); + palette.getFieldCv().addTarget(frame,"|graphics",ldp); + String sAnchor = frame.getAttribute(XMLString.TEXT_ANCHOR_TYPE); + + // TODO: Recognize Jex equations (needs further testing of Jex) + /*Element desc = Misc.getChildByTagName(frame,XMLString.SVG_DESC); + if (desc!=null) { + String sDesc = Misc.getPCDATA(desc); + if (sDesc.startsWith("jex149$tex={") && sDesc.endsWith("}")) { + String sTeX = sDesc.substring(12,sDesc.length()-1); + if (sTeX.length()>0) { + // Succesfully extracted Jex equation! + ldp.append("$"+sTeX+"$"); + return; + } + } + }*/ + + // Recognize OOoLaTeX equation + // The LaTeX code is embedded in a custom style attribute: + StyleWithProperties style = ofr.getFrameStyle( + Misc.getAttribute(frame, XMLString.DRAW_STYLE_NAME)); + if (style!=null) { + String sOOoLaTeX = style.getProperty("OOoLatexArgs"); + // The content of the attribute is <point size><paragraph sign><mode><paragraph sign><TeX code> + int n=0; + if (sOOoLaTeX!=null) { + if ((n=sOOoLaTeX.indexOf("\u00A7display\u00A7"))>-1) { + ldp.append("\\[").append(sOOoLaTeX.substring(n+9)).append("\\]"); + bNeedOOoLaTeXPreamble = true; + return; + } + else if ((n=sOOoLaTeX.indexOf("\u00A7inline\u00A7"))>-1) { + ldp.append("$").append(sOOoLaTeX.substring(n+8)).append("$"); + bNeedOOoLaTeXPreamble = true; + return; + } + else if ((n=sOOoLaTeX.indexOf("\u00A7text\u00A7"))>-1) { + ldp.append(sOOoLaTeX.substring(n+6)); + bNeedOOoLaTeXPreamble = true; + return; + } + } + } + + //if (oc.isInFrame() || "as-char".equals(sAnchor)) { + if ("as-char".equals(sAnchor)) { + handleDrawImageAsChar(node,ldp,oc); + } + else { + ((LinkedList) floatingFramesStack.peek()).add(node); + } + } + + private void handleDrawImageAsChar(Element node, LaTeXDocumentPortion ldp, Context oc) { + ldp.append(" "); + includeGraphics(node,ldp,oc); + ldp.append(" "); + } + + private void handleDrawImageFloat(Element node, LaTeXDocumentPortion ldp, Context oc) { + Context ic = (Context) oc.clone(); + BeforeAfter ba = new BeforeAfter(); + + applyFigureFloat(ba,ic); + + ldp.append(ba.getBefore()); + includeGraphics(node,ldp,ic); + ldp.append(ba.getAfter()); + } + + private void includeGraphics(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sFileName = null; + boolean bCommentOut = true; + String sHref = node.getAttribute(XMLString.XLINK_HREF); + + if (node.hasAttribute(XMLString.XLINK_HREF) && !ofr.isInPackage(sHref)) { + // Linked image is not yet handled by ImageLoader. This is a temp. + // solution (will go away when ImageLoader is finished) + sFileName = sHref; + // In OpenDocument package format ../ means "leave the package" + if (ofr.isOpenDocument() && ofr.isPackageFormat() && sFileName.startsWith("../")) { + sFileName=sFileName.substring(3); + } + int nExtStart = sHref.lastIndexOf("."); + String sExt = nExtStart>=0 ? sHref.substring(nExtStart).toLowerCase() : ""; + // Accept only relative filenames and supported filetypes: + bCommentOut = sFileName.indexOf(":")>-1 || !( + config.getBackend()==LaTeXConfig.UNSPECIFIED || + (config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.JPEG_EXT.equals(sExt)) || + (config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PNG_EXT.equals(sExt)) || + (config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PDF_EXT.equals(sExt)) || + (config.getBackend()==LaTeXConfig.XETEX && MIMETypes.JPEG_EXT.equals(sExt)) || + (config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PNG_EXT.equals(sExt)) || + (config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PDF_EXT.equals(sExt)) || + (config.getBackend()==LaTeXConfig.DVIPS && MIMETypes.EPS_EXT.equals(sExt))); + } + else { // embedded or base64 encoded image + BinaryGraphicsDocument bgd = palette.getImageLoader().getImage(node); + if (bgd!=null) { + palette.addDocument(bgd); + sFileName = bgd.getFileName(); + String sMIME = bgd.getDocumentMIMEType(); + bCommentOut = !( + config.getBackend()==LaTeXConfig.UNSPECIFIED || + (config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.JPEG.equals(sMIME)) || + (config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PNG.equals(sMIME)) || + (config.getBackend()==LaTeXConfig.PDFTEX && MIMETypes.PDF.equals(sMIME)) || + (config.getBackend()==LaTeXConfig.XETEX && MIMETypes.JPEG.equals(sMIME)) || + (config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PNG.equals(sMIME)) || + (config.getBackend()==LaTeXConfig.XETEX && MIMETypes.PDF.equals(sMIME)) || + (config.getBackend()==LaTeXConfig.DVIPS && MIMETypes.EPS.equals(sMIME))); + } + } + + if (sFileName==null) { + ldp.append("[Warning: Image not found]"); + return; + } + + // Now for the actual inclusion: + bNeedGraphicx = true; + /* TODO: handle cropping and mirror: + style:mirror can be none, vertical (lodret), horizontal (vandret), + horizontal-on-odd, or + horizontal-on-even (horizontal on odd or even pages). + mirror is handled with scalebox, eg: + %\\scalebox{-1}[1]{...} + can check for even/odd page first!! + + fo:clip="rect(t,r,b,l) svarer til trim + value can be auto - no clip! + cropping is handled with clip and trim: + \\includegraphics[clip,trim=l b r t]{...} + note the different order from xsl-fo! + */ + + if (bCommentOut) { + ldp.append(" [Warning: Image ignored] "); + ldp.append("% Unhandled or unsupported graphics:").nl().append("%"); + } + ldp.append("\\includegraphics"); + + CSVList options = new CSVList(','); + if (!config.originalImageSize()) { + Element frame = getFrame(node); + String sWidth = Misc.truncateLength(frame.getAttribute(XMLString.SVG_WIDTH)); + String sHeight = Misc.truncateLength(frame.getAttribute(XMLString.SVG_HEIGHT)); + if (sWidth!=null) { options.addValue("width="+sWidth); } + if (sHeight!=null) { options.addValue("height="+sHeight); } + } + if (config.getImageOptions().length()>0) { + options.addValue(config.getImageOptions()); // TODO: New CSVList... + } + if (!options.isEmpty()) { + ldp.append("[").append(options.toString()).append("]"); + } + + if (config.removeGraphicsExtension()) { + sFileName = Misc.removeExtension(sFileName); + } + ldp.append("{").append(sFileName).append("}"); + if (bCommentOut) { ldp.nl(); } + } + + //-------------------------------------------------------------------------- + // handle draw:text-box element + + private void handleDrawTextBox(Element node, LaTeXDocumentPortion ldp, Context oc) { + Element frame = getFrame(node); + //String sName = frame.getAttribute(XMLString.DRAW_NAME); + palette.getFieldCv().addTarget(frame,"|frame",ldp); + String sAnchor = frame.getAttribute(XMLString.TEXT_ANCHOR_TYPE); + //if (oc.isInFrame() || "as-char".equals(sAnchor)) { + if ("as-char".equals(sAnchor)) { + makeDrawTextBox(node, ldp, oc); + } + else { + ((LinkedList) floatingFramesStack.peek()).add(node); + } + } + + private void handleDrawTextBoxFloat(Element node, LaTeXDocumentPortion ldp, Context oc) { + BeforeAfter ba = new BeforeAfter(); + Context ic = (Context) oc.clone(); + + applyFigureFloat(ba,ic); + + ldp.append(ba.getBefore()); + makeDrawTextBox(node, ldp, ic); + ldp.append(ba.getAfter()); + } + + private void makeDrawTextBox(Element node, LaTeXDocumentPortion ldp, Context oc) { + Context ic = (Context) oc.clone(); + ic.setInFrame(true); + ic.setNoFootnotes(true); + + // Check to see, if this is really a container for a figure caption + boolean bIsCaption = false; + if (OfficeReader.isSingleParagraph(node)) { + Element par = Misc.getFirstChildElement(node); + String sSeqName = ofr.getSequenceName(par); + if (ofr.isFigureSequenceName(sSeqName)) { bIsCaption = true; } + } + + String sWidth = Misc.truncateLength(getFrame(node).getAttribute(XMLString.SVG_WIDTH)); + if (!bIsCaption) { + ldp.append("\\begin{minipage}{").append(sWidth).append("}").nl(); + } + floatingFramesStack.push(new LinkedList()); + palette.getBlockCv().traverseBlockText(node,ldp,ic); + flushFloatingFrames(ldp,ic); + floatingFramesStack.pop(); + if (!bIsCaption) { + ldp.append("\\end{minipage}"); + } + if (!oc.isNoFootnotes()) { palette.getNoteCv().flushFootnotes(ldp,oc); } + + } + + //------------------------------------------------------------------------- + //handle any pending floating frames + + public void flushFloatingFrames(LaTeXDocumentPortion ldp, Context oc) { + // todo: fix language + LinkedList floatingFrames = (LinkedList) floatingFramesStack.peek(); + int n = floatingFrames.size(); + if (n==0) { return; } + for (int i=0; i<n; i++) { + Element node = (Element) floatingFrames.get(i); + String sName = node.getNodeName(); + if (sName.equals(XMLString.DRAW_IMAGE)) { + handleDrawImageFloat(node,ldp,oc); + } + else if (sName.equals(XMLString.DRAW_TEXT_BOX)) { + handleDrawTextBoxFloat(node,ldp,oc); + } + } + floatingFrames.clear(); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/FieldConverter.java b/source/java/writer2latex/latex/FieldConverter.java new file mode 100644 index 0000000..054fb0e --- /dev/null +++ b/source/java/writer2latex/latex/FieldConverter.java @@ -0,0 +1,672 @@ +/************************************************************************ + * + * FieldConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +//import java.io.UnsupportedEncodingException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.latex.util.Context; +import writer2latex.latex.util.HeadingMap; +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; +import writer2latex.util.ExportNameCollection; +import writer2latex.util.Misc; +import writer2latex.util.SimpleInputBuffer; + +/** + * This class handles text fields and links in the document. + * Packages: lastpage, hyperref, titleref, oooref (all optional) + * TODO: Need proper treatment of "caption" and "text" for sequence + * references not to figures and tables (should be fairly rare, though) + + */ +public class FieldConverter extends ConverterHelper { + + // Links & references + private ExportNameCollection targets = new ExportNameCollection(true); + private ExportNameCollection refnames = new ExportNameCollection(true); + private ExportNameCollection bookmarknames = new ExportNameCollection(true); + private ExportNameCollection seqnames = new ExportNameCollection(true); + private ExportNameCollection seqrefnames = new ExportNameCollection(true); + + // sequence declarations (maps name->text:sequence-decl element) + private Hashtable seqDecl = new Hashtable(); + // first usage of sequence (maps name->text:sequence element) + private Hashtable seqFirst = new Hashtable(); + + private Vector postponedReferenceMarks = new Vector(); + private Vector postponedBookmarks = new Vector(); + + private boolean bUseHyperref = false; + private boolean bUsesPageCount = false; + private boolean bUsesTitleref = false; + private boolean bUsesOooref = false; + + public FieldConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + // hyperref.sty is not compatible with titleref.sty and oooref.sty: + bUseHyperref = config.useHyperref() && !config.useTitleref() && !config.useOooref(); + } + + /** <p>Append declarations needed by the <code>FieldConverter</code> to + * the preamble.</p> + * @param pack the <code>LaTeXDocumentPortion</code> to which + * declarations of packages should be added (<code>\\usepackage</code>). + * @param decl the <code>LaTeXDocumentPortion</code> to which + * other declarations should be added. + */ + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + // use lastpage.sty + if (bUsesPageCount) { + pack.append("\\usepackage{lastpage}").nl(); + } + + // use titleref.sty + if (bUsesTitleref) { + pack.append("\\usepackage{titleref}").nl(); + } + + // use oooref.sty + if (bUsesOooref) { + pack.append("\\usepackage["); + HeadingMap hm = config.getHeadingMap(); + CSVList opt = new CSVList(","); + for (int i=0; i<=hm.getMaxLevel(); i++) { opt.addValue(hm.getName(i)); } + pack.append(opt.toString()).append("]{oooref}").nl(); + } + + // use hyperref.sty + if (bUseHyperref){ + pack.append("\\usepackage{hyperref}").nl(); + pack.append("\\hypersetup{"); + if (config.getBackend()==LaTeXConfig.PDFTEX) pack.append("pdftex, "); + else if (config.getBackend()==LaTeXConfig.DVIPS) pack.append("dvips, "); + //else pack.append("hypertex"); + pack.append("colorlinks=true, linkcolor=blue, citecolor=blue, filecolor=blue, urlcolor=blue"); + if (config.getBackend()==LaTeXConfig.PDFTEX) { + pack.append(createPdfMeta("pdftitle",palette.getMetaData().getTitle())); + if (config.metadata()) { + pack.append(createPdfMeta("pdfauthor",palette.getMetaData().getCreator())) + .append(createPdfMeta("pdfsubject",palette.getMetaData().getSubject())) + .append(createPdfMeta("pdfkeywords",palette.getMetaData().getKeywords())); + } + } + pack.append("}").nl(); + } + + // Export sequence declarations + // The number format is fetched from the first occurence of the + // sequence in the text, while the outline level and the separation + // character are fetched from the declaration + Enumeration names = seqFirst.keys(); + while (names.hasMoreElements()) { + // Get first text:sequence element + String sName = (String) names.nextElement(); + Element first = (Element) seqFirst.get(sName); + // Collect data + String sNumFormat = Misc.getAttribute(first,XMLString.STYLE_NUM_FORMAT); + if (sNumFormat==null) { sNumFormat="1"; } + int nLevel = 0; + String sSepChar = "."; + if (seqDecl.containsKey(sName)) { + Element sdecl = (Element) seqDecl.get(sName); + nLevel = Misc.getPosInteger(sdecl.getAttribute(XMLString.TEXT_DISPLAY_OUTLINE_LEVEL),0); + if (sdecl.hasAttribute(XMLString.TEXT_SEPARATION_CHARACTER)) { + sSepChar = palette.getI18n().convert( + sdecl.getAttribute(XMLString.TEXT_SEPARATION_CHARACTER), + false,palette.getMainContext().getLang()); + } + } + // Create counter + decl.append("\\newcounter{") + .append(seqnames.getExportName(sName)) + .append("}"); + String sPrefix = ""; + if (nLevel>0) { + HeadingMap hm = config.getHeadingMap(); + int nUsedLevel = nLevel<=hm.getMaxLevel() ? nLevel : hm.getMaxLevel(); + if (nUsedLevel>0) { + decl.append("[").append(hm.getName(nUsedLevel)).append("]"); + sPrefix = "\\the"+hm.getName(nUsedLevel)+sSepChar; + } + } + decl.nl() + .append("\\renewcommand\\the") + .append(seqnames.getExportName(sName)) + .append("{").append(sPrefix) + .append(ListStyleConverter.numFormat(sNumFormat)) + .append("{").append(seqnames.getExportName(sName)) + .append("}}").nl(); + } + } + + /** <p>Process sequence declarations</p> + * @param node the text:sequence-decls node + */ + public void handleSequenceDecls(Element node) { + Node child = node.getFirstChild(); + while (child!=null) { + if (Misc.isElement(child,XMLString.TEXT_SEQUENCE_DECL)) { + // Don't process the declaration, but store a reference + seqDecl.put(((Element)child).getAttribute(XMLString.TEXT_NAME),child); + } + child = child.getNextSibling(); + } + } + + /** <p>Process a sequence field (text:sequence tag)</p> + * @param node The element containing the sequence field + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleSequence(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sName = Misc.getAttribute(node,XMLString.TEXT_NAME); + String sRefName = Misc.getAttribute(node,XMLString.TEXT_REF_NAME); + String sFormula = Misc.getAttribute(node,XMLString.TEXT_FORMULA); + if (sFormula==null) { + // If there's no formula, we must use the content as formula + // The parser below requires a namespace, so we add that.. + sFormula = "ooow:"+Misc.getPCDATA(node); + } + if (sName!=null) { + if (ofr.isFigureSequenceName(sName) || ofr.isTableSequenceName(sName)) { + // Export \label only, assuming the number is generated by \caption + if (sRefName!=null && ofr.hasSequenceRefTo(sRefName)) { + ldp.append("\\label{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + } + } + else { + // General purpose sequence -> export as counter + if (!seqFirst.containsKey(sName)) { + // Save first occurence -> used to determine number format + seqFirst.put(sName,node); + } + if (sRefName!=null && ofr.hasSequenceRefTo(sRefName)) { + // Export as {\refstepcounter{name}\thename\label{refname}} + ldp.append("{").append(changeCounter(sName,sFormula,true)) + .append("\\the").append(seqnames.getExportName(sName)) + .append("\\label{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}}"); + } + else { + // Export as \stepcounter{name}{\thename} + ldp.append(changeCounter(sName,sFormula,false)) + .append("{\\the") + .append(seqnames.getExportName(sName)) + .append("}"); + } + } + } + } + + /** <p>Create label for a sequence field (text:sequence tag)</p> + * @param node The element containing the sequence field + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + */ + public void handleSequenceLabel(Element node, LaTeXDocumentPortion ldp) { + String sRefName = Misc.getAttribute(node,XMLString.TEXT_REF_NAME); + if (sRefName!=null && ofr.hasSequenceRefTo(sRefName)) { + ldp.append("\\label{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + } + } + + // According to the spec for OpenDocument, the formula is application + // specific, prefixed with a namespace. OOo uses the namespace ooow, and + // we accept the formulas ooow:<number>, ooow:<name>, ooow:<name>+<number> + // and ooow:<name>-<number> + // Note: In OOo a counter is a 16 bit unsigned integer, whereas a (La)TeX + // counter can be negative - thus there will be a slight deviation in the + // (rare) case of a negative number + private String changeCounter(String sName, String sFormula, boolean bRef) { + if (sFormula!=null) { + sFormula = sFormula.trim(); + if (sFormula.startsWith("ooow:")) { + SimpleInputBuffer input = new SimpleInputBuffer(sFormula.substring(5)); + if (input.peekChar()>='0' && input.peekChar()<='9') { + // Value is <number> + String sNumber = input.getInteger(); + if (input.atEnd()) { + return setCounter(sName, Misc.getPosInteger(sNumber,0), bRef); + } + } + else if (input.peekChar()=='-') { + // Value is a negative <number> + input.getChar(); + if (input.peekChar()>='0' && input.peekChar()<='9') { + String sNumber = input.getInteger(); + if (input.atEnd()) { + return setCounter(sName, -Misc.getPosInteger(sNumber,0), bRef); + } + } + } + else { + // Value starts with <name> + String sToken = input.getIdentifier(); + if (sToken.equals(sName)) { + input.skipSpaces(); + if (input.peekChar()=='+') { + // Value is <name>+<number> + input.getChar(); + input.skipSpaces(); + String sNumber = input.getInteger(); + if (input.atEnd()) { + return addtoCounter(sName, Misc.getPosInteger(sNumber,0), bRef); + } + } + else if (input.peekChar()=='-') { + // Value is <name>-<number> + input.getChar(); + input.skipSpaces(); + String sNumber = input.getInteger(); + if (input.atEnd()) { + return addtoCounter(sName, -Misc.getPosInteger(sNumber,0), bRef); + } + } + else if (input.atEnd()) { + // Value is <name> + return addtoCounter(sName, 0, bRef); + } + } + } + } + } + // No formula, or a formula we don't understand -> use default behavior + return stepCounter(sName, bRef); + } + + private String stepCounter(String sName, boolean bRef) { + if (bRef) { + return "\\refstepcounter{" + seqnames.getExportName(sName) + "}"; + } + else { + return "\\stepcounter{" + seqnames.getExportName(sName) + "}"; + } + } + + private String addtoCounter(String sName, int nValue, boolean bRef) { + if (nValue==1) { + return stepCounter(sName, bRef); + } + else if (bRef) { + return "\\addtocounter{" + seqnames.getExportName(sName) + "}" + + "{" + Integer.toString(nValue-1) + "}" + + "\\refstepcounter{" + seqnames.getExportName(sName) + "}"; + } + else if (nValue!=0) { + return "\\addtocounter{" + seqnames.getExportName(sName) + "}" + + "{" + Integer.toString(nValue) + "}"; + } + else { + return ""; + } + } + + private String setCounter(String sName, int nValue, boolean bRef) { + if (bRef) { + return "\\setcounter{" + seqnames.getExportName(sName) + "}" + + "{" + Integer.toString(nValue-1) + "}" + + "\\refstepcounter{" + seqnames.getExportName(sName) + "}"; + } + else { + return "\\setcounter{" + seqnames.getExportName(sName) + "}" + + "{" + Integer.toString(nValue) + "}"; + } + } + + /** <p>Process a sequence reference (text:sequence-ref tag)</p> + * @param node The element containing the sequence reference + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleSequenceRef(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sRefName = Misc.getAttribute(node,XMLString.TEXT_REF_NAME); + String sFormat = Misc.getAttribute(node,XMLString.TEXT_REFERENCE_FORMAT); + String sName = ofr.getSequenceFromRef(sRefName); + if (sRefName!=null) { + if (sFormat==null || "page".equals(sFormat)) { + ldp.append("\\pageref{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + } + else if ("value".equals(sFormat)) { + ldp.append("\\ref{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + } + else if ("category-and-value".equals(sFormat)) { + // Export as Name~\\ref{refname} + if (sName!=null) { + if (ofr.isFigureSequenceName(sName)) { + ldp.append("\\figurename~"); + } + else if (ofr.isTableSequenceName(sName)) { + ldp.append("\\tablename~"); + } + else { + ldp.append(sName).append("~"); + } + } + ldp.append("\\ref{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + } + else if ("chapter".equals(sFormat) && config.useOooref()) { + ldp.append("\\chapterref{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + bUsesOooref = true; + } + else if ("caption".equals(sFormat) && config.useTitleref() && + (ofr.isFigureSequenceName(sName) || ofr.isTableSequenceName(sName))) { + ldp.append("\\titleref{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + bUsesTitleref = true; + } + else if ("text".equals(sFormat) && config.useTitleref() && + (ofr.isFigureSequenceName(sName) || ofr.isTableSequenceName(sName))) { + // This is a combination of "category-and-value" and "caption" + // Export as \\figurename~\ref{refname}:~\titleref{refname} + if (ofr.isFigureSequenceName(sName)) { + ldp.append("\\figurename"); + } + else if (ofr.isTableSequenceName(sName)) { + ldp.append("\\tablename"); + } + ldp.append("~\\ref{seq:") + .append(seqrefnames.getExportName(sRefName)) + .append("}:~\\titleref{") + .append(seqrefnames.getExportName(sRefName)) + .append("}"); + bUsesTitleref = true; + } + else { // use current value + palette.getInlineCv().traversePCDATA(node,ldp,oc); + } + } + } + + + /** <p>Process a reference mark (text:reference-mark tag)</p> + * @param node The element containing the reference mark + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleReferenceMark(Element node, LaTeXDocumentPortion ldp, Context oc) { + if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) { + // Note: Always include \label here, even when it's not used + String sName = node.getAttribute(XMLString.TEXT_NAME); + if (sName!=null) { + ldp.append("\\label{ref:"+refnames.getExportName(sName)+"}"); + } + } + else { + // Reference marks should not appear within \section or \caption + postponedReferenceMarks.add(node); + } + } + + /** <p>Process a reference (text:reference-ref tag)</p> + * @param node The element containing the reference + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleReferenceRef(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT); + String sName = node.getAttribute(XMLString.TEXT_REF_NAME); + if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) { + ldp.append("\\pageref{ref:"+refnames.getExportName(sName)+"}"); + } + else if ("chapter".equals(sFormat) && ofr.referenceMarkInHeading(sName)) { + // This is safe if the reference mark is contained in a heading + ldp.append("\\ref{ref:"+refnames.getExportName(sName)+"}"); + } + else { // use current value + palette.getInlineCv().traversePCDATA(node,ldp,oc); + } + } + + /** <p>Process a bookmark (text:bookmark tag)</p> + * <p>A bookmark may be the target for either a hyperlink or a reference, + * so this will generate a <code>\\hyperref</code> and/or a <code>\\label</code></p> + * @param node The element containing the bookmark + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleBookmark(Element node, LaTeXDocumentPortion ldp, Context oc) { + if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) { + String sName = node.getAttribute(XMLString.TEXT_NAME); + if (sName!=null) { + // A bookmark may be used as a target for a hyperlink as well as + // for a reference. We export whatever is actually used: + addTarget(node,"",ldp); + if (ofr.hasBookmarkRefTo(sName)) { + ldp.append("\\label{bkm:"+bookmarknames.getExportName(sName)+"}"); + } + } + } + else { + // Bookmarks should not appear within \section or \caption + postponedBookmarks.add(node); + } + } + + /** <p>Process a bookmark reference (text:bookmark-ref tag).</p> + * @param node The element containing the bookmark reference + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleBookmarkRef(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT); + String sName = node.getAttribute(XMLString.TEXT_REF_NAME); + if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) { + ldp.append("\\pageref{bkm:"+bookmarknames.getExportName(sName)+"}"); + } + else if ("chapter".equals(sFormat) && ofr.bookmarkInHeading(sName)) { + // This is safe if the bookmark is contained in a heading + ldp.append("\\ref{bkm:"+bookmarknames.getExportName(sName)+"}"); + } + else { // use current value + palette.getInlineCv().traversePCDATA(node,ldp,oc); + } + } + + /** <p>Process pending reference marks and bookmarks (which may have been + * postponed within sections, captions or verbatim text.</p> + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void flushReferenceMarks(LaTeXDocumentPortion ldp, Context oc) { + // We may still be in a context with no reference marks + if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) { + // Type out all postponed reference marks + int n = postponedReferenceMarks.size(); + for (int i=0; i<n; i++) { + handleReferenceMark((Element) postponedReferenceMarks.get(i),ldp,oc); + } + postponedReferenceMarks.clear(); + // Type out all postponed bookmarks + n = postponedBookmarks.size(); + for (int i=0; i<n; i++) { + handleBookmark((Element) postponedBookmarks.get(i),ldp,oc); + } + postponedBookmarks.clear(); + } + } + + /** <p>Process a hyperlink (text:a tag)</p> + * @param node The element containing the hyperlink + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleAnchor(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sHref = node.getAttribute(XMLString.XLINK_HREF); + if (sHref!=null) { + if (sHref.startsWith("#")) { + // TODO: hyperlinks to headings (?) and objects + if (bUseHyperref) { + ldp.append("\\hyperlink{") + .append(targets.getExportName(Misc.urlDecode(sHref.substring(1)))) + .append("}{"); + // ignore text style (let hyperref.sty handle the decoration): + palette.getInlineCv().traverseInlineText(node,ldp,oc); + ldp.append("}"); + } + else { // user don't want to include hyperlinks + palette.getInlineCv().handleTextSpan(node,ldp,oc); + } + } + else { + if (bUseHyperref) { + if (ofr.getTextContent(node).trim().equals(sHref)) { + // The link text equals the url + ldp.append("\\url{") + .append(oc.isInFootnote() ? escapeHref(Misc.urlDecode(sHref)) : Misc.urlDecode(sHref)) + .append("}"); + } + else { + ldp.append("\\href{") + .append(oc.isInFootnote() ? escapeHref(Misc.urlDecode(sHref)) : Misc.urlDecode(sHref)) + .append("}{"); + // ignore text style (let hyperref.sty handle the decoration): + palette.getInlineCv().traverseInlineText(node,ldp,oc); + ldp.append("}"); + } + } + else { // user don't want to include hyperlinks + palette.getInlineCv().handleTextSpan(node,ldp,oc); + } + } + } + else { + palette.getInlineCv().handleTextSpan(node,ldp,oc); + } + } + + /** <p>Add a <code>\\hypertarget</code></p> + * @param node The element containing the name of the target + * @param sSuffix A suffix to be added to the target, + * e.g. "|table" for a reference to a table. + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + */ + public void addTarget(Element node, String sSuffix, LaTeXDocumentPortion ldp) { + // TODO: Remove this and use addTarget by name only + String sName = node.getAttribute(XMLString.TEXT_NAME); + if (sName == null) { sName = node.getAttribute(XMLString.TABLE_NAME); } + if (sName == null || !bUseHyperref) { return; } + if (!ofr.hasLinkTo(sName+sSuffix)) { return; } + ldp.append("\\hypertarget{") + .append(targets.getExportName(sName+sSuffix)) + .append("}{}"); + } + + /** <p>Add a <code>\\hypertarget</code></p> + * @param sName The name of the target + * @param sSuffix A suffix to be added to the target, + * e.g. "|table" for a reference to a table. + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + */ + public void addTarget(String sName, String sSuffix, LaTeXDocumentPortion ldp) { + if (sName!=null && bUseHyperref && ofr.hasLinkTo(sName+sSuffix)) { + ldp.append("\\hypertarget{") + .append(targets.getExportName(sName+sSuffix)) + .append("}{}"); + } + } + + /** <p>Process a page number field (text:page-number tag)</p> + * @param node The element containing the page number field + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handlePageNumber(Element node, LaTeXDocumentPortion ldp, Context oc) { + // TODO: Obey attributes! + ldp.append("\\thepage{}"); + } + + /** <p>Process a page count field (text:page-count tag)</p> + * @param node The element containing the page count field + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handlePageCount(Element node, LaTeXDocumentPortion ldp, Context oc) { + // TODO: Obey attributes! + // Note: Actually LastPage refers to the page number of the last page, not the number of pages + if (config.useLastpage()) { + bUsesPageCount = true; + ldp.append("\\pageref{LastPage}"); + } + else { + ldp.append("?"); + } + } + + // Helpers: + + private String createPdfMeta(String sName, String sValue) { + if (sValue==null) { return ""; } + // Replace commas with semicolons (the keyval package doesn't like commas): + sValue = sValue.replace(',', ';'); + // Meta data is assumed to be in the default language: + return ", "+sName+"="+palette.getI18n().convert(sValue,false,palette.getMainContext().getLang()); + } + + // For href within footnote, we have to escape the # + private String escapeHref(String s) { + StringBuffer buf = new StringBuffer(); + for (int i=0; i<s.length(); i++) { + if (s.charAt(i)=='#') { buf.append("\\#"); } + else { buf.append(s.charAt(i)); } + } + return buf.toString(); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/HeadingConverter.java b/source/java/writer2latex/latex/HeadingConverter.java new file mode 100644 index 0000000..ef2f7bd --- /dev/null +++ b/source/java/writer2latex/latex/HeadingConverter.java @@ -0,0 +1,357 @@ +/************************************************************************ + * + * HeadingConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.latex; + +//import java.util.Hashtable; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.util.*; +import writer2latex.office.*; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; +import writer2latex.latex.util.HeadingMap; +//import writer2latex.latex.util.StyleMap; + +/* This class converts OpenDocument headings (<code>text:h</code>) and + * paragraph styles/formatting into LaTeX + * Export of formatting depends on the option "formatting": + * <ul> + * <li><code>ignore_all</code> + * <li><code>ignore_most</code> + * <li><code>convert_basic</code> + * <li><code>convert_most</code> + * <li><code>convert_all</code> + * </ul> + */ +public class HeadingConverter extends ConverterHelper { + private String[] sHeadingStyles = new String[11]; + + /** Constructs a new <code>HeadingConverter</code>. + */ + public HeadingConverter(OfficeReader ofr, LaTeXConfig config, + ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + appendHeadingStyles(decl); + } + + /** Process a heading + * @param node The text:h element node containing the heading + * @param ldp The LaTeXDocumentPortion to add LaTeX code to + * @param oc The current context + */ + public void handleHeading(Element node, LaTeXDocumentPortion ldp, Context oc) { + // Get the level, the heading map and the style name + int nLevel = ofr.isOpenDocument() ? + Misc.getPosInteger(Misc.getAttribute(node, XMLString.TEXT_OUTLINE_LEVEL),1) : + Misc.getPosInteger(Misc.getAttribute(node, XMLString.TEXT_LEVEL),1); + HeadingMap hm = config.getHeadingMap(); + String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + + if (nLevel<=hm.getMaxLevel()) { + // Always push the font used + palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getParStyle(sStyleName))); + + Context ic = (Context) oc.clone(); + ic.setInSection(true); + // Footnotes with more than one paragraph are not allowed within + // sections. To be safe, we disallow all footnotes + ic.setNoFootnotes(true); + + // Apply style + BeforeAfter baHardPage = new BeforeAfter(); + BeforeAfter baHardChar = new BeforeAfter(); + applyHardHeadingStyle(nLevel, sStyleName, + baHardPage, baHardChar, ic); + + // Export the heading + ldp.append(baHardPage.getBefore()); + ldp.append("\\"+hm.getName(nLevel)); + // If this heading contains formatting, add optional argument: + if (baHardChar.getBefore().length()>0 || containsElements(node)) { + ldp.append("["); + palette.getInlineCv().traversePlainInlineText(node,ldp,ic); + ldp.append("]"); + } + ldp.append("{").append(baHardChar.getBefore()); + palette.getInlineCv().traverseInlineText(node,ldp,ic); + ldp.append(baHardChar.getAfter()).append("}").nl(); + ldp.append(baHardPage.getAfter()); + + // Include pending index marks, labels, footnotes & floating frames + palette.getFieldCv().flushReferenceMarks(ldp,oc); + palette.getIndexCv().flushIndexMarks(ldp,oc); + palette.getNoteCv().flushFootnotes(ldp,oc); + palette.getDrawCv().flushFloatingFrames(ldp,ic); + + // Pop the font name + palette.getI18n().popSpecialTable(); + } + else { // beyond supported headings - export as ordinary paragraph + palette.getParCv().handleParagraph(node,ldp,oc,false); + } + } + + + /** Use a paragraph style on a heading. If hard paragraph formatting + * is applied to a heading, page break and font is converted - other + * hard formatting is ignored. + * This method also collects name of heading style + * @param <code>nLevel</code> The level of this heading + * @param <code>sStyleName</code> the name of the paragraph style to use + * @param <code>baPage</code> a <code>BeforeAfter</code> to put page break code into + * @param <code>baText</code> a <code>BeforeAfter</code> to put character formatting code into + * @param <code>context</code> the current context. This method will use and update the formatting context + */ + private void applyHardHeadingStyle(int nLevel, String sStyleName, + BeforeAfter baPage, BeforeAfter baText, Context context) { + + // Get the style + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style==null) { return; } + + // Register heading style + if (sHeadingStyles[nLevel]==null) { + sHeadingStyles[nLevel] = style.isAutomatic() ? style.getParentName() : sStyleName; + } + + // Do conversion + if (style.isAutomatic()) { + palette.getPageSc().applyPageBreak(style,false,baPage); + palette.getCharSc().applyHardCharFormatting(style,baText); + } + + // Update context + context.updateFormattingFromStyle(style); + } + + + /** Convert heading styles and outline numbering to LaTeX. + * An array of stylenames to use is required: The OOo writer file format + * allows different paragraph styles to be applied to individual headings, + * so this is not included in the styles. + * LaTeX (and OOo Writer!) usually uses the same format for all headings. + * @param ldp the LaTeXDocumentPortion to add definitions to. + */ + // TODO: use method from ListStyleConverter to create labels + private void appendHeadingStyles(LaTeXDocumentPortion ldp) { + // The user may not want to convert the formatting of headings + if (config.formatting()<=LaTeXConfig.IGNORE_MOST) { return; } + + HeadingMap hm = config.getHeadingMap(); + + // OK, we are going to convert. First find the max level for headings + int nMaxLevel = 0; + for (int i=1; i<=5; i++) { if (sHeadingStyles[i]!=null) { nMaxLevel=i; } } + if (nMaxLevel==0) { return; } // no headings, nothing to do! + if (nMaxLevel>hm.getMaxLevel()) { nMaxLevel = hm.getMaxLevel(); } + + boolean bOnlyNum = config.formatting()==LaTeXConfig.CONVERT_BASIC; + if (bOnlyNum) { + ldp.append("% Outline numbering").nl(); + } + else { + ldp.append("% Headings and outline numbering").nl() + .append("\\makeatletter").nl(); + } + + // Paragraph style for headings: + if (!bOnlyNum) { + for (int i=1; i<=nMaxLevel; i++) { + if (sHeadingStyles[i]!=null) { + StyleWithProperties style = ofr.getParStyle(sHeadingStyles[i]); + if (style!=null) { + BeforeAfter decl = new BeforeAfter(); + BeforeAfter comm = new BeforeAfter(); + + palette.getPageSc().applyPageBreak(style,true,decl); + + palette.getCharSc().applyNormalFont(decl); + palette.getCharSc().applyFont(style,true,true,decl,new Context()); + palette.getParCv().applyAlignment(style,false,true,decl); + + palette.getI18n().applyLanguage(style,false,true,comm); + palette.getCharSc().applyFontEffects(style,true,comm); + + String sMarginTop = style.getAbsoluteLength(XMLString.FO_MARGIN_TOP); + String sMarginBottom = style.getAbsoluteLength(XMLString.FO_MARGIN_BOTTOM); + String sMarginLeft = style.getAbsoluteLength(XMLString.FO_MARGIN_LEFT); + + String sSecName = hm.getName(i); + if (!comm.isEmpty()) { // have to create a cs for this heading + ldp.append("\\newcommand\\cs").append(sSecName).append("[1]{") + .append(comm.getBefore()).append("#1").append(comm.getAfter()) + .append("}").nl(); + } + ldp.append("\\renewcommand\\").append(sSecName) + .append("{\\@startsection{").append(sSecName).append("}{"+hm.getLevel(i)) + .append("}{"+sMarginLeft+"}{"); + // Suppress indentation after heading? currently not.. + // ldp.append("-"); + ldp.append(sMarginTop) + .append("}{").append(sMarginBottom).append("}{"); + // Note: decl.getAfter() may include a page break after, which we ignore + ldp.append(decl.getBefore()); + if (!comm.isEmpty()) { + ldp.append("\\cs").append(sSecName); + } + ldp.append("}}").nl(); + } + } + } + } + + // redefine formatting of section counters + // simplified if the user wants to ignore formatting + if (!bOnlyNum) { + ldp.append("\\renewcommand\\@seccntformat[1]{") + .append("\\csname @textstyle#1\\endcsname{\\csname the#1\\endcsname}") + .append("\\csname @distance#1\\endcsname}").nl(); + } + + // Collect numbering styles and set secnumdepth + int nSecnumdepth = nMaxLevel; + ListStyle outline = ofr.getOutlineStyle(); + String[] sNumFormat = new String[6]; + for (int i=nMaxLevel; i>=1; i--) { + sNumFormat[i] = ListStyleConverter.numFormat(outline.getLevelProperty(i, + XMLString.STYLE_NUM_FORMAT)); + if (sNumFormat[i]==null || "".equals(sNumFormat[i])) { + nSecnumdepth = i-1; + } + } + ldp.append("\\setcounter{secnumdepth}{"+nSecnumdepth+"}").nl(); + + for (int i=1; i<=nMaxLevel; i++) { + if (sNumFormat[i]==null || "".equals(sNumFormat[i])) { + // no numbering at this level + if (!bOnlyNum) { + ldp.append("\\newcommand\\@distance") + .append(hm.getName(i)).append("{}").nl() + .append("\\newcommand\\@textstyle") + .append(hm.getName(i)).append("[1]{#1}").nl(); + } + } + else { + if (!bOnlyNum) { + // Distance between label and text: + String sDistance = outline.getLevelStyleProperty(i,XMLString.TEXT_MIN_LABEL_DISTANCE); + ldp.append("\\newcommand\\@distance") + .append(hm.getName(i)).append("{"); + if (sDistance!=null) { + ldp.append("\\hspace{").append(sDistance).append("}"); + } + ldp.append("}").nl(); + // Label width and alignment + String sLabelWidth = outline.getLevelStyleProperty(i,XMLString.TEXT_MIN_LABEL_WIDTH); + String sTextAlign = outline.getLevelStyleProperty(i,XMLString.FO_TEXT_ALIGN); + String sAlignmentChar = "l"; // start (or left) is default + if (sTextAlign!=null) { + if ("end".equals(sTextAlign)) { sAlignmentChar="r"; } + else if ("right".equals(sTextAlign)) { sAlignmentChar="r"; } + else if ("center".equals(sTextAlign)) { sAlignmentChar="c"; } + } + // Textstyle to use for label: + String sStyleName = outline.getLevelProperty(i,XMLString.TEXT_STYLE_NAME); + // Prefix and suffix text to decorate the label + String sPrefix = outline.getLevelProperty(i,XMLString.STYLE_NUM_PREFIX); + String sSuffix = outline.getLevelProperty(i,XMLString.STYLE_NUM_SUFFIX); + // TODO is this correct?? todo: space-before??? start value??? + BeforeAfter baText = new BeforeAfter(); + if (!bOnlyNum) {palette.getCharSc().applyTextStyle(sStyleName,baText,new Context()); } + ldp.append("\\newcommand\\@textstyle") + .append(hm.getName(i)).append("[1]{"); + if (!bOnlyNum && sLabelWidth!=null) { + ldp.append("\\makebox[").append(sLabelWidth).append("][").append(sAlignmentChar).append("]{"); + } + ldp.append(baText.getBefore()) + .append(sPrefix!=null ? sPrefix : "") + .append("#1") + .append(sSuffix!=null ? sSuffix : "") + .append(baText.getAfter()); + if (!bOnlyNum && sLabelWidth!=null) { + ldp.append("}"); + } + ldp.append("}").nl(); + } + + // The label: + int nLevels = Misc.getPosInteger(outline.getLevelProperty(i, + XMLString.TEXT_DISPLAY_LEVELS),1); + ldp.append("\\renewcommand\\the") + .append(hm.getName(i)) + .append("{"); + for (int j=i-nLevels+1; j<i; j++) { + ldp.append(sNumFormat[j]) + .append("{").append(sectionName(j)).append("}") + .append("."); + } + ldp.append(sNumFormat[i]) + .append("{").append(hm.getName(i)).append("}") + .append("}").nl(); + } + + } + + if (!bOnlyNum) { + ldp.append("\\makeatother").nl(); + } + } + + /* Check to see if this node contains any element nodes, except reference marks */ + public boolean containsElements(Node node) { + if (!node.hasChildNodes()) { return false; } + NodeList list = node.getChildNodes(); + int nLen = list.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = list.item(i); + if (child.getNodeType()==Node.ELEMENT_NODE && + !child.getNodeName().startsWith(XMLString.TEXT_REFERENCE_MARK)) { + return true; + } + } + return false; + } + + static final String sectionName(int nLevel){ + switch (nLevel) { + case 1: return "section"; + case 2: return "subsection"; + case 3: return "subsubsection"; + case 4: return "paragraph"; + case 5: return "subparagraph"; + default: return null; + } + } + + +} diff --git a/source/java/writer2latex/latex/IndexConverter.java b/source/java/writer2latex/latex/IndexConverter.java new file mode 100644 index 0000000..846dbd2 --- /dev/null +++ b/source/java/writer2latex/latex/IndexConverter.java @@ -0,0 +1,242 @@ +/************************************************************************ + * + * IndexConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import java.util.Vector; + +import org.w3c.dom.Element; +//import org.w3c.dom.Node; + +import writer2latex.util.Misc; + +import writer2latex.office.IndexMark; +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; + +import writer2latex.latex.util.Context; + +/** + * <p>This class handles indexes (table of contents, list of tables, list of + * illustrations, object index, user index, alphabetical index) + * as well as their associated index marks.</p> + */ +public class IndexConverter extends ConverterHelper { + + private boolean bContainsAlphabeticalIndex = false; + + private Vector postponedIndexMarks = new Vector(); + + /** <p>Construct a new <code>IndexConverter</code>. + * @param config the configuration to use + * @param palette the <code>ConverterPalette</code> to link to + * if such a document is created by the <code>IndexConverter</code> + */ + public IndexConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + /** <p>Append declarations needed by the <code>IndexConverter</code> to + * the preamble. + * @param pack the <code>LaTeXDocumentPortion</code> to which + * declarations of packages should be added (<code>\\usepackage</code>). + * @param decl the <code>LaTeXDocumentPortion</code> to which + * other declarations should be added. + */ + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bContainsAlphabeticalIndex) { + pack.append("\\usepackage{makeidx}").nl(); + decl.append("\\makeindex").nl(); + } + } + + /** Process Table of Contents (text:table-of-content tag) + * @param node The element containing the Table of Contents + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleTOC (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.noIndex()) { return; } + /* TODO: Apply more formatting by modfification of \l@section etc. + Something like this: + \newcommand\l@section[2]{\@dottedtocline{1}{1.5em}{2.3em}{\textbf{#1}}{\textit{#2}} + Textformatting is trivial; see article.cls for examples of more complicated + formatting. Note: The section number can't be formatted indivdually.*/ + + Element source = Misc.getChildByTagName(node,XMLString.TEXT_TABLE_OF_CONTENT_SOURCE); + if (source!=null) { + if ("chapter".equals(source.getAttribute(XMLString.TEXT_INDEX_SOURCE))) { + ldp.append("[Warning: Table of content (for this chapter) ignored!]").nl().nl(); + } + else { + int nLevel = Misc.getPosInteger(source.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1); + ldp.append("\\setcounter{tocdepth}{"+nLevel+"}").nl(); + Element title = Misc.getChildByTagName(source,XMLString.TEXT_INDEX_TITLE_TEMPLATE); + if (title!=null) { + ldp.append("\\renewcommand\\contentsname{"); + palette.getInlineCv().traversePCDATA(title,ldp,oc); + ldp.append("}").nl(); + } + } + } + ldp.append("\\tableofcontents").nl(); + } + + /** Process List of Illustrations (text:list-of-illustrations tag) + * @param node The element containing the List of Illustrations + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleLOF (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.noIndex()) { return; } + ldp.append("\\listoffigures").nl(); + } + + /** Process List of Tables (text:list-of-tables tag) + * @param node The element containing the List of Tables + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleLOT (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.noIndex()) { return; } + ldp.append("\\listoftables").nl(); + } + + /** Process Object Index (text:object index tag) + * @param node The element containing the Object Index + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleObjectIndex (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.noIndex()) { return; } + ldp.append("[Warning: Object index ignored]").nl().nl(); + } + + /** Process User Index (text:user-index tag) + * @param node The element containing the User Index + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleUserIndex (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.noIndex()) { return; } + ldp.append("[Warning: User index ignored]").nl().nl(); + } + + + /** Process Alphabetical Index (text:alphabetical-index tag) + * @param node The element containing the Alphabetical Index + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleAlphabeticalIndex (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (config.noIndex()) { return; } + ldp.append("\\printindex").nl(); + bContainsAlphabeticalIndex = true; + } + + + /** Process an Alphabetical Index Mark (text:alphabetical-index-mark{-start} tag) + * @param node The element containing the Mark + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleAlphabeticalIndexMark(Element node, LaTeXDocumentPortion ldp, Context oc) { + if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) { + String sValue = IndexMark.getIndexValue(node); + if (sValue!=null) { + ldp.append("\\index{"); + String sKey1 = IndexMark.getKey1(node); + if (sKey1!=null) { + writeIndexText(sKey1.trim(),ldp,oc); + ldp.append("!"); + } + String sKey2 = IndexMark.getKey2(node); + if (sKey2!=null) { + writeIndexText(sKey2.trim(),ldp,oc); + ldp.append("!"); + } + writeIndexText(sValue.trim(),ldp,oc); + ldp.append("}"); + } + } + else { + // Index marks should not appear within \section or \caption + postponedIndexMarks.add(node); + } + } + + public void flushIndexMarks(LaTeXDocumentPortion ldp, Context oc) { + // We may still be in a context with no index marks + if (!oc.isInSection() && !oc.isInCaption() && !oc.isVerbatim()) { + // Type out all postponed index marks + int n = postponedIndexMarks.size(); + for (int i=0; i<n; i++) { + handleAlphabeticalIndexMark((Element) postponedIndexMarks.get(i),ldp,oc); + } + postponedIndexMarks.clear(); + } + } + + + // Helper: Write the text of an index mark, escaping special characters + private void writeIndexText(String sText, LaTeXDocumentPortion ldp, Context oc) { + String sTextOut = palette.getI18n().convert(sText,false,oc.getLang()); + // need to escape !, @, | and ": + int nLen = sTextOut.length(); + boolean bBackslash = false; + for (int i=0; i<nLen; i++) { + if (bBackslash) { + ldp.append(sTextOut.substring(i,i+1)); + bBackslash = false; + } + else { + switch (sTextOut.charAt(i)) { + case '\\' : bBackslash = true; + ldp.append("\\"); + break; + case '~' : + case '\u00A0' : + // Non-breaking space causes trouble in index: + ldp.append(" "); + break; + case '!' : + case '@' : + case '|' : + case '"' : ldp.append("\""); + default : ldp.append(sTextOut.substring(i,i+1)); + } + } + } + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/InlineConverter.java b/source/java/writer2latex/latex/InlineConverter.java new file mode 100644 index 0000000..9450eb6 --- /dev/null +++ b/source/java/writer2latex/latex/InlineConverter.java @@ -0,0 +1,636 @@ +/************************************************************************ + * + * InlineConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.util.Misc; +import writer2latex.office.OfficeReader; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; +import writer2latex.latex.util.HeadingMap; + +/** + * <p>This class handles basic inline text.</p> + */ +public class InlineConverter extends ConverterHelper { + + private String sTabstop = "\\ \\ "; + private boolean bHasPdfannotation = false; + + public InlineConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + // Get custom code for tab stops + if (config.getTabstop().length()>0) { + sTabstop = config.getTabstop(); + } + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bHasPdfannotation) { + decl.append("\\newcommand\\pdfannotation[1]") + .append("{\\ifx\\pdfoutput\\undefined\\marginpar{#1}\\else") + .append("\\pdfstringdef\\tempstring{#1}\\marginpar{") + .append("\\pdfannot width 5cm height 12pt depth 4cm ") + .append("{ /Subtype /Text /Open false /Contents(\\tempstring) /Color [1 0 0]}") + .append("}\\fi}").nl(); + } + } + + /** Handle a text:span element + */ + public void handleTextSpan(Element node, LaTeXDocumentPortion ldp, Context oc) { + if (oc.isMathMode()) { handleTextSpanMath(node, ldp, oc); } + else { handleTextSpanText(node, ldp, oc); } + } + + private void handleTextSpanMath(Element node, LaTeXDocumentPortion ldp, Context oc) { + // TODO: Handle a selection of formatting attributes: color, supscript... + String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + StyleWithProperties style = ofr.getTextStyle(sStyleName); + + // Always push the font used + palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(style)); + + // Convert formatting + BeforeAfter ba = new BeforeAfter(); + if (style!=null) { + String sPos = style.getProperty(XMLString.STYLE_TEXT_POSITION, true); + if (sPos!=null) { + if (sPos.startsWith("sub") || sPos.startsWith("-")) { + ba.add("_{", "}"); + } + else if (sPos.startsWith("super") || !sPos.startsWith("0%")) { + ba.add("^{", "}"); + } + } + } + + ldp.append(ba.getBefore()); + traverseInlineMath(node, ldp, oc); + ldp.append(ba.getAfter()); + + // finally pop the font table + palette.getI18n().popSpecialTable(); + } + + private void handleTextSpanText(Element node, LaTeXDocumentPortion ldp, Context oc) { + String styleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + + // Check for strict handling of styles + String sDisplayName = ofr.getTextStyles().getDisplayName(styleName); + if (config.otherStyles()!=LaTeXConfig.ACCEPT && !config.getTextStyleMap().contains(sDisplayName)) { + if (config.otherStyles()==LaTeXConfig.WARNING) { + System.err.println("Warning: Text with style "+sDisplayName+" was ignored"); + } + else if (config.otherStyles()==LaTeXConfig.ERROR) { + ldp.append("% Error in source document: Text with style ") + .append(palette.getI18n().convert(sDisplayName,false,oc.getLang())) + .append(" was ignored").nl(); + } + // Ignore this text: + return; + } + + boolean styled = true; + + // don't style it if a {foot|end}note is the only content + if (onlyNote(node) || OfficeReader.getCharacterCount(node)==0) { styled = false; } + + // Also don't style it if we're already within a verbatim environment + if (oc.isVerbatim()) { styled = false; } + + boolean bNoFootnotes = false; + + // Always push the font used + palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getTextStyle(styleName))); + + // Apply the style + BeforeAfter ba = new BeforeAfter(); + Context ic = (Context) oc.clone(); + if (styled) { palette.getCharSc().applyTextStyle(styleName,ba,ic); } + + // Footnote problems: + // No footnotes in sub/superscript (will disappear) + // No multiparagraph footnotes embedded in text command (eg. \textbf{..}) + // Simple solution: styled text element is forbidden footnote area + if (styled && !ic.isInFootnote()) { bNoFootnotes = true; } + + // Temp solution: Ignore hard formatting in header/footer (name clash problem) + // only in package format. + StyleWithProperties style = ofr.getTextStyle(styleName); + if (ofr.isPackageFormat() && (style!=null && style.isAutomatic()) && ic.isInHeaderFooter()) { + styled = false; + } + + if (styled) { + if (bNoFootnotes) { ic.setNoFootnotes(true); } + ldp.append(ba.getBefore()); + } + + traverseInlineText(node,ldp,ic); + + if (styled) { + ldp.append(ba.getAfter()); + ic.setNoFootnotes(false); + if (!ic.isInFootnote()) { palette.getNoteCv().flushFootnotes(ldp,oc); } + } + + // Flush any pending index marks and reference marks + palette.getFieldCv().flushReferenceMarks(ldp,oc); + palette.getIndexCv().flushIndexMarks(ldp,oc); + + // finally pop the special table + palette.getI18n().popSpecialTable(); + } + + public void traverseInlineText(Element node, LaTeXDocumentPortion ldp, Context oc) { + if (oc.isVerbatim()) { + traverseVerbatimInlineText(node,ldp,oc); + } + else if (oc.isMathMode()) { + traverseInlineMath(node,ldp,oc); + } + else { + traverseOrdinaryInlineText(node,ldp,oc); + } + } + + // Traverse ordinary inline text in text mode (temporarily changing to math + // mode for a sequence of text:span with style "OOoLaTeX") + private void traverseOrdinaryInlineText(Element node,LaTeXDocumentPortion ldp, Context oc) { + Node childNode = node.getFirstChild(); + while (childNode!=null) { + short nodeType = childNode.getNodeType(); + + switch (nodeType) { + case Node.TEXT_NODE: + String s = childNode.getNodeValue(); + if (s.length() > 0) { + ldp.append(palette.getI18n().convert(s, false, oc.getLang())); + } + break; + + case Node.ELEMENT_NODE: + Element child = (Element)childNode; + String sName = child.getTagName(); + if (sName.equals(XMLString.TEXT_SPAN)) { + String sStyleName = child.getAttribute(XMLString.TEXT_STYLE_NAME); + boolean bIsMathSpan = "OOoLaTeX".equals(ofr.getTextStyles().getDisplayName(sStyleName)); + if (bIsMathSpan) { + // Temporarily change to math mode + Context ic = (Context) oc.clone(); + ic.setMathMode(true); + + ldp.append("$"); + + Node remember; + boolean bContinue = false; + + do { + handleTextSpanMath((Element)childNode, ldp, ic); + remember = childNode; + childNode = childNode.getNextSibling(); + bContinue = false; + if (childNode!=null && childNode.getNodeType()==Node.ELEMENT_NODE && + childNode.getNodeName().equals(XMLString.TEXT_SPAN)) { + sStyleName = Misc.getAttribute(childNode,XMLString.TEXT_STYLE_NAME); + if ("OOoLaTeX".equals(ofr.getTextStyles().getDisplayName(sStyleName))) + //child = (Element) childNode; + bContinue = true; + } + } while(bContinue); + childNode = remember; + + ldp.append("$"); + } + else { + handleTextSpan(child,ldp,oc); + } + } + else if (child.getNodeName().startsWith("draw:")) { + palette.getDrawCv().handleDrawElement(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_S)) { + if (config.ignoreDoubleSpaces()) { + ldp.append(" "); + } + else { + int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1); + //String sSpace = config.ignoreDoubleSpaces() ? " " : "\\ "; + for ( ; count > 0; count--) { ldp.append("\\ "); } + } + } + else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis + // tab stops are not supported by the converter, but the special usage + // of tab stops in header and footer can be emulated with \hfill + // TODO: Sometimes extra \hfill should be added at end of line + if (oc.isInHeaderFooter()) { ldp.append("\\hfill "); } + else { ldp.append(sTabstop); } + } + else if (sName.equals(XMLString.TEXT_LINE_BREAK)) { + if (!oc.isInHeaderFooter() && !config.ignoreHardLineBreaks()) { + ldp.append("\\newline").nl(); + } + else { ldp.append(" "); } + } + else if (sName.equals(XMLString.TEXT_A)) { + palette.getFieldCv().handleAnchor(child,ldp,oc); + } + else if (sName.equals(XMLString.OFFICE_ANNOTATION)) { + handleOfficeAnnotation(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_PAGE_NUMBER)) { + palette.getFieldCv().handlePageNumber(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_PAGE_COUNT)) { + palette.getFieldCv().handlePageCount(child,ldp,oc); + } + else if (oc.isInHeaderFooter()) { + if (sName.equals(XMLString.TEXT_CHAPTER)) { + handleChapterField(child,ldp,oc); + } + else if (sName.startsWith("text:")) { + traverseInlineText(child,ldp,oc); + } + } + else { + // These tags are ignored in header and footer + if (sName.equals(XMLString.TEXT_FOOTNOTE)) { + palette.getNoteCv().handleFootnote(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_ENDNOTE)) { + palette.getNoteCv().handleEndnote(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_NOTE)) { + if ("endnote".equals(child.getAttribute(XMLString.TEXT_NOTE_CLASS))) { + palette.getNoteCv().handleEndnote(child,ldp,oc); + } + else { + palette.getNoteCv().handleFootnote(child,ldp,oc); + } + } + else if (sName.equals(XMLString.TEXT_SEQUENCE)) { + palette.getFieldCv().handleSequence(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_SEQUENCE_REF)) { + palette.getFieldCv().handleSequenceRef(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_FOOTNOTE_REF)) { + palette.getNoteCv().handleFootnoteRef(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_ENDNOTE_REF)) { + palette.getNoteCv().handleEndnoteRef(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_NOTE_REF)) { // oasis + palette.getNoteCv().handleNoteRef(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK)) { + palette.getFieldCv().handleReferenceMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK_START)) { + palette.getFieldCv().handleReferenceMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_REF)) { + palette.getFieldCv().handleReferenceRef(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK)) { + palette.getFieldCv().handleBookmark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK_START)) { + palette.getFieldCv().handleBookmark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK_REF)) { + palette.getFieldCv().handleBookmarkRef(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_BIBLIOGRAPHY_MARK)) { + palette.getBibCv().handleBibliographyMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK)) { + palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK_START)) { + palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc); + } + else if (sName.startsWith("text:")) { + traverseInlineText(child,ldp,oc); + } + } + break; + default: + // Do nothing + } + + childNode = childNode.getNextSibling(); + } + + } + + /* traverse inline text, ignoring any draw objects, footnotes, formatting and hyperlinks */ + public void traversePlainInlineText(Element node,LaTeXDocumentPortion ldp, Context oc) { + String styleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + + // Always push the font used + palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getTextStyle(styleName))); + + Node childNode = node.getFirstChild(); + while (childNode!=null) { + short nodeType = childNode.getNodeType(); + + switch (nodeType) { + case Node.TEXT_NODE: + String s = childNode.getNodeValue(); + if (s.length() > 0) { + // Need to protect ] + for (int j=0; j<s.length(); j++) { + if (s.charAt(j)!=']') { + ldp.append(palette.getI18n().convert(Character.toString(s.charAt(j)),false,oc.getLang())); + } + else { + ldp.append("{]}"); + } + } + } + break; + + case Node.ELEMENT_NODE: + Element child = (Element)childNode; + String sName = child.getTagName(); + if (sName.equals(XMLString.TEXT_SPAN)) { + traversePlainInlineText(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_S)) { + int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1); + for ( ; count > 0; count--) { + ldp.append("\\ "); + } + } + else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis + // tab stops are not supported by the converter + ldp.append(sTabstop); + } + else if (OfficeReader.isNoteElement(child)) { + // ignore + } + else if (OfficeReader.isTextElement(child)) { + traversePlainInlineText(child,ldp,oc); + } + break; + default: + // Do nothing + } + childNode = childNode.getNextSibling(); + } + // finally pop the special table + palette.getI18n().popSpecialTable(); + } + + /* traverse inline math, ignoring any draw objects, footnotes, formatting and hyperlinks */ + public void traverseInlineMath(Element node,LaTeXDocumentPortion ldp, Context oc) { + String styleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + + // Always push the font used + palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getTextStyle(styleName))); + + Node childNode = node.getFirstChild(); + while (childNode!=null) { + short nodeType = childNode.getNodeType(); + + switch (nodeType) { + case Node.TEXT_NODE: + String s = childNode.getNodeValue(); + ldp.append(palette.getI18n().convert(s,true,oc.getLang())); + break; + + case Node.ELEMENT_NODE: + Element child = (Element)childNode; + String sName = child.getTagName(); + if (sName.equals(XMLString.TEXT_S)) { + int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1); + for ( ; count > 0; count--) { + ldp.append("\\ "); + } + } + else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis + // tab stops are not supported by the converter + ldp.append(" "); + } + else if (OfficeReader.isNoteElement(child)) { + // ignore + } + else if (OfficeReader.isTextElement(child)) { + traversePlainInlineText(child,ldp,oc); + } + break; + default: + // Do nothing + } + childNode = childNode.getNextSibling(); + } + // finally pop the special table + palette.getI18n().popSpecialTable(); + } + + /* traverse verbatim inline text, ignoring any draw objects, footnotes, formatting and hyperlinks */ + private void traverseVerbatimInlineText(Element node,LaTeXDocumentPortion ldp, Context oc) { + if (node.hasChildNodes()) { + + NodeList nList = node.getChildNodes(); + int len = nList.getLength(); + + for (int i = 0; i < len; i++) { + + Node childNode = nList.item(i); + short nodeType = childNode.getNodeType(); + + switch (nodeType) { + case Node.TEXT_NODE: + String s = childNode.getNodeValue(); + if (s.length() > 0) { + // text is copied verbatim! (Will be replaced by + // question marks if outside inputenc) + ldp.append(s); + } + break; + + case Node.ELEMENT_NODE: + Element child = (Element)childNode; + String sName = child.getTagName(); + if (sName.equals(XMLString.TEXT_S)) { + int count= Misc.getPosInteger(child.getAttribute(XMLString.TEXT_C),1); + for ( ; count > 0; count--) { + ldp.append(" "); + } + } + else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis + // tab stops are not supported by the onverter + ldp.append(sTabstop); + } + else if (sName.equals(XMLString.TEXT_LINE_BREAK)) { + if (!oc.isNoLineBreaks()) { ldp.nl(); } + } + else if (sName.equals(XMLString.TEXT_NOTE)) { + // oasis; ignore + } + else if (sName.equals(XMLString.TEXT_FOOTNOTE)) { + // ignore + } + else if (sName.equals(XMLString.TEXT_ENDNOTE)) { + // ignore + } + // The respective handlers know how to postpone these marks in verbatim context: + else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK)) { + palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK_START)) { + palette.getIndexCv().handleAlphabeticalIndexMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK)) { + palette.getFieldCv().handleReferenceMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK_START)) { + palette.getFieldCv().handleReferenceMark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK)) { + palette.getFieldCv().handleBookmark(child,ldp,oc); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK_START)) { + palette.getFieldCv().handleBookmark(child,ldp,oc); + } + + else if (sName.startsWith("text:")) { + traverseVerbatimInlineText(child,ldp,oc); + } + break; + default: + // Do nothing + } + } + } + } + + public void traversePCDATA(Element node, LaTeXDocumentPortion ldp, Context oc) { + if (node.hasChildNodes()) { + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i=0; i<nLen; i++) { + if (nl.item(i).getNodeType()==Node.TEXT_NODE) { + ldp.append(palette.getI18n().convert(nl.item(i).getNodeValue(),false,oc.getLang())); + } + } + } + } + + private void handleChapterField(Element node, LaTeXDocumentPortion ldp, Context oc) { + HeadingMap hm = config.getHeadingMap(); + int nLevel = Misc.getPosInteger(node.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1); + if (nLevel<=hm.getMaxLevel()) { + int nLaTeXLevel = hm.getLevel(nLevel); + if (nLaTeXLevel==1) { + palette.getPageSc().setChapterField1(node.getAttribute(XMLString.TEXT_DISPLAY)); + ldp.append("{\\leftmark}"); + } + else if (nLaTeXLevel==2) { + palette.getPageSc().setChapterField2(node.getAttribute(XMLString.TEXT_DISPLAY)); + ldp.append("{\\rightmark}"); + } + } + } + + //////////////////////////////////////////////////////////////////// + // Annotations + + private void handleOfficeAnnotation(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sCommand = null; + switch (config.notes()) { + case LaTeXConfig.IGNORE: return; + case LaTeXConfig.COMMENT: + // Get the unformatted text of all paragraphs and insert each paragraph as a single comment + ldp.append("%").nl(); + Node child = node.getFirstChild(); + while (child!=null) { + if (Misc.isElement(child, XMLString.TEXT_P)) { + ldp.append("%"); + traversePlainInlineText((Element)child, ldp, oc); + ldp.nl(); + } + child = child.getNextSibling(); + } + return; + case LaTeXConfig.PDFANNOTATION: + bHasPdfannotation = true; + sCommand = "\\pdfannotation"; + break; + case LaTeXConfig.MARGINPAR: + sCommand = "\\marginpar"; + break; + case LaTeXConfig.CUSTOM: + sCommand = config.getNotesCommand(); + break; + } + + // Get the unformatted text of all paragraphs, separated by spaces + ldp.append(sCommand).append("{"); + boolean bFirst = true; + Node child = node.getFirstChild(); + while (child!=null) { + if (Misc.isElement(child, XMLString.TEXT_P)) { + if (!bFirst) ldp.append(" "); + traversePlainInlineText((Element)child, ldp, oc); + bFirst = false; + } + child = child.getNextSibling(); + + } + ldp.append("}"); + } + + /* Check to see if this node has a footnote or endnote as the only subnode */ + private boolean onlyNote(Node node) { + if (!node.hasChildNodes()) { return false; } + NodeList nList = node.getChildNodes(); + int nLen = nList.getLength(); + + for (int i = 0; i < nLen; i++) { + + Node child = nList.item(i); + short nType = child.getNodeType(); + + switch (nType) { + case Node.TEXT_NODE: return false; + case Node.ELEMENT_NODE: + if (!OfficeReader.isNoteElement(child)) { return false; } + } + } + return true; + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/LaTeXConfig.java b/source/java/writer2latex/latex/LaTeXConfig.java new file mode 100644 index 0000000..30a5739 --- /dev/null +++ b/source/java/writer2latex/latex/LaTeXConfig.java @@ -0,0 +1,543 @@ +/************************************************************************ + * + * LaTeXConfig.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-17) + * + */ + +package writer2latex.latex; + +import java.util.LinkedList; +import java.util.Hashtable; +import java.util.Enumeration; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.Element; + +import writer2latex.base.BooleanOption; +import writer2latex.base.IntegerOption; +import writer2latex.base.Option; +import writer2latex.latex.util.HeadingMap; +import writer2latex.latex.i18n.ClassicI18n; +import writer2latex.latex.i18n.ReplacementTrie; +import writer2latex.latex.util.StyleMap; +import writer2latex.util.Misc; + +public class LaTeXConfig extends writer2latex.base.ConfigBase { + protected int getOptionCount() { return 59; } + protected String getDefaultConfigPath() { return "/writer2latex/latex/config/"; } + + // Override setOption to be backwards compatible + public void setOption(String sName,String sValue) { + // this option has been renamed: + if (sName.equals("keep_image_size")) { sName = "original_image_size"; } + super.setOption(sName, sValue); + } + + // Backend + public static final int GENERIC = 0; + public static final int DVIPS = 1; + public static final int PDFTEX = 2; + public static final int UNSPECIFIED = 3; + public static final int XETEX = 4; + + // Formatting (must be ordered) + public static final int IGNORE_ALL = 0; + public static final int IGNORE_MOST = 1; + public static final int CONVERT_BASIC = 2; + public static final int CONVERT_MOST = 3; + public static final int CONVERT_ALL = 4; + // Page formatting + public static final int CONVERT_HEADER_FOOTER = 5; + // Handling of other formatting + public static final int IGNORE = 0; + public static final int ACCEPT = 1; + public static final int WARNING = 2; + public static final int ERROR = 3; + + // Notes + //public static final int IGNORE = 0; + public static final int COMMENT = 1; + public static final int PDFANNOTATION = 2; + public static final int MARGINPAR = 3; + public static final int CUSTOM = 4; + + // Options + protected int OPTION_COUNT = 59; + + private static final int BACKEND = 0; + private static final int NO_PREAMBLE = 1; + private static final int NO_INDEX = 2; + private static final int DOCUMENTCLASS = 3; + private static final int GLOBAL_OPTIONS = 4; + private static final int INPUTENCODING = 5; + private static final int MULTILINGUAL = 6; + private static final int GREEK_MATH = 7; + private static final int USE_OOOMATH = 8; + private static final int USE_PIFONT = 9; + private static final int USE_IFSYM = 10; + private static final int USE_WASYSYM = 11; + private static final int USE_BBDING = 12; + private static final int USE_EUROSYM = 13; + private static final int USE_TIPA = 14; + private static final int USE_COLOR = 15; + private static final int USE_COLORTBL = 16; + private static final int USE_GEOMETRY = 17; + private static final int USE_FANCYHDR = 18; + private static final int USE_HYPERREF = 19; + private static final int USE_CAPTION = 20; + private static final int USE_LONGTABLE = 21; + private static final int USE_SUPERTABULAR = 22; + private static final int USE_TABULARY = 23; + private static final int USE_ENDNOTES = 24; + private static final int USE_ULEM = 25; + private static final int USE_LASTPAGE = 26; + private static final int USE_TITLEREF = 27; + private static final int USE_OOOREF = 28; + private static final int USE_BIBTEX = 29; + private static final int BIBTEX_STYLE = 30; + private static final int EXTERNAL_BIBTEX_FILES = 31; + private static final int FORMATTING = 32; + private static final int PAGE_FORMATTING = 33; + private static final int OTHER_STYLES = 34; + private static final int IMAGE_CONTENT = 35; + private static final int TABLE_CONTENT = 36; + private static final int IGNORE_HARD_PAGE_BREAKS = 37; + private static final int IGNORE_HARD_LINE_BREAKS = 38; + private static final int IGNORE_EMPTY_PARAGRAPHS = 39; + private static final int IGNORE_DOUBLE_SPACES = 40; + private static final int ALIGN_FRAMES = 41; + private static final int FLOAT_FIGURES = 42; + private static final int FLOAT_TABLES = 43; + private static final int FLOAT_OPTIONS = 44; + private static final int FIGURE_SEQUENCE_NAME = 45; + private static final int TABLE_SEQUENCE_NAME = 46; + private static final int IMAGE_OPTIONS = 47; + private static final int REMOVE_GRAPHICS_EXTENSION = 48; + private static final int ORIGINAL_IMAGE_SIZE = 49; + private static final int SIMPLE_TABLE_LIMIT = 50; + private static final int NOTES = 51; + private static final int METADATA = 52; + private static final int TABSTOP = 53; + private static final int WRAP_LINES_AFTER = 54; + private static final int SPLIT_LINKED_SECTIONS = 55; + private static final int SPLIT_TOPLEVEL_SECTIONS = 56; + private static final int SAVE_IMAGES_IN_SUBDIR = 57; + private static final int DEBUG = 58; + + protected LinkedList customPreamble = new LinkedList(); + protected StyleMap par = new StyleMap(); + protected StyleMap parBlock = new StyleMap(); + protected StyleMap text = new StyleMap(); + protected StyleMap list = new StyleMap(); + protected StyleMap listItem = new StyleMap(); + protected StyleMap textAttr = new StyleMap(); + protected HeadingMap headingMap = new HeadingMap(5); + protected Hashtable mathSymbols = new Hashtable(); + protected ReplacementTrie stringReplace = new ReplacementTrie(); + + public LaTeXConfig() { + super(); + // create options with default values + options[NO_PREAMBLE] = new BooleanOption("no_preamble","false"); + options[NO_INDEX] = new BooleanOption("no_index","false"); + options[DOCUMENTCLASS] = new Option("documentclass","article"); + options[GLOBAL_OPTIONS] = new Option("global_options",""); + options[BACKEND] = new IntegerOption("backend","pdftex") { + public void setString(String sValue) { + super.setString(sValue); + if ("generic".equals(sValue)) nValue = GENERIC; + else if ("dvips".equals(sValue)) nValue = DVIPS; + else if ("pdftex".equals(sValue)) nValue = PDFTEX; + else if ("unspecified".equals(sValue)) nValue = UNSPECIFIED; + else if ("xetex".equals(sValue)) nValue = XETEX; + } + }; + options[INPUTENCODING] = new IntegerOption("inputencoding",ClassicI18n.writeInputenc(ClassicI18n.ASCII)) { + public void setString(String sValue) { + super.setString(sValue); + nValue = ClassicI18n.readInputenc(sValue); + } + }; + options[MULTILINGUAL] = new BooleanOption("multilingual","true"); + options[GREEK_MATH] = new BooleanOption("greek_math","true"); + options[USE_OOOMATH] = new BooleanOption("use_ooomath","false"); + options[USE_PIFONT] = new BooleanOption("use_pifont","false"); + options[USE_IFSYM] = new BooleanOption("use_ifsym","false"); + options[USE_WASYSYM] = new BooleanOption("use_wasysym","false"); + options[USE_BBDING] = new BooleanOption("use_bbding","false"); + options[USE_EUROSYM] = new BooleanOption("use_eurosym","false"); + options[USE_TIPA] = new BooleanOption("use_tipa","false"); + options[USE_COLOR] = new BooleanOption("use_color","true"); + options[USE_COLORTBL] = new BooleanOption("use_colortbl","false"); + options[USE_GEOMETRY] = new BooleanOption("use_geometry","false"); + options[USE_FANCYHDR] = new BooleanOption("use_fancyhdr","false"); + options[USE_HYPERREF] = new BooleanOption("use_hyperref","true"); + options[USE_CAPTION] = new BooleanOption("use_caption","false"); + options[USE_LONGTABLE] = new BooleanOption("use_longtable","false"); + options[USE_SUPERTABULAR] = new BooleanOption("use_supertabular","true"); + options[USE_TABULARY] = new BooleanOption("use_tabulary","false"); + options[USE_ENDNOTES] = new BooleanOption("use_endnotes","false"); + options[USE_ULEM] = new BooleanOption("use_ulem","false"); + options[USE_LASTPAGE] = new BooleanOption("use_lastpage","false"); + options[USE_TITLEREF] = new BooleanOption("use_titleref","false"); + options[USE_OOOREF] = new BooleanOption("use_oooref","false"); + options[USE_BIBTEX] = new BooleanOption("use_bibtex","false"); + options[BIBTEX_STYLE] = new Option("bibtex_style","plain"); + options[EXTERNAL_BIBTEX_FILES] = new Option("external_bibtex_files",""); + options[FORMATTING] = new IntegerOption("formatting","convert_basic") { + public void setString(String sValue) { + super.setString(sValue); + if ("convert_all".equals(sValue)) nValue = CONVERT_ALL; + else if ("convert_most".equals(sValue)) nValue = CONVERT_MOST; + else if ("convert_basic".equals(sValue)) nValue = CONVERT_BASIC; + else if ("ignore_most".equals(sValue)) nValue = IGNORE_MOST; + else if ("ignore_all".equals(sValue)) nValue = IGNORE_ALL; + } + }; + options[PAGE_FORMATTING] = new IntegerOption("page_formatting","convert_all") { + public void setString(String sValue) { + super.setString(sValue); + if ("convert_all".equals(sValue)) nValue = CONVERT_ALL; + else if ("convert_header_footer".equals(sValue)) nValue = CONVERT_HEADER_FOOTER; + else if ("ignore_all".equals(sValue)) nValue = IGNORE_ALL; + } + }; + options[OTHER_STYLES] = new ContentHandlingOption("other_styles","accept"); + options[IMAGE_CONTENT] = new ContentHandlingOption("image_content","accept"); + options[TABLE_CONTENT] = new ContentHandlingOption("table_content","accept"); + options[IGNORE_HARD_PAGE_BREAKS] = new BooleanOption("ignore_hard_page_breaks","false"); + options[IGNORE_HARD_LINE_BREAKS] = new BooleanOption("ignore_hard_line_breaks","false"); + options[IGNORE_EMPTY_PARAGRAPHS] = new BooleanOption("ignore_empty_paragraphs","false"); + options[IGNORE_DOUBLE_SPACES] = new BooleanOption("ignore_double_spaces","false"); + options[ALIGN_FRAMES] = new BooleanOption("align_frames","true"); + options[FLOAT_FIGURES] = new BooleanOption("float_figures","false"); + options[FLOAT_TABLES] = new BooleanOption("float_tables","false"); + options[FLOAT_OPTIONS] = new Option("float_options","h"); + options[FIGURE_SEQUENCE_NAME] = new BooleanOption("figure_sequence_name",""); + options[TABLE_SEQUENCE_NAME] = new BooleanOption("table_sequence_name",""); + options[IMAGE_OPTIONS] = new Option("image_options",""); + options[REMOVE_GRAPHICS_EXTENSION] = new BooleanOption("remove_graphics_extension","false"); + options[ORIGINAL_IMAGE_SIZE] = new BooleanOption("original_image_size","false"); + options[SIMPLE_TABLE_LIMIT] = new IntegerOption("simple_table_limit","0") { + public void setString(String sValue) { + super.setString(sValue); + nValue = Misc.getPosInteger(sValue,0); + } + }; + options[NOTES] = new IntegerOption("notes","comment") { + public void setString(String sValue) { + super.setString(sValue); + if ("ignore".equals(sValue)) nValue = IGNORE; + else if ("comment".equals(sValue)) nValue = COMMENT; + else if ("pdfannotation".equals(sValue)) nValue = PDFANNOTATION; + else if ("marginpar".equals(sValue)) nValue = MARGINPAR; + else nValue = CUSTOM; + } + }; + options[METADATA] = new BooleanOption("metadata","true"); + options[TABSTOP] = new Option("tabstop",""); + options[WRAP_LINES_AFTER] = new IntegerOption("wrap_lines_after","72") { + public void setString(String sValue) { + super.setString(sValue); + nValue = Misc.getPosInteger(sValue,0); + } + }; + options[SPLIT_LINKED_SECTIONS] = new BooleanOption("split_linked_sections","false"); + options[SPLIT_TOPLEVEL_SECTIONS] = new BooleanOption("split_toplevel_sections","false"); + options[SAVE_IMAGES_IN_SUBDIR] = new BooleanOption("save_images_in_subdir","false"); + options[DEBUG] = new BooleanOption("debug","false"); + // Headings for article class: + headingMap.setLevelData(1,"section",1); + headingMap.setLevelData(2,"subsection",2); + headingMap.setLevelData(3,"subsubsection",3); + headingMap.setLevelData(4,"paragraph",4); + headingMap.setLevelData(5,"subparagraph",5); + // Standard string replace: + // Fix french spacing; replace nonbreaking space + // right before em-dash, !, ?, : and ; (babel handles this) + stringReplace.put("\u00A0\u2014"," \u2014",ClassicI18n.readFontencs("any")); + stringReplace.put("\u00A0!"," !",ClassicI18n.readFontencs("any")); + stringReplace.put("\u00A0?"," ?",ClassicI18n.readFontencs("any")); + stringReplace.put("\u00A0:"," :",ClassicI18n.readFontencs("any")); + stringReplace.put("\u00A0;"," ;",ClassicI18n.readFontencs("any")); + // Right after opening guillemet and right before closing guillemet: + // Here we must *keep* the non-breaking space + // TODO: Use \og and \fg if the document contains french... + //stringReplace.put("\u00AB\u00A0","\u00AB ",I18n.readFontencs("any")); + //stringReplace.put("\u00A0\u00BB"," \u00BB",I18n.readFontencs("any")); + } + + protected void readInner(Element elm) { + if (elm.getTagName().equals("style-map")) { + String sName = elm.getAttribute("name"); + String sFamily = elm.getAttribute("family"); + if (sFamily.length()==0) { // try old name + sFamily = elm.getAttribute("class"); + } + String sBefore = elm.getAttribute("before"); + String sAfter = elm.getAttribute("after"); + boolean bLineBreak = !"false".equals(elm.getAttribute("line-break")); + boolean bVerbatim = "true".equals(elm.getAttribute("verbatim")); + if ("paragraph".equals(sFamily)) { + par.put(sName,sBefore,sAfter,bLineBreak,bVerbatim); + } + if ("paragraph-block".equals(sFamily)) { + String sNext = elm.getAttribute("next"); + parBlock.put(sName,sBefore,sAfter,sNext,bVerbatim); + } + else if ("text".equals(sFamily)) { + text.put(sName,sBefore,sAfter,false,bVerbatim); + } + else if ("list".equals(sFamily)) { + list.put(sName,sBefore,sAfter); + } + else if ("listitem".equals(sFamily)) { + listItem.put(sName,sBefore,sAfter); + } + else if ("text-attribute".equals(sFamily)) { + textAttr.put(sName, sBefore, sAfter); + } + } + else if (elm.getTagName().equals("heading-map")) { + readHeadingMap(elm); + } + else if (elm.getTagName().equals("string-replace")) { + String sInput = elm.getAttribute("input"); + String sLaTeXCode = elm.getAttribute("latex-code"); + String sFontencs = elm.getAttribute("fontencs"); + int nFontencs = ClassicI18n.readFontencs(sFontencs.length()>0 ? sFontencs : "any"); + stringReplace.put(sInput,sLaTeXCode,nFontencs); + } + else if (elm.getTagName().equals("custom-preamble")) { + Node child = elm.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.TEXT_NODE) { + customPreamble.add(child.getNodeValue()); + } + child = child.getNextSibling(); + } + } + else if (elm.getTagName().equals("math-symbol-map")) { + String sName = elm.getAttribute("name"); + String sLatex = elm.getAttribute("latex"); + mathSymbols.put(sName,sLatex); + } + } + + public void readHeadingMap(Element node) { + int nMaxLevel = Misc.getPosInteger(node.getAttribute("max-level"),0); + headingMap.reset(nMaxLevel); + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + Element elm = (Element) child; + if (elm.getTagName().equals("heading-level-map")) { + int nWriterLevel = Misc.getPosInteger(elm.getAttribute("writer-level"),1); + String sName = elm.getAttribute("name"); + int nLevel = Misc.getPosInteger(elm.getAttribute("level"),0); + headingMap.setLevelData(nWriterLevel,sName,nLevel); + } + } + child = child.getNextSibling(); + } + } + + protected void writeInner(Document dom) { + // Write math symbol map + Enumeration msEnum = mathSymbols.keys(); + while (msEnum.hasMoreElements()) { + String sName = (String) msEnum.nextElement(); + String sLatex = (String) mathSymbols.get(sName); + Element msNode = dom.createElement("math-symbol-map"); + msNode.setAttribute("name",sName); + msNode.setAttribute("latex",sLatex); + dom.getDocumentElement().appendChild(msNode); + } + + writeStyleMap(dom,par,"paragraph"); + writeStyleMap(dom,parBlock,"paragraph-block"); + writeStyleMap(dom,text,"text"); + writeStyleMap(dom,list,"list"); + writeStyleMap(dom,listItem,"listitem"); + writeStyleMap(dom,textAttr,"text-attribute"); + + Element hmNode = dom.createElement("heading-map"); + hmNode.setAttribute("max-level",Integer.toString(headingMap.getMaxLevel())); + dom.getDocumentElement().appendChild(hmNode); + for (int i=1; i<=headingMap.getMaxLevel(); i++) { + Element hlmNode = dom.createElement("heading-level-map"); + hlmNode.setAttribute("writer-level",Integer.toString(i)); + hlmNode.setAttribute("name",headingMap.getName(i)); + hlmNode.setAttribute("level",Integer.toString(headingMap.getLevel(i))); + hmNode.appendChild(hlmNode); + } + + // TODO: Export string replacements + //String[] sInputStrings = stringReplace.getInputStrings(); + /* + int nSize = sInputStrings.size(); + for (int i=0; i<nSize; i++) { + String sInput = sInputStrings[i]; + ReplacementTrieNode node = stringReplace.get(sInput); + Element srNode = dom.createElement("string-replace"); + srNode.setAttribute("input",sInput); + srNode.setAttribute("latex-code",node.getLaTeXCode()); + srNode.setAttribute("fontenc",I18n.writeFontencs(node.getFontencs())); + hmNode.appendChild(srNode); + } + */ + + writeContent(dom,customPreamble,"custom-preamble"); + + } + + private void writeStyleMap(Document dom, StyleMap sm, String sFamily) { + Enumeration smEnum = sm.getNames(); + while (smEnum.hasMoreElements()) { + String sName = (String) smEnum.nextElement(); + Element smNode = dom.createElement("style-map"); + smNode.setAttribute("name",sName); + smNode.setAttribute("family",sFamily); + smNode.setAttribute("before",sm.getBefore(sName)); + smNode.setAttribute("after",sm.getAfter(sName)); + if (sm.getNext(sName)!=null) { + smNode.setAttribute("next",sm.getNext(sName)); + } + if (!sm.getLineBreak(sName)) { + smNode.setAttribute("line-break","false"); + } + if (sm.getVerbatim(sName)) { + smNode.setAttribute("verbatim","true"); + } + dom.getDocumentElement().appendChild(smNode); + } + } + + private void writeContent(Document dom, LinkedList list, String sElement) { + Element node = dom.createElement(sElement); + int nLen = list.size(); + for (int i=0; i<nLen; i++) { + node.appendChild( dom.createTextNode( (String) list.get(i) ) ); + } + dom.getDocumentElement().appendChild(node); + } + + // Convenience accessor methods + + // String replace + public ReplacementTrie getStringReplace() { return stringReplace; } + + // Common options + public boolean debug() { return ((BooleanOption) options[DEBUG]).getValue(); } + + // General options + public String getDocumentclass() { return options[DOCUMENTCLASS].getString(); } + public String getGlobalOptions() { return options[GLOBAL_OPTIONS].getString(); } + public int getBackend() { return ((IntegerOption) options[BACKEND]).getValue(); } + public int getInputencoding() { return ((IntegerOption) options[INPUTENCODING]).getValue(); } + public boolean multilingual() { return ((BooleanOption) options[MULTILINGUAL]).getValue(); } + public boolean greekMath() { return ((BooleanOption) options[GREEK_MATH]).getValue(); } + public boolean noPreamble() { return ((BooleanOption) options[NO_PREAMBLE]).getValue(); } + public boolean noIndex() { return ((BooleanOption) options[NO_INDEX]).getValue(); } + + // Package options + public boolean useOoomath() { return ((BooleanOption) options[USE_OOOMATH]).getValue(); } + public boolean usePifont() { return ((BooleanOption) options[USE_PIFONT]).getValue(); } + public boolean useIfsym() { return ((BooleanOption) options[USE_IFSYM]).getValue(); } + public boolean useWasysym() { return ((BooleanOption) options[USE_WASYSYM]).getValue(); } + public boolean useBbding() { return ((BooleanOption) options[USE_BBDING]).getValue(); } + public boolean useEurosym() { return ((BooleanOption) options[USE_EUROSYM]).getValue(); } + public boolean useTipa() { return ((BooleanOption) options[USE_TIPA]).getValue(); } + public boolean useColor() { return ((BooleanOption) options[USE_COLOR]).getValue(); } + public boolean useColortbl() { return ((BooleanOption) options[USE_COLORTBL]).getValue(); } + public boolean useGeometry() { return ((BooleanOption) options[USE_GEOMETRY]).getValue(); } + public boolean useFancyhdr() { return ((BooleanOption) options[USE_FANCYHDR]).getValue(); } + public boolean useHyperref() { return ((BooleanOption) options[USE_HYPERREF]).getValue(); } + public boolean useCaption() { return ((BooleanOption) options[USE_CAPTION]).getValue(); } + public boolean useLongtable() { return ((BooleanOption) options[USE_LONGTABLE]).getValue(); } + public boolean useSupertabular() { return ((BooleanOption) options[USE_SUPERTABULAR]).getValue(); } + public boolean useTabulary() { return ((BooleanOption) options[USE_TABULARY]).getValue(); } + public boolean useEndnotes() { return ((BooleanOption) options[USE_ENDNOTES]).getValue(); } + public boolean useUlem() { return ((BooleanOption) options[USE_ULEM]).getValue(); } + public boolean useLastpage() { return ((BooleanOption) options[USE_LASTPAGE]).getValue(); } + public boolean useTitleref() { return ((BooleanOption) options[USE_TITLEREF]).getValue(); } + public boolean useOooref() { return ((BooleanOption) options[USE_OOOREF]).getValue(); } + public boolean useBibtex() { return ((BooleanOption) options[USE_BIBTEX]).getValue(); } + public String bibtexStyle() { return options[BIBTEX_STYLE].getString(); } + public String externalBibtexFiles() { return options[EXTERNAL_BIBTEX_FILES].getString(); } + + // Formatting options + public int formatting() { return ((IntegerOption) options[FORMATTING]).getValue(); } + public int pageFormatting() { return ((IntegerOption) options[PAGE_FORMATTING]).getValue(); } + public int otherStyles() { return ((IntegerOption) options[OTHER_STYLES]).getValue(); } + public int imageContent() { return ((IntegerOption) options[IMAGE_CONTENT]).getValue(); } + public int tableContent() { return ((IntegerOption) options[TABLE_CONTENT]).getValue(); } + public boolean ignoreHardPageBreaks() { return ((BooleanOption) options[IGNORE_HARD_PAGE_BREAKS]).getValue(); } + public boolean ignoreHardLineBreaks() { return ((BooleanOption) options[IGNORE_HARD_LINE_BREAKS]).getValue(); } + public boolean ignoreEmptyParagraphs() { return ((BooleanOption) options[IGNORE_EMPTY_PARAGRAPHS]).getValue(); } + public boolean ignoreDoubleSpaces() { return ((BooleanOption) options[IGNORE_DOUBLE_SPACES]).getValue(); } + + // Graphics options + public boolean alignFrames() { return ((BooleanOption) options[ALIGN_FRAMES]).getValue(); } + public boolean floatFigures() { return ((BooleanOption) options[FLOAT_FIGURES]).getValue(); } + public boolean floatTables() { return ((BooleanOption) options[FLOAT_TABLES]).getValue(); } + public String getFloatOptions() { return options[FLOAT_OPTIONS].getString(); } + public String getFigureSequenceName() { return options[FIGURE_SEQUENCE_NAME].getString(); } + public String getTableSequenceName() { return options[TABLE_SEQUENCE_NAME].getString(); } + public String getImageOptions() { return options[IMAGE_OPTIONS].getString(); } + public boolean removeGraphicsExtension() { return ((BooleanOption) options[REMOVE_GRAPHICS_EXTENSION]).getValue(); } + public boolean originalImageSize() { return ((BooleanOption) options[ORIGINAL_IMAGE_SIZE]).getValue(); } + + // Tables + public int getSimpleTableLimit() { return ((IntegerOption) options[SIMPLE_TABLE_LIMIT]).getValue(); } + + // Notes + public int notes() { return ((IntegerOption) options[NOTES]).getValue(); } + public String getNotesCommand() { return options[NOTES].getString(); } + + // Metadata + public boolean metadata() { return ((BooleanOption) options[METADATA]).getValue(); } + + // Tab stops + public String getTabstop() { return options[TABSTOP].getString(); } + + // Files + public int getWrapLinesAfter() { return ((IntegerOption) options[WRAP_LINES_AFTER]).getValue(); } + public boolean splitLinkedSections() { return ((BooleanOption) options[SPLIT_LINKED_SECTIONS]).getValue(); } + public boolean splitToplevelSections() { return ((BooleanOption) options[SPLIT_TOPLEVEL_SECTIONS]).getValue(); } + public boolean saveImagesInSubdir() { return ((BooleanOption) options[SAVE_IMAGES_IN_SUBDIR]).getValue(); } + + public Hashtable getMathSymbols() { return mathSymbols; } + + public StyleMap getParStyleMap() { return par; } + public StyleMap getParBlockStyleMap() { return parBlock; } + public StyleMap getTextStyleMap() { return text; } + public StyleMap getListStyleMap() { return list; } + public StyleMap getListItemStyleMap() { return listItem; } + public StyleMap getTextAttributeStyleMap() { return textAttr; } + public HeadingMap getHeadingMap() { return headingMap; } + public LinkedList getCustomPreamble() { return customPreamble; } + +} + diff --git a/source/java/writer2latex/latex/LaTeXDocument.java b/source/java/writer2latex/latex/LaTeXDocument.java new file mode 100644 index 0000000..9ea1804 --- /dev/null +++ b/source/java/writer2latex/latex/LaTeXDocument.java @@ -0,0 +1,152 @@ +/************************************************************************ + * + * LaTeXDocument.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import writer2latex.xmerge.Document; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +/** + * <p>Class representing a LaTeX document.</p> + * + */ +public class LaTeXDocument implements Document { + private static final String FILE_EXTENSION = ".tex"; + + private String sName; + + private String sEncoding = "ASCII"; + + private int nWrap; + + private LaTeXDocumentPortion contents; + + /** + * <p>Constructs a new LaTeX Document.</p> + * + * <p>This new document is empty. Document data must added to the preamble and + * the body using appropriate methods.</p> + * + * @param sName The name of the <code>LaTeXDocument</code>. + * @param nWrap Lines should be wrapped after this position + */ + public LaTeXDocument(String sName,int nWrap) { + this.nWrap = nWrap; + this.sName = trimDocumentName(sName); + contents = new LaTeXDocumentPortion(true); + } + + /** + * <p>This method is supposed to read <code>byte</code> data from the InputStream. + * Currently it does nothing, since we don't need it.</p> + * + * @param is InputStream containing a LaTeX data file. + * + * @throws IOException In case of any I/O errors. + */ + public void read(InputStream is) throws IOException { + // Do nothing. + } + + + /** + * <p>Returns the <code>Document</code> name with no file extension.</p> + * + * @return The <code>Document</code> name with no file extension. + */ + public String getName() { + return sName; + } + + + /** + * <p>Returns the <code>Document</code> name with file extension.</p> + * + * @return The <code>Document</code> name with file extension. + */ + public String getFileName() { + return new String(sName + FILE_EXTENSION); + } + + + /** + * <p>Writes out the <code>Document</code> content to the specified + * <code>OutputStream</code>.</p> + * + * <p>This method may not be thread-safe. + * Implementations may or may not synchronize this + * method. User code (i.e. caller) must make sure that + * calls to this method are thread-safe.</p> + * + * @param os <code>OutputStream</code> to write out the + * <code>Document</code> content. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + OutputStreamWriter osw = new OutputStreamWriter(os,sEncoding); + contents.write(osw,nWrap,"\n"); + osw.flush(); + osw.close(); + } + + /** + * <p> Set the output encoding to use when writing the document.</p> + */ + public void setEncoding(String sEncoding) { this.sEncoding = sEncoding; } + + /** + * <p>Returns the <code>LaTeXDocumentPortion</code>, that contains the + * contents of the document.</p> + * + * @return The content <code>LaTeXDocumentPortion</code>. + */ + public LaTeXDocumentPortion getContents(){ + return contents; + } + + /* + * Utility method to make sure the document name is stripped of any file + * extensions before use. + */ + private String trimDocumentName(String name) { + String temp = name.toLowerCase(); + + if (temp.endsWith(FILE_EXTENSION)) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - FILE_EXTENSION.length(); + name = name.substring(0,endIndex); + } + + return name; + } +} + \ No newline at end of file diff --git a/source/java/writer2latex/latex/LaTeXDocumentPortion.java b/source/java/writer2latex/latex/LaTeXDocumentPortion.java new file mode 100644 index 0000000..32d7ed0 --- /dev/null +++ b/source/java/writer2latex/latex/LaTeXDocumentPortion.java @@ -0,0 +1,214 @@ +/************************************************************************ + * + * LaTeXDocumentPortion.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2006 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2007-10-02) + * + */ + +package writer2latex.latex; + +import java.io.OutputStreamWriter; +import java.io.IOException; +import java.util.Vector; + +import writer2latex.util.Misc; + +/** This class represents a portion of a LaTeX document. A portion is any +number of lines, and may include subportions. */ +public class LaTeXDocumentPortion { + + private Vector nodes; // The collection of all nodes in this portion + + private StringBuffer curText; // The currently active node (always the last node) + private boolean bEmpty; // Is the active node empty? + + private boolean bWrap; // Do we allow line wrap in this portion? + + public LaTeXDocumentPortion(boolean bWrap){ + this.bWrap = bWrap; + nodes = new Vector(); + curText = new StringBuffer(); + bEmpty = true; + } + + /** Add another portion to the end of this portion */ + public LaTeXDocumentPortion append(LaTeXDocumentPortion ldp) { + if (!bEmpty) { + // add the current node to the node list and create new current node + nodes.add(curText); + curText = new StringBuffer(); + bEmpty = true; + } + nodes.add(ldp); + return this; + } + + /** Add a string to the end of this portion */ + public LaTeXDocumentPortion append(String s){ + curText.append(s); + bEmpty = false; // even if this is the empty string! + return this; + } + + /** Add a newline to the end of this portion */ + public LaTeXDocumentPortion nl(){ + curText.append("\n"); + bEmpty = false; + return this; + } + + /** write a segment of text (eg. a word) to the output */ + private void writeSegment(String s, int nStart, int nEnd, OutputStreamWriter osw) throws IOException { + for (int i=nStart; i<nEnd; i++) { osw.write(s.charAt(i)); } + } + + /** write the contents of a StringBuffer to the output */ + private void writeBuffer(StringBuffer text, OutputStreamWriter osw, int nLineLen, String sNewline) throws IOException { + String s = text.toString(); + int nLen = s.length(); + + int[] nBreakPoints = new int[100]; + int nLastBPIndex = 99; + + int nStart = 0; + + while (nStart<nLen) { + // identify line and breakpoints + int nBPIndex = 0; + boolean bEscape = false; + boolean bComment = false; + int nNewline = nStart; + char c; + while (nNewline<nLen) { + if (nBPIndex==nLastBPIndex) { + nBreakPoints = Misc.doubleIntArray(nBreakPoints); + nLastBPIndex = nBreakPoints.length-1; + } + c = s.charAt(nNewline); + if (c=='\n') { + nBreakPoints[nBPIndex++] = nNewline; + break; + } + if (bEscape) { bEscape = false; } + else if (c=='\\') { bEscape = true; } + else if (c=='%') { bComment = true; } + else if (!bComment && c==' ') { nBreakPoints[nBPIndex++] = nNewline; } + nNewline++; + } + if (nBPIndex==nLastBPIndex) { + nBreakPoints = Misc.doubleIntArray(nBreakPoints); + nLastBPIndex = nBreakPoints.length-1; + } + if (nNewline==nLen) { nBreakPoints[nBPIndex++] = nNewline; } + + // write out line + int nCurLineLen = nBreakPoints[0]-nStart; + writeSegment(s,nStart,nBreakPoints[0],osw); + for (int i=0; i<nBPIndex-1; i++) { + int nSegmentLen = nBreakPoints[i+1]-nBreakPoints[i]; + if (nSegmentLen+nCurLineLen>nLineLen) { + // break line before this segment + osw.write(sNewline); + nCurLineLen = nSegmentLen; + } + else { + // segment fits in current line + osw.write(" "); + nCurLineLen += nSegmentLen; + } + writeSegment(s,nBreakPoints[i]+1,nBreakPoints[i+1],osw); + } + osw.write(sNewline); + nStart = nNewline+1; + } + } + + /** write the contents of a StringBuffer to the output without wrap */ + private void writeBuffer(StringBuffer text, OutputStreamWriter osw, String sNewline) throws IOException { + String s = text.toString(); + int nLen = s.length(); + + int nStart = 0; + + while (nStart<nLen) { + // identify line + int nNewline = nStart; + while (nNewline<nLen) { + if (s.charAt(nNewline)=='\n') { break; } + nNewline++; + } + + // write out line + writeSegment(s,nStart,nNewline,osw); + osw.write(sNewline); + nStart = nNewline+1; + } + } + + /** Write this portion to the output (note: nLineLen=0 means no wrap) */ + public void write(OutputStreamWriter osw, int nLineLen, String sNewline) throws IOException { + int n = nodes.size(); + for (int i=0; i<n; i++) { + if (nodes.get(i) instanceof LaTeXDocumentPortion) { + ((LaTeXDocumentPortion) nodes.get(i)).write(osw,nLineLen,sNewline); + } + else if (bWrap && nLineLen>0) { + writeBuffer((StringBuffer) nodes.get(i),osw,nLineLen,sNewline); + } + else { + writeBuffer((StringBuffer) nodes.get(i),osw,sNewline); + } + } + if (!bEmpty) { // write current node as well + if (bWrap && nLineLen>0) { + writeBuffer(curText,osw,nLineLen,sNewline); + } + else { + writeBuffer(curText,osw,sNewline); + } + } + } + + /** Return the content of this LaTeXDocumentStream as a string */ + public String toString() { + StringBuffer buf = new StringBuffer(); + int n = nodes.size(); + for (int i=0; i<n; i++) { + if (nodes.get(i) instanceof LaTeXDocumentPortion) { + buf.append(((LaTeXDocumentPortion) nodes.get(i)).toString()); + } + else { + buf.append(((StringBuffer) nodes.get(i)).toString()); + } + } + if (!bEmpty) { // write current node as well + buf.append(curText.toString()); + } + return buf.toString(); + } + + + +} // end class LaTeXDocumentPortion + +// TO DO: consider StringBuffer->ByteArrayOutputStream (performance??) + diff --git a/source/java/writer2latex/latex/ListStyleConverter.java b/source/java/writer2latex/latex/ListStyleConverter.java new file mode 100644 index 0000000..c6dab0c --- /dev/null +++ b/source/java/writer2latex/latex/ListStyleConverter.java @@ -0,0 +1,384 @@ +/************************************************************************ + * + * ListStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.latex; + +import java.util.Hashtable; + +import writer2latex.util.*; +import writer2latex.office.*; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; + +/* This class creates LaTeX code from OOo list styles + */ +public class ListStyleConverter extends StyleConverter { + boolean bNeedSaveEnumCounter = false; + private Hashtable listStyleLevelNames = new Hashtable(); + + /** <p>Constructs a new <code>ListStyleConverter</code>.</p> + */ + public ListStyleConverter(OfficeReader ofr, LaTeXConfig config, + ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (config.formatting()>=LaTeXConfig.CONVERT_MOST || !styleNames.isEmpty()) { + decl.append("% List styles").nl(); + // May need an extra counter to handle continued numbering in lists + if (bNeedSaveEnumCounter) { + decl.append("\\newcounter{saveenum}").nl(); + } + // If we export formatting, we need some hooks from lists to paragraphs: + if (config.formatting()>=LaTeXConfig.CONVERT_MOST) { + decl.append("\\newcommand\\writerlistleftskip{}").nl() + .append("\\newcommand\\writerlistparindent{}").nl() + .append("\\newcommand\\writerlistlabel{}").nl() + .append("\\newcommand\\writerlistremovelabel{") + .append("\\aftergroup\\let\\aftergroup\\writerlistparindent\\aftergroup\\relax") + .append("\\aftergroup\\let\\aftergroup\\writerlistlabel\\aftergroup\\relax}").nl(); + } + super.appendDeclarations(pack,decl); + } + } + + /** <p>Apply a list style to an ordered or unordered list.</p> */ + public void applyListStyle(String sStyleName, int nLevel, boolean bOrdered, + boolean bContinue, BeforeAfter ba) { + // Step 1. We may have a style map, this always takes precedence + String sDisplayName = ofr.getListStyles().getDisplayName(sStyleName); + if (config.getListStyleMap().contains(sDisplayName)) { + ba.add(config.getListStyleMap().getBefore(sDisplayName), + config.getListStyleMap().getAfter(sDisplayName)); + return; + } + // Step 2: The list style may not exist, or the user wants to ignore it. + // In this case we create default lists + ListStyle style = ofr.getListStyle(sStyleName); + if (style==null || config.formatting()<=LaTeXConfig.IGNORE_MOST) { + if (nLevel<=4) { + if (bOrdered) { + ba.add("\\begin{enumerate}","\\end{enumerate}"); + } + else { + ba.add("\\begin{itemize}","\\end{itemize}"); + } + } + return; + } + // Step 3: Export as default lists, but redefine labels + if (config.formatting()==LaTeXConfig.CONVERT_BASIC) { + if (nLevel==1) { + if (!styleNames.containsName(getDisplayName(sStyleName))) { + createListStyleLabels(sStyleName); + } + ba.add("\\liststyle"+styleNames.getExportName(getDisplayName(sStyleName))+"\n",""); + } + if (nLevel<=4) { + String sCounterName = ((String[]) listStyleLevelNames.get(sStyleName))[nLevel]; + if (bContinue && style.isNumber(nLevel)) { + bNeedSaveEnumCounter = true; + ba.add("\\setcounter{saveenum}{\\value{"+sCounterName+"}}\n",""); + } + if (bOrdered) { + ba.add("\\begin{enumerate}","\\end{enumerate}"); + } + else { + ba.add("\\begin{itemize}","\\end{itemize}"); + } + if (bContinue && style.isNumber(nLevel)) { + ba.add("\n\\setcounter{"+sCounterName+"}{\\value{saveenum}}",""); + } + } + return; + } + // Step 4: Export with formatting, as "Writer style" custom lists + if (nLevel<=4) { // TODO: Max level should not be fixed + if (!styleNames.containsName(getDisplayName(sStyleName))) { + createListStyle(sStyleName); + } + String sTeXName="list"+styleNames.getExportName(getDisplayName(sStyleName)) + +"level"+Misc.int2roman(nLevel); + if (!bContinue && style.isNumber(nLevel)) { + int nStartValue = Misc.getPosInteger(style.getLevelProperty(nLevel,XMLString.TEXT_START_VALUE),1)-1; + ba.add("\\setcounter{"+sTeXName+"}{"+Integer.toString(nStartValue)+"}\n",""); + } + ba.add("\\begin{"+sTeXName+"}","\\end{"+sTeXName+"}"); + } + } + + /** <p>Apply a list style to a list item.</p> */ + public void applyListItemStyle(String sStyleName, int nLevel, boolean bHeader, + boolean bRestart, int nStartValue, BeforeAfter ba) { + // Step 1. We may have a style map, this always takes precedence + String sDisplayName = ofr.getListStyles().getDisplayName(sStyleName); + if (config.getListItemStyleMap().contains(sDisplayName)) { + ba.add(config.getListItemStyleMap().getBefore(sDisplayName), + config.getListItemStyleMap().getAfter(sDisplayName)); + return; + } + // Step 2: The list style may not exist, or the user wants to ignore it. + // In this case we create default lists + ListStyle style = ofr.getListStyle(sStyleName); + if (style==null || config.formatting()<=LaTeXConfig.IGNORE_MOST) { + if (nLevel<=4) { + if (bHeader) { ba.add("\\item[] ",""); } + else { ba.add("\\item ",""); } + } + return; + } + // Step 3: Export as default lists (with redefined labels) + if (config.formatting()==LaTeXConfig.CONVERT_BASIC) { + if (nLevel<=4) { + if (bHeader) { + ba.add("\\item[] ",""); + } + else if (bRestart && style.isNumber(nLevel)) { + ba.add("\n\\setcounter{enum"+Misc.int2roman(nLevel) + +"}{"+(nStartValue-1)+"}\n\\item ",""); + } + else { + ba.add("\\item ",""); + } + } + return; + } + // Step 4: Export with formatting, as "Writer style" custom lists + if (nLevel<=4 && !bHeader) { // TODO: Max level should not be fixed + String sTeXName="list"+styleNames.getExportName(getDisplayName(sStyleName)) + +"level"+Misc.int2roman(nLevel); + if (bRestart && style.isNumber(nLevel)) { + ba.add("\\setcounter{"+sTeXName+"}{"+(nStartValue-1)+"}\n",""); + } + ba.add("\\item ",""); + } + } + + + /** <p>Create labels for default lists (enumerate/itemize) based on + * a List Style + */ + private void createListStyleLabels(String sStyleName) { + String sTeXName = styleNames.getExportName(getDisplayName(sStyleName)); + declarations.append("\\newcommand\\liststyle") + .append(sTeXName).append("{%").nl(); + ListStyle style = ofr.getListStyle(sStyleName); + int nEnum = 0; + int nItem = 0; + String sName[] = new String[5]; + for (int i=1; i<=4; i++) { + if (style.isNumber(i)) { sName[i]="enum"+Misc.int2roman(++nEnum); } + else { sName[i]="item"+Misc.int2roman(++nItem); } + } + listStyleLevelNames.put(sStyleName, sName); + createLabels(style, sName, 4, false, true, false, declarations); + declarations.append("}").nl(); + } + + /** <p>Create "Writer style" lists based on a List Style. + <p>A list in writer is really a sequence of numbered paragraphs, so + this is also how we implement it in LaTeX. + The enivronment + redefined \item defines three hooks: + \writerlistleftskip, \writerlistparindent, \writerlistlabel + which are used by exported paragraph styles to apply numbering. + */ + private void createListStyle(String sStyleName) { + ListStyle style = ofr.getListStyle(sStyleName); + + // Create labels + String sTeXName = styleNames.getExportName(getDisplayName(sStyleName)); + String[] sLevelName = new String[5]; + for (int i=1; i<=4; i++) { + sLevelName[i]="list"+sTeXName+"level"+Misc.int2roman(i); + } + createLabels(style,sLevelName,4,true,false,true,declarations); + + // Create environments + for (int i=1; i<=4; i++) { + String sSpaceBefore = getLength(style,i,XMLString.TEXT_SPACE_BEFORE); + String sLabelWidth = getLength(style,i,XMLString.TEXT_MIN_LABEL_WIDTH); + String sLabelDistance = getLength(style,i,XMLString.TEXT_MIN_LABEL_DISTANCE); + String sTextAlign = style.getLevelStyleProperty(i,XMLString.FO_TEXT_ALIGN); + String sAlignmentChar = "l"; // start (or left) is default + if (sTextAlign!=null) { + if ("end".equals(sTextAlign)) { sAlignmentChar="r"; } + else if ("right".equals(sTextAlign)) { sAlignmentChar="r"; } + else if ("center".equals(sTextAlign)) { sAlignmentChar="c"; } + } + declarations + .append("\\newenvironment{") + .append(sLevelName[i]).append("}{") + .append("\\def\\writerlistleftskip{\\addtolength\\leftskip{") + .append(Misc.add(sSpaceBefore,sLabelWidth)).append("}}") + .append("\\def\\writerlistparindent{}") + .append("\\def\\writerlistlabel{}"); + // Redefine \item + declarations + .append("\\def\\item{") + .append("\\def\\writerlistparindent{\\setlength\\parindent{") + .append("-").append(sLabelWidth).append("}}") + .append("\\def\\writerlistlabel{"); + if (style.isNumber(i)) { + declarations.append("\\stepcounter{") + .append(sLevelName[i]).append("}"); + } + declarations + .append("\\makebox[").append(sLabelWidth).append("][") + .append(sAlignmentChar).append("]{") + .append("\\label").append(sLevelName[i]).append("}") + .append("\\hspace{").append(sLabelDistance).append("}") + .append("\\writerlistremovelabel}}}{}").nl(); + } + } + + /** <p>Create LaTeX list labels from an OOo list style. Examples:</p> + * <p>Bullets:</p> + * <pre>\newcommand\labelliststylei{\textbullet} + * \newcommand\labelliststyleii{*} + * \newcommand\labelliststyleiii{\textstylebullet{>}}</pre> + * <p>Numbering:</p> + * <pre>\newcounter{liststylei} + * \newcounter{liststyleii}[liststylei] + * \newcounter{liststyleiii}[liststyleii] + * \renewcommand\theliststylei{\Roman{liststylei}} + * \renewcommand\theliststyleii{\Roman{liststylei}.\arabic{liststyleii}} + * \renewcommand\theliststyleiii{\alph{liststyleiii}} + * \newcommand\labelliststylei{\textstylelabel{\theliststylei .}} + * \newcommand\labelliststyleii{\textstylelabel{\theliststyleii .}} + * \newcommand\labelliststyleiii{\textstylelabel{\theliststyleiii )}}</pre> + * + * @param <code>style</code> the OOo list style to use + * @param <code>sName</code> an array of label basenames to use + * @param <code>nMaxLevel</code> the highest level in this numbering + * @param <code>bDeclareCounters</code> true if counters should be declared (they may + * exist already, eg. "section", "subsection"... or "enumi", "enumii"... + * @param <code>bRenewLabels</code> true if labels should be defined with \renewcommand + * @param <code>bUseTextStyle</code> true if labels should be formatted with the associated text style + * (rather than \newcommand). + * @param <code>ldp</code> the <code>LaTeXDocumentPortion</code> to add LaTeX code to. + */ + private void createLabels(ListStyle style, String[] sName, int nMaxLevel, + boolean bDeclareCounters, boolean bRenewLabels, + boolean bUseTextStyle, LaTeXDocumentPortion ldp) { + // Declare counters if required (eg. "\newcounter{countername1}[countername2]") + if (bDeclareCounters) { + int j = 0; + for (int i=1; i<=nMaxLevel; i++) { + if (style.isNumber(i)) { + ldp.append("\\newcounter{").append(sName[i]).append("}"); + if (j>0) { ldp.append("[").append(sName[j]).append("]"); } + ldp.nl(); + j = i; + } + } + } + // Create numbering for each level (eg. "\arabic{countername}") + String[] sNumFormat = new String[nMaxLevel+1]; + for (int i=1; i<=nMaxLevel; i++) { + String s = numFormat(style.getLevelProperty(i,XMLString.STYLE_NUM_FORMAT)); + if (s==null) { sNumFormat[i]=""; } + else { sNumFormat[i] = s + "{" + sName[i] + "}"; } + } + // Create numberings (ie. define "\thecountername"): + for (int i=1; i<=nMaxLevel; i++) { + if (style.isNumber(i)) { + ldp.append("\\renewcommand\\the").append(sName[i]).append("{"); + int nLevels = Misc.getPosInteger(style.getLevelProperty(i,XMLString.TEXT_DISPLAY_LEVELS),1); + for (int j=i-nLevels+1; j<i; j++) { + if (style.isNumber(j)) { + ldp.append(sNumFormat[j]).append("."); + } + } + ldp.append(sNumFormat[i]); + ldp.append("}").nl(); + } + } + // Create labels (ie. define "\labelcountername"): + for (int i=1; i<=nMaxLevel; i++) { + ldp.append(bRenewLabels ? "\\renewcommand" : "\\newcommand") + .append("\\label").append(sName[i]).append("{"); + // Apply text style if required + BeforeAfter baText = new BeforeAfter(); + if (bUseTextStyle) { + String sStyleName = style.getLevelProperty(i,XMLString.TEXT_STYLE_NAME); + palette.getCharSc().applyTextStyle(sStyleName,baText,new Context()); + } + + // Create label content + if (style.isNumber(i)) { + String sPrefix = style.getLevelProperty(i,XMLString.STYLE_NUM_PREFIX); + String sSuffix = style.getLevelProperty(i,XMLString.STYLE_NUM_SUFFIX); + // Apply style + ldp.append(baText.getBefore()); + if (sPrefix!=null) { ldp.append(sPrefix); } + ldp.append("\\the").append(sName[i]); + if (sSuffix!=null) { ldp.append(sSuffix); } + ldp.append(baText.getAfter()); + } + else if (style.isBullet(i)) { + String sBullet = style.getLevelProperty(i,XMLString.TEXT_BULLET_CHAR); + // Apply style + ldp.append(baText.getBefore()); + if (sBullet!=null) { + // Bullets are usually symbols, so this should be OK: + ldp.append(palette.getI18n().convert(sBullet,false,"en")); + } + ldp.append(baText.getAfter()); + } + else { + // TODO: Support images! + ldp.append("\\textbullet"); + } + + ldp.append("}").nl(); + } + } + + /* Helper: Get a length property that defaults to 0cm. */ + private String getLength(ListStyle style,int nLevel,String sProperty) { + String s = style.getLevelStyleProperty(nLevel,sProperty); + if (s==null) { return "0cm"; } + else { return s; } + } + + /* Helper: Get display name, or original name if it doesn't exist */ + private String getDisplayName(String sName) { + String sDisplayName = ofr.getListStyles().getDisplayName(sName); + return sDisplayName!=null ? sDisplayName : sName; + } + + /* Helper: Convert OOo number format to LaTeX number format */ + public static final String numFormat(String sFormat){ + if ("1".equals(sFormat)) { return "\\arabic"; } + else if ("i".equals(sFormat)) { return "\\roman"; } + else if ("I".equals(sFormat)) { return "\\Roman"; } + else if ("a".equals(sFormat)) { return "\\alph"; } + else if ("A".equals(sFormat)) { return "\\Alph"; } + else { return null; } + } + +} diff --git a/source/java/writer2latex/latex/MathmlConverter.java b/source/java/writer2latex/latex/MathmlConverter.java new file mode 100644 index 0000000..821d909 --- /dev/null +++ b/source/java/writer2latex/latex/MathmlConverter.java @@ -0,0 +1,232 @@ +/************************************************************************ + * + * MathmlConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.latex; + +//import java.util.Hashtable; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +//import writer2latex.latex.i18n.I18n; +import writer2latex.office.MIMETypes; +import writer2latex.office.OfficeReader; +import writer2latex.office.XMLString; +import writer2latex.util.Misc; +import writer2latex.xmerge.EmbeddedObject; +import writer2latex.xmerge.EmbeddedXMLObject; + +/** + * This class converts mathml nodes to LaTeX. + * (Actually it only converts the starmath annotation currently, if available). + */ +public final class MathmlConverter extends ConverterHelper { + + private StarMathConverter smc; + + private boolean bContainsFormulas = false; + + public MathmlConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + smc = new StarMathConverter(palette.getI18n(),config); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bContainsFormulas) { + if (config.useOoomath()) { + pack.append("\\usepackage{ooomath}").nl(); + } + else { + smc.appendDeclarations(pack,decl); + } + } + } + + public String convert(Node settings, Node formula) { + // TODO: Use settings to determine display mode/text mode + // formula must be a math:math node + // First try to find a StarMath annotation + Node semantics = Misc.getChildByTagName(formula,XMLString.MATH_SEMANTICS); + if (semantics!=null) { + Node annotation = Misc.getChildByTagName(semantics,XMLString.MATH_ANNOTATION); + if (annotation!=null) { + String sStarMath = ""; + if (annotation.hasChildNodes()) { + NodeList anl = annotation.getChildNodes(); + int nLen = anl.getLength(); + for (int i=0; i<nLen; i++) { + if (anl.item(i).getNodeType() == Node.TEXT_NODE) { + sStarMath+=anl.item(i).getNodeValue(); + } + } + bContainsFormulas = true; + return smc.convert(sStarMath); + } + } + } + // No annotation was found. In this case we should convert the mathml, + // but currently we ignore the problem. + // TODO: Investigate if Vasil I. Yaroshevich's MathML->LaTeX + // XSL transformation could be used here. (Potential problem: + // OOo uses MathML 1.01, not MathML 2) + return "\\text{Warning: No StarMath annotation}"; + } + + // Data for display equations + private Element theEquation = null; + private Element theSequence = null; + + /**Try to convert a paragraph as a display equation: + * A paragraph which contains exactly one formula + at most one sequence + * number is treated as a display equation. Other content must be brackets + * or whitespace (possible with formatting). + * @param node the paragraph + * @param ldp the LaTeXDocumentPortion to contain the converted equation + * @return true if the conversion was succesful, false if the paragraph + * did not contain a display equation + */ + public boolean handleDisplayEquation(Element node, LaTeXDocumentPortion ldp) { + theEquation = null; + theSequence = null; + if (parseDisplayEquation(node) && theEquation!=null) { + if (theSequence!=null) { + // Numbered equation + ldp.append("\\begin{equation}"); + palette.getFieldCv().handleSequenceLabel(theSequence,ldp); + ldp.nl() + .append(convert(null,theEquation)).nl() + .append("\\end{equation}").nl(); + } + else { + // Unnumbered equation + ldp.append("\\begin{equation*}").nl() + .append(convert(null,theEquation)).nl() + .append("\\end{equation*}").nl(); + } + return true; + } + else { + return false; + } + } + + private boolean parseDisplayEquation(Node node) { + Node child = node.getFirstChild(); + while (child!=null) { + Node equation = getFormula(child); + if (equation!=null) { + if (theEquation==null) { + theEquation = (Element) equation; + } + else { // two or more equations -> not a display + return false; + } + } + else if (Misc.isElement(child)) { + String sName = child.getNodeName(); + if (XMLString.TEXT_SEQUENCE.equals(sName)) { + if (theSequence==null) { + theSequence = (Element) child; + } + else { // two sequence numbers -> not a display + return false; + } + } + else if (XMLString.TEXT_SPAN.equals(sName)) { + if (!parseDisplayEquation(child)) { + return false; + } + } + else if (XMLString.TEXT_S.equals(sName)) { + // Spaces are allowed + } + else if (XMLString.TEXT_TAB.equals(sName)) { + // Tab stops are allowed + } + else if (XMLString.TEXT_TAB_STOP.equals(sName)) { // old + // Tab stops are allowed + } + else { + // Other elements -> not a display + return false; + } + } + else if (Misc.isText(child)) { + String s = child.getNodeValue(); + int nLen = s.length(); + for (int i=0; i<nLen; i++) { + char c = s.charAt(i); + if (c!='(' && c!=')' && c!='[' && c!=']' && c!='{' && c!='}' && c!=' ' && c!='\u00A0') { + // Characters except brackets and whitespace -> not a display + return false; + } + } + } + child = child.getNextSibling(); + } + return true; + } + + // TODO: Extend OfficeReader to handle frames + private Node getFormula(Node node) { + if (Misc.isElement(node,XMLString.DRAW_FRAME)) { + node=Misc.getFirstChildElement(node); + } + + String sHref = Misc.getAttribute(node,XMLString.XLINK_HREF); + + if (sHref!=null) { // Embedded object in package or linked object + if (ofr.isInPackage(sHref)) { // Embedded object in package + if (sHref.startsWith("#")) { sHref=sHref.substring(1); } + if (sHref.startsWith("./")) { sHref=sHref.substring(2); } + EmbeddedObject object = palette.getEmbeddedObject(sHref); + if (object!=null) { + if (MIMETypes.MATH.equals(object.getType()) || MIMETypes.ODF.equals(object.getType())) { // Formula! + try { + Document formuladoc = ((EmbeddedXMLObject) object).getContentDOM(); + return Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH); + } + catch (org.xml.sax.SAXException e) { + e.printStackTrace(); + } + catch (java.io.IOException e) { + e.printStackTrace(); + } + } + } + } + } + else { // flat xml, object is contained in node + return Misc.getChildByTagName(node,XMLString.MATH_MATH); + } + return null; + } + + + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/NoteConverter.java b/source/java/writer2latex/latex/NoteConverter.java new file mode 100644 index 0000000..eb618a5 --- /dev/null +++ b/source/java/writer2latex/latex/NoteConverter.java @@ -0,0 +1,391 @@ +/************************************************************************ + * + * NoteConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import java.util.LinkedList; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.util.Misc; +import writer2latex.util.ExportNameCollection; +import writer2latex.office.OfficeReader; +import writer2latex.office.PropertySet; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; + +/** + * <p>This class handles conversion of footnotes and endnotes, including + * references. It takes advantage of the packages <code>endnotes.sty</code> + * and <code>perpage.sty</code> if allowed in the configuration.</p> + */ +public class NoteConverter extends ConverterHelper { + + private ExportNameCollection footnotenames = new ExportNameCollection(true); + private ExportNameCollection endnotenames = new ExportNameCollection(true); + private boolean bContainsEndnotes = false; + private boolean bContainsFootnotes = false; + // Keep track of footnotes (inside minipage etc.), that should be typeset later + private LinkedList postponedFootnotes = new LinkedList(); + + public NoteConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + /** <p>Append declarations needed by the <code>NoteConverter</code> to + * the preamble. + * @param pack the <code>LaTeXDocumentPortion</code> to which + * declarations of packages should be added (<code>\\usepackage</code>). + * @param decl the <code>LaTeXDocumentPortion</code> to which + * other declarations should be added. + */ + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bContainsEndnotes) { pack.append("\\usepackage{endnotes}").nl(); } + if (bContainsFootnotes) convertFootnotesConfiguration(decl); + if (bContainsEndnotes) convertEndnotesConfiguration(decl); + } + + /** <p>Process a footnote (text:footnote tag) + * @param node The element containing the footnote + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleFootnote(Element node, LaTeXDocumentPortion ldp, Context oc) { + Context ic = (Context) oc.clone(); + ic.setInFootnote(true); + + String sId = node.getAttribute(XMLString.TEXT_ID); + Element fntbody = Misc.getChildByTagName(node,XMLString.TEXT_FOOTNOTE_BODY); + if (fntbody==null) { // try oasis + fntbody = Misc.getChildByTagName(node,XMLString.TEXT_NOTE_BODY); + } + if (fntbody != null) { + bContainsFootnotes = true; + if (ic.isNoFootnotes()) { + ldp.append("\\footnotemark{}"); + postponedFootnotes.add(fntbody); + } + else { + ldp.append("\\footnote"); + ldp.append("{"); + if (sId != null && ofr.hasFootnoteRefTo(sId)) { + ldp.append("\\label{fnt:"+footnotenames.getExportName(sId)+"}"); + } + traverseNoteBody(fntbody,ldp,ic); + ldp.append("}"); + } + } + } + + /** Flush the queue of postponed footnotes */ + public void flushFootnotes(LaTeXDocumentPortion ldp, Context oc) { + // We may still be in a context with no footnotes + if (oc.isNoFootnotes()) { return; } + // Type out all postponed footnotes: + Context ic = (Context) oc.clone(); + ic.setInFootnote(true); + int n = postponedFootnotes.size(); + if (n==1) { + ldp.append("\\footnotetext{"); + traverseNoteBody((Element) postponedFootnotes.get(0),ldp,ic); + ldp.append("}").nl(); + postponedFootnotes.clear(); + } + else if (n>1) { + // Several footnotes; have to adjust the footnote counter + ldp.append("\\addtocounter{footnote}{-"+n+"}").nl(); + for (int i=0; i<n; i++) { + ldp.append("\\stepcounter{footnote}\\footnotetext{"); + traverseNoteBody((Element) postponedFootnotes.get(i),ldp,ic); + ldp.append("}").nl(); + } + postponedFootnotes.clear(); + } + } + + /** <p>Process an endnote (text:endnote tag) + * @param node The element containing the endnote + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleEndnote(Element node, LaTeXDocumentPortion ldp, Context oc) { + Context ic = (Context) oc.clone(); + ic.setInFootnote(true); + + String sId = node.getAttribute(XMLString.TEXT_ID); + Element entbody = Misc.getChildByTagName(node,XMLString.TEXT_ENDNOTE_BODY); + if (entbody==null) { // try oasis + entbody = Misc.getChildByTagName(node,XMLString.TEXT_NOTE_BODY); + } + if (entbody != null) { + if (ic.isNoFootnotes() && !config.useEndnotes()) { + ldp.append("\\footnotemark()"); + postponedFootnotes.add(entbody); + } + else { + if (config.useEndnotes()) { + ldp.append("\\endnote"); + bContainsEndnotes = true; + } + else { + ldp.append("\\footnote"); + bContainsFootnotes = true; + } + ldp.append("{"); + if (sId != null && ofr.hasEndnoteRefTo(sId)) { + ldp.append("\\label{ent:"+endnotenames.getExportName(sId)+"}"); + } + traverseNoteBody(entbody,ldp,ic); + ldp.append("}"); + } + } + } + + /** <p>Insert the endnotes into the documents. + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * the endnotes should be added. + */ + public void insertEndnotes(LaTeXDocumentPortion ldp) { + if (bContainsEndnotes) { + ldp.append("\\clearpage").nl() + .append("\\theendnotes").nl(); + } + } + + + + /** <p>Process a note reference (text:note-ref tag, oasis) + * @param node The element containing the note reference + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleNoteRef(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sClass=node.getAttribute(XMLString.TEXT_NOTE_CLASS); + if (sClass.equals("footnote")) { handleFootnoteRef(node,ldp,oc); } + else if (sClass.equals("endnote")) { handleEndnoteRef(node,ldp,oc); } + } + + /** <p>Process a footnote reference (text:footnote-ref tag) + * @param node The element containing the footnote reference + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleFootnoteRef(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT); + String sName = node.getAttribute(XMLString.TEXT_REF_NAME); + if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) { + ldp.append("\\pageref{fnt:"+footnotenames.getExportName(sName)+"}"); + } + else if ("text".equals(sFormat) && sName!=null) { + ldp.append("\\ref{fnt:"+footnotenames.getExportName(sName)+"}"); + } + else { // use current value + palette.getInlineCv().traversePCDATA(node,ldp,oc); + } + } + + /** <p>Process an endnote reference (text:endnote-ref tag) + * @param node The element containing the endnote reference + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleEndnoteRef(Element node, LaTeXDocumentPortion ldp, Context oc) { + String sFormat = node.getAttribute(XMLString.TEXT_REFERENCE_FORMAT); + String sName = node.getAttribute(XMLString.TEXT_REF_NAME); + if (("page".equals(sFormat) || "".equals(sFormat)) && sName!=null) { + ldp.append("\\pageref{ent:"+endnotenames.getExportName(sName)+"}"); + } + else if ("text".equals(sFormat) && sName!=null) { + ldp.append("\\ref{ent:"+endnotenames.getExportName(sName)+"}"); + } + else { // use current value + palette.getInlineCv().traversePCDATA(node,ldp,oc); + } + } + + /** <p>Add a footnote name. The method <code>handleFootnote</code> includes + * a <code>\label</code> only if the footnote name is already known to the + * <code>NoteConverter</code>. Hence this method is invoked by the prepass + * for each footnote reference. The end result is, that only necessary + * labels will be included. + * @param sName the name (id) of the footnote + */ + public void addFootnoteName(String sName) { footnotenames.addName(sName); } + + /** <p>Add an endnote name. The method <code>handleEndnote</code> includes + * a <code>\label</code> only if the endnote name is already known to the + * <code>NoteConverter</code>. Hence this method is invoked by the prepass + * for each endnote reference. The end result is, that only necessary + * labels will be included. + * @param sName the name (id) of the endnote + */ + public void addEndnoteName(String sName) { endnotenames.addName(sName); } + + /* + * Process the contents of a footnote or endnote + * TODO: Merge with BlockConverter.traverseBlockText? + */ + private void traverseNoteBody (Element node, LaTeXDocumentPortion ldp, Context oc) { + if (node.hasChildNodes()) { + NodeList nList = node.getChildNodes(); + int len = nList.getLength(); + + for (int i = 0; i < len; i++) { + Node childNode = nList.item(i); + + if (childNode.getNodeType() == Node.ELEMENT_NODE) { + Element child = (Element)childNode; + String nodeName = child.getTagName(); + + palette.getInfo().addDebugInfo(child,ldp); + + if (nodeName.equals(XMLString.TEXT_H)) { + palette.getHeadingCv().handleHeading(child,ldp,oc); + } + + if (nodeName.equals(XMLString.TEXT_P)) { + palette.getInlineCv().traverseInlineText(child,ldp,oc); + if (i<len-1) { + if (nList.item(i+1).getNodeName().startsWith(XMLString.TEXT_)) { + ldp.append("\\par "); + } + else { + ldp.nl(); + } + } + } + + else if (nodeName.equals(XMLString.TEXT_LIST)) { // oasis + palette.getBlockCv().handleList(child,ldp,oc); + } + + if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) { + palette.getBlockCv().handleList(child,ldp,oc); + } + + if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) { + palette.getBlockCv().handleList(child,ldp,oc); + } + } + } + } + } + + // Convert footnotes configuration. + private void convertFootnotesConfiguration(LaTeXDocumentPortion ldp) { + // Note: Continuation notices are not supported in LaTeX + // TODO: Support text:footnotes-postion="document" (footnotes as endnotes) + // TODO: Support text:start-numbering-at="page" (footnpag.sty/footmisc.sty) + convertFootEndnotesConfiguration(ofr.getFootnotesConfiguration(),"foot",ldp); + } + + // Convert endnotes configuration. + private void convertEndnotesConfiguration(LaTeXDocumentPortion ldp) { + // Note: Continuation notices are not supported in LaTeX + convertFootEndnotesConfiguration(ofr.getEndnotesConfiguration(),"end",ldp); + } + + /* Convert {foot|end}notes configuration. + * Note: All {foot|end}notes are formatted with the default style for {foot|end}footnotes. + * (This doesn't conform with the file format specification, but in LaTeX + * all {foot|end}notes are usually formatted in a fixed style.) + */ + private void convertFootEndnotesConfiguration(PropertySet notes, String sType, LaTeXDocumentPortion ldp) { + if (config.formatting()<LaTeXConfig.CONVERT_BASIC) { return; } + String sTypeShort = sType.equals("foot") ? "fn" : "en"; + if (notes==null) { return; } + ldp.append("% ").append(sType).append("notes configuration").nl() + .append("\\makeatletter").nl(); + + // The numbering style is controlled by \the{foot|end}note + String sFormat = notes.getProperty(XMLString.STYLE_NUM_FORMAT); + if (sFormat!=null) { + ldp.append("\\renewcommand\\the").append(sType).append("note{") + .append(ListStyleConverter.numFormat(sFormat)) + .append("{").append(sType).append("note}}").nl(); + } + + // Number {foot|end}notes by sections + if ("chapter".equals(notes.getProperty(XMLString.TEXT_START_NUMBERING_AT))) { + ldp.append("\\@addtoreset{").append(sType).append("note}{section}").nl(); + } + + // Set start value offset (default 0) + int nStartValue = Misc.getPosInteger(notes.getProperty(XMLString.TEXT_START_VALUE),0); + if (nStartValue!=0) { + ldp.append("\\setcounter{").append(sType).append("note}{"+nStartValue+"}").nl(); + } + + if (config.formatting()>=LaTeXConfig.CONVERT_MOST) { + // The formatting of the {foot|end}note citation is controlled by \@make{fn|en}mark + String sCitBodyStyle = notes.getProperty(XMLString.TEXT_CITATION_BODY_STYLE_NAME); + if (sCitBodyStyle!=null && ofr.getTextStyle(sCitBodyStyle)!=null) { + BeforeAfter baText = new BeforeAfter(); + palette.getCharSc().applyTextStyle(sCitBodyStyle,baText,new Context()); + ldp.append("\\renewcommand\\@make").append(sTypeShort).append("mark{\\mbox{") + .append(baText.getBefore()) + .append("\\@the").append(sTypeShort).append("mark") + .append(baText.getAfter()) + .append("}}").nl(); + } + + // The layout and formatting of the {foot|end}note is controlled by \@make{fn|en}text + String sCitStyle = notes.getProperty(XMLString.TEXT_CITATION_STYLE_NAME); + String sStyleName = notes.getProperty(XMLString.TEXT_DEFAULT_STYLE_NAME); + if (sStyleName!=null) { + BeforeAfter baText = new BeforeAfter(); + palette.getCharSc().applyTextStyle(sCitStyle,baText,new Context()); + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style!=null) { + BeforeAfter baPar = new BeforeAfter(); + palette.getCharSc().applyHardCharFormatting(style,baPar); + ldp.append("\\renewcommand\\@make").append(sTypeShort) + .append("text[1]{\\noindent") + .append(baText.getBefore()) + .append("\\@the").append(sTypeShort).append("mark\\ ") + .append(baText.getAfter()) + .append(baPar.getBefore()) + .append("#1") + .append(baPar.getAfter()); + ldp.append("}").nl(); + } + } + } + + ldp.append("\\makeatother").nl(); + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/Package.html b/source/java/writer2latex/latex/Package.html new file mode 100644 index 0000000..09c22a1 --- /dev/null +++ b/source/java/writer2latex/latex/Package.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.latex</title> +</head> + +<body> +<p>This package contains LaTeX specific code.</p> +<p>It contains a <code>writerlatex.api.Converter</code> implementation for +conversion into LaTeX.</p> +<p>Since version 1.0 you can build Writer2LaTeX without this package if you +don't need LaTeX support (in this case you can exclude writer2latex.bibtex as +well).</p> +</body> +</html> diff --git a/source/java/writer2latex/latex/PageStyleConverter.java b/source/java/writer2latex/latex/PageStyleConverter.java new file mode 100644 index 0000000..e4903e0 --- /dev/null +++ b/source/java/writer2latex/latex/PageStyleConverter.java @@ -0,0 +1,596 @@ +/************************************************************************ + * + * PageStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import java.util.Enumeration; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.util.CSVList; +import writer2latex.util.Misc; +import writer2latex.office.*; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; + +// TODO: chngpage.sty?? + +/* This class creates LaTeX code from OOo page layouts/master pages + */ +public class PageStyleConverter extends StyleConverter { + + // Value of attribute text:display of most recent text:chapter field + // This is used to handle chaptermarks in headings + private String sChapterField1 = null; + private String sChapterField2 = null; + + // The page layout used for the page geometry + // (LaTeX only supports one page geometry per page) + private PageLayout mainPageLayout; + + /** <p>Constructs a new <code>PageStyleConverter</code>.</p> + */ + public PageStyleConverter(OfficeReader ofr, LaTeXConfig config, + ConverterPalette palette) { + super(ofr,config,palette); + // Determine the main page master + MasterPage firstMasterPage = ofr.getFirstMasterPage(); + String sPageLayoutName = null; + if (firstMasterPage!=null) { + MasterPage nextMasterPage = ofr.getMasterPage( + firstMasterPage.getProperty(XMLString.STYLE_NEXT_STYLE_NAME)); + if (nextMasterPage!=null) { + sPageLayoutName = nextMasterPage.getPageLayoutName(); + } + else { + sPageLayoutName = firstMasterPage.getPageLayoutName(); + } + } + mainPageLayout = ofr.getPageLayout(sPageLayoutName); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (config.useFancyhdr()) { pack.append("\\usepackage{fancyhdr}").nl(); } + // The first master page must be known + MasterPage firstMasterPage = ofr.getFirstMasterPage(); + if (firstMasterPage!=null) { + styleNames.addName(getDisplayName(firstMasterPage.getName())); + } + // Convert page geometry + convertPageMasterGeometry(pack,decl); + // Convert master pages + convertMasterPages(decl); + if (firstMasterPage!=null) { + BeforeAfter ba = new BeforeAfter(); + applyMasterPage(firstMasterPage.getName(),ba); + decl.append(ba.getBefore()); + } + + } + + public void setChapterField1(String s) { sChapterField1 = s; } + + public void setChapterField2(String s) { sChapterField2 = s; } + + public boolean isTwocolumn() { + return mainPageLayout!=null && mainPageLayout.getColCount()>1; + } + + /** <p>Apply page break properties from a style.</p> + * @param style the style to use + * @param bInherit true if inheritance from parent style should be used + * @param ba a <code>BeforeAfter</code> to put code into + */ + public void applyPageBreak(StyleWithProperties style, boolean bInherit, BeforeAfter ba) { + if (style==null) { return; } + if (style.isAutomatic() && config.ignoreHardPageBreaks()) { return; } + // A page break can be a simple page break before or after... + String s = style.getProperty(XMLString.FO_BREAK_BEFORE,bInherit); + if ("page".equals(s)) { ba.add("\\clearpage",""); } + s = style.getProperty(XMLString.FO_BREAK_AFTER,bInherit); + if ("page".equals(s)) { ba.add("","\\clearpage"); } + // ...or it can be a new master page + String sMasterPage = style.getMasterPageName(); + if (sMasterPage==null || sMasterPage.length()==0) { return; } + ba.add("\\clearpage",""); + String sPageNumber=style.getProperty(XMLString.STYLE_PAGE_NUMBER); + if (sPageNumber!=null) { + int nPageNumber = Misc.getPosInteger(sPageNumber,1); + ba.add("\\setcounter{page}{"+nPageNumber+"}",""); + } + applyMasterPage(sMasterPage,ba); + } + + /** <p>Use a Master Page (pagestyle in LaTeX)</p> + * @param sName name of the master page to use + * @param ba the <code>BeforeAfter</code> to add code to. + */ + private void applyMasterPage(String sName, BeforeAfter ba) { + if (config.pageFormatting()==LaTeXConfig.IGNORE_ALL) return; + MasterPage style = ofr.getMasterPage(sName); + if (style==null) { return; } + String sNextName = style.getProperty(XMLString.STYLE_NEXT_STYLE_NAME); + MasterPage nextStyle = ofr.getMasterPage(sNextName); + if (style==nextStyle || nextStyle==null) { + ba.add("\\pagestyle{"+styleNames.getExportName(getDisplayName(sName))+"}\n", ""); + } + else { + ba.add("\\pagestyle{"+styleNames.getExportName(getDisplayName(sNextName))+"}\n"+ + "\\thispagestyle{"+styleNames.getExportName(getDisplayName(sName))+"}\n",""); + } + // todo: should warn the user if next master also contains a next-style-name; + // LaTeX's page style mechanism cannot handle that + } + + /* + * Process header or footer contents + */ + private void convertMasterPages(LaTeXDocumentPortion ldp) { + if (config.pageFormatting()==LaTeXConfig.IGNORE_ALL) { return; } + + Context context = new Context(); + context.resetFormattingFromStyle(ofr.getDefaultParStyle()); + context.setInHeaderFooter(true); + + + Enumeration styles = ofr.getMasterPages().getStylesEnumeration(); + ldp.append("% Pages styles").nl(); + if (!config.useFancyhdr()) { + ldp.append("\\makeatletter").nl(); + } + while (styles.hasMoreElements()) { + MasterPage style = (MasterPage) styles.nextElement(); + String sName = style.getName(); + if (styleNames.containsName(getDisplayName(sName))) { + sChapterField1 = null; + sChapterField2 = null; + + String sPageLayout = style.getPageLayoutName(); + PageLayout pageLayout = ofr.getPageLayout(sPageLayout); + + if (config.useFancyhdr()) { + ldp.append("\\fancypagestyle{") + .append(styleNames.getExportName(getDisplayName(sName))) + .append("}{\\fancyhf{}").nl(); + // Header - odd or both + ldp.append(" \\fancyhead[") + .append(getParAlignment(style.getHeader())) + .append(style.getHeaderLeft()!=null ? "O" : "") + .append("]{"); + traverseHeaderFooter((Element)style.getHeader(),ldp,context); + ldp.append("}").nl(); + // Header - even + if (style.getHeaderLeft()!=null) { + ldp.append(" \\fancyhead[") + .append(getParAlignment(style.getHeaderLeft())) + .append("E]{"); + traverseHeaderFooter((Element)style.getHeaderLeft(),ldp,context); + ldp.append("}").nl(); + } + // Footer - odd or both + ldp.append(" \\fancyfoot[") + .append(getParAlignment(style.getFooter())) + .append(style.getFooterLeft()!=null ? "O" : "") + .append("]{"); + traverseHeaderFooter((Element)style.getFooter(),ldp,context); + ldp.append("}").nl(); + // Footer - even + if (style.getFooterLeft()!=null) { + ldp.append(" \\fancyfoot[") + .append(getParAlignment(style.getFooterLeft())) + .append("E]{"); + traverseHeaderFooter((Element)style.getFooterLeft(),ldp,context); + ldp.append("}").nl(); + } + // Rules + ldp.append(" \\renewcommand\\headrulewidth{") + .append(getBorderWidth(pageLayout,true)) + .append("}").nl() + .append(" \\renewcommand\\footrulewidth{") + .append(getBorderWidth(pageLayout,false)) + .append("}").nl(); + } + else { // use low-level page styles + ldp.append("\\newcommand\\ps@") + .append(styleNames.getExportName(getDisplayName(sName))) + .append("{").nl(); + // Header + ldp.append(" \\renewcommand\\@oddhead{"); + traverseHeaderFooter((Element)style.getHeader(),ldp,context); + ldp.append("}").nl(); + ldp.append(" \\renewcommand\\@evenhead{"); + if (style.getHeaderLeft()!=null) { + traverseHeaderFooter((Element)style.getHeaderLeft(),ldp,context); + } + else if (style.getHeader()!=null) { + ldp.append("\\@oddhead"); + } + ldp.append("}").nl(); + // Footer + ldp.append(" \\renewcommand\\@oddfoot{"); + traverseHeaderFooter((Element)style.getFooter(),ldp,context); + ldp.append("}").nl(); + ldp.append(" \\renewcommand\\@evenfoot{"); + if (style.getFooterLeft()!=null) { + traverseHeaderFooter((Element)style.getFooterLeft(),ldp,context); + } + else if (style.getFooter()!=null) { + ldp.append("\\@oddfoot"); + } + ldp.append("}").nl(); + } + + // Sectionmark and subsectionmark + if (sChapterField1!=null) { + ldp.append(" \\def\\sectionmark##1{\\markboth{"); + if ("name".equals(sChapterField1)) { ldp.append("##1"); } + else if ("number".equals(sChapterField1) || "plain-number".equals(sChapterField1)) { + ldp.append("\\thesection"); + } + else { ldp.append("\\thesection\\ ##1"); } + ldp.append("}{}}").nl(); + } + if (sChapterField2!=null) { + if (sChapterField1==null) { + ldp.append(" \\def\\sectionmark##1{\\markboth{}{}}").nl(); + } + ldp.append(" \\def\\subsectionmark##1{\\markright{"); + if ("name".equals(sChapterField2)) { ldp.append("##1"); } + else if ("number".equals(sChapterField2) || "plain-number".equals(sChapterField1)) { + ldp.append("\\thesubsection"); + } + else { ldp.append("\\thesubsection\\ ##1"); } + ldp.append("}{}}").nl(); + } + // Page number (this is the only part of the page master used in each page style) + if (pageLayout!=null) { + String sNumFormat = pageLayout.getProperty(XMLString.STYLE_NUM_FORMAT); + if (sNumFormat!=null) { + ldp.append(" \\renewcommand\\thepage{") + .append(ListStyleConverter.numFormat(sNumFormat)) + .append("{page}}").nl(); + } + String sPageNumber = pageLayout.getProperty(XMLString.STYLE_FIRST_PAGE_NUMBER); + if (sPageNumber!=null && !sPageNumber.equals("continue")) { + ldp.append(" \\setcounter{page}{") + .append(Integer.toString(Misc.getPosInteger(sPageNumber,0))) + .append("}").nl(); + } + } + + ldp.append("}").nl(); + } + } + if (!config.useFancyhdr()) { + ldp.append("\\makeatother").nl(); + } + } + + // Get alignment of first paragraph in node + private String getParAlignment(Node node) { + String sAlign = "L"; + if (node!=null) { + Element par = Misc.getChildByTagName(node,XMLString.TEXT_P); + if (par!=null) { + String sStyleName = Misc.getAttribute(par,XMLString.TEXT_STYLE_NAME); + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style!=null) { + String s = style.getProperty(XMLString.FO_TEXT_ALIGN); + if ("center".equals(s)) { sAlign = "C"; } + else if ("end".equals(s)) { sAlign = "R"; } + } + } + } + return sAlign; + } + + // Get border width from header/footer style + private String getBorderWidth(PageLayout style, boolean bHeader) { + if (style==null) { return "0pt"; } + String sBorder; + if (bHeader) { + sBorder = style.getHeaderProperty(XMLString.FO_BORDER_BOTTOM); + if (sBorder==null) { + sBorder = style.getHeaderProperty(XMLString.FO_BORDER); + } + } + else { + sBorder = style.getFooterProperty(XMLString.FO_BORDER_TOP); + if (sBorder==null) { + sBorder = style.getFooterProperty(XMLString.FO_BORDER); + } + } + if (sBorder!=null && !sBorder.equals("none")) { + return sBorder.substring(0,sBorder.indexOf(' ')); + } + else { + return "0pt"; + } + } + + private void traverseHeaderFooter(Element node, LaTeXDocumentPortion ldp, Context context) { + if (node==null) { return; } + // get first paragraph; all other content is ignored + Element par = Misc.getChildByTagName(node,XMLString.TEXT_P); + if (par==null) { return; } + + String sStyleName = par.getAttribute(XMLString.TEXT_STYLE_NAME); + BeforeAfter ba = new BeforeAfter(); + // Temp solution: Ignore hard formatting in header/footer (name clash problem) + // only in package format. TODO: Find a better solution! + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style!=null && (!ofr.isPackageFormat() || !style.isAutomatic())) { + palette.getCharSc().applyHardCharFormatting(style,ba); + } + + if (par.hasChildNodes()) { + ldp.append(ba.getBefore()); + palette.getInlineCv().traverseInlineText(par,ldp,context); + ldp.append(ba.getAfter()); + } + + } + + // TODO: Reenable several geometries per document?? + private void convertPageMasterGeometry(LaTeXDocumentPortion pack, LaTeXDocumentPortion ldp) { + if (config.pageFormatting()!=LaTeXConfig.CONVERT_ALL) { return; } + if (mainPageLayout==null) { return; } + + // Set global document options + if ("mirrored".equals(mainPageLayout.getPageUsage())) { + palette.addGlobalOption("twoside"); + } + if (isTwocolumn()) { + palette.addGlobalOption("twocolumn"); + } + + // Collect all page geometry + // 1. Page size + String sPaperHeight = mainPageLayout.getAbsoluteProperty(XMLString.FO_PAGE_HEIGHT); + String sPaperWidth = mainPageLayout.getAbsoluteProperty(XMLString.FO_PAGE_WIDTH); + // 2. Margins + String sMarginTop = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_TOP); + String sMarginBottom = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_BOTTOM); + String sMarginLeft = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_LEFT); + String sMarginRight = mainPageLayout.getAbsoluteProperty(XMLString.FO_MARGIN_RIGHT); + // 3. Header+footer dimensions + String sHeadHeight = "0cm"; + String sHeadSep = "0cm"; + String sFootHeight = "0cm"; + String sFootSep = "0cm"; + boolean bIncludeHead = false; + boolean bIncludeFoot = false; + // Look through all applied page layouts and use largest heights + Enumeration masters = ofr.getMasterPages().getStylesEnumeration(); + while (masters.hasMoreElements()) { + MasterPage master = (MasterPage) masters.nextElement(); + if (styleNames.containsName(getDisplayName(master.getName()))) { + PageLayout layout = ofr.getPageLayout(master.getPageLayoutName()); + if (layout!=null) { + if (layout.hasHeaderStyle()) { + String sThisHeadHeight = layout.getHeaderProperty(XMLString.FO_MIN_HEIGHT); + if (sThisHeadHeight!=null && Misc.isLessThan(sHeadHeight,sThisHeadHeight)) { + sHeadHeight = sThisHeadHeight; + } + String sThisHeadSep = layout.getHeaderProperty(XMLString.FO_MARGIN_BOTTOM); + if (sThisHeadSep!=null && Misc.isLessThan(sHeadSep,sThisHeadSep)) { + sHeadSep = sThisHeadSep; + } + bIncludeHead = true; + } + if (layout.hasFooterStyle()) { + String sThisFootHeight = layout.getFooterProperty(XMLString.FO_MIN_HEIGHT); + if (sThisFootHeight!=null && Misc.isLessThan(sFootHeight,sThisFootHeight)) { + sFootHeight = sThisFootHeight; + } + String sThisFootSep = layout.getFooterProperty(XMLString.FO_MARGIN_TOP); + if (sThisFootSep!=null && Misc.isLessThan(sFootSep,sThisFootSep)) { + sFootSep = sThisFootSep; + } + bIncludeFoot = true; + } + } + } + } + // Define 12pt as minimum height (the source may specify 0pt..) + if (bIncludeHead && Misc.isLessThan(sHeadHeight,"12pt")) { + sHeadHeight = "12pt"; + } + if (bIncludeFoot && Misc.isLessThan(sFootHeight,"12pt")) { + sFootHeight = "12pt"; + } + + String sFootSkip = Misc.add(sFootHeight,sFootSep); + + if (config.useGeometry()) { + // Set up options for geometry.sty + CSVList props = new CSVList(","); + if (!standardPaperSize(sPaperWidth,sPaperHeight)) { + props.addValue("paperwidth="+sPaperWidth); + props.addValue("paperheight="+sPaperHeight); + } + props.addValue("top="+sMarginTop); + props.addValue("bottom="+sMarginBottom); + props.addValue("left="+sMarginLeft); + props.addValue("right="+sMarginRight); + if (bIncludeHead) { + props.addValue("includehead"); + props.addValue("head="+sHeadHeight); + props.addValue("headsep="+sHeadSep); + } + else { + props.addValue("nohead"); + } + if (bIncludeFoot) { + props.addValue("includefoot"); + props.addValue("foot="+sFootHeight); + props.addValue("footskip="+sFootSkip); + } + else { + props.addValue("nofoot"); + } + // Use the package + pack.append("\\usepackage[").append(props.toString()).append("]{geometry}").nl(); + + } + else { + // Calculate text height and text width + String sTextHeight = Misc.sub(sPaperHeight,sMarginTop); + sTextHeight = Misc.sub(sTextHeight,sHeadHeight); + sTextHeight = Misc.sub(sTextHeight,sHeadSep); + sTextHeight = Misc.sub(sTextHeight,sFootSkip); + sTextHeight = Misc.sub(sTextHeight,sMarginBottom); + String sTextWidth = Misc.sub(sPaperWidth,sMarginLeft); + sTextWidth = Misc.sub(sTextWidth,sMarginRight); + + ldp.append("% Page layout (geometry)").nl(); + + // Page dimensions + if (!standardPaperSize(sPaperWidth,sPaperHeight)) { + ldp.append("\\setlength\\paperwidth{").append(sPaperWidth).append("}").nl() + .append("\\setlength\\paperheight{").append(sPaperHeight).append("}").nl(); + } + + // PDF page dimensions, only if hyperref.sty is not loaded + if (config.getBackend()==LaTeXConfig.PDFTEX && !config.useHyperref()) { + ldp.append("\\setlength\\pdfpagewidth{").append(sPaperWidth).append("}").nl() + .append("\\setlength\\pdfpageheight{").append(sPaperHeight).append("}").nl(); + } + + // Page starts in upper left corner of paper!! + ldp.append("\\setlength\\voffset{-1in}").nl() + .append("\\setlength\\hoffset{-1in}").nl(); + + // Margins + ldp.append("\\setlength\\topmargin{").append(sMarginTop).append("}").nl() + .append("\\setlength\\oddsidemargin{").append(sMarginLeft).append("}").nl(); + // Left margin for even (left) pages; only for mirrored page master + if ("mirrored".equals(mainPageLayout.getPageUsage())) { + ldp.append("\\setlength\\evensidemargin{").append(sMarginRight).append("}").nl(); + } + + // Text size (sets bottom and right margins indirectly) + ldp.append("\\setlength\\textheight{").append(sTextHeight).append("}").nl(); + ldp.append("\\setlength\\textwidth{").append(sTextWidth).append("}").nl(); + + // Header and footer + ldp.append("\\setlength\\footskip{").append(sFootSkip).append("}").nl(); + ldp.append("\\setlength\\headheight{").append(sHeadHeight).append("}").nl(); + ldp.append("\\setlength\\headsep{").append(sHeadSep).append("}").nl(); + } + + // Footnote rule + // TODO: Support alignment. + String sAdjustment = mainPageLayout.getFootnoteProperty(XMLString.STYLE_ADJUSTMENT); + String sBefore = mainPageLayout.getFootnoteProperty(XMLString.STYLE_DISTANCE_BEFORE_SEP); + if (sBefore==null) { sBefore = "1mm"; } + String sAfter = mainPageLayout.getFootnoteProperty(XMLString.STYLE_DISTANCE_AFTER_SEP); + if (sAfter==null) { sAfter = "1mm"; } + String sHeight = mainPageLayout.getFootnoteProperty(XMLString.STYLE_WIDTH); + if (sHeight==null) { sHeight = "0.2mm"; } + String sWidth = mainPageLayout.getFootnoteProperty(XMLString.STYLE_REL_WIDTH); + if (sWidth==null) { sWidth = "25%"; } + sWidth=Float.toString(Misc.getFloat(sWidth.substring(0,sWidth.length()-1),1)/100); + BeforeAfter baColor = new BeforeAfter(); + String sColor = mainPageLayout.getFootnoteProperty(XMLString.STYLE_COLOR); + palette.getColorCv().applyColor(sColor,false,baColor,new Context()); + + String sSkipFootins = Misc.add(sBefore,sHeight); + + ldp.append("% Footnote rule").nl() + .append("\\setlength{\\skip\\footins}{").append(sSkipFootins).append("}").nl() + .append("\\renewcommand\\footnoterule{\\vspace*{-").append(sHeight) + .append("}"); + if ("right".equals(sAdjustment)) { + ldp.append("\\setlength\\leftskip{0pt plus 1fil}\\setlength\\rightskip{0pt}"); + } + else if ("center".equals(sAdjustment)) { + ldp.append("\\setlength\\leftskip{0pt plus 1fil}\\setlength\\rightskip{0pt plus 1fil}"); + } + else { // default left + ldp.append("\\setlength\\leftskip{0pt}\\setlength\\rightskip{0pt plus 1fil}"); + } + ldp.append("\\noindent") + .append(baColor.getBefore()).append("\\rule{").append(sWidth) + .append("\\columnwidth}{").append(sHeight).append("}") + .append(baColor.getAfter()) + .append("\\vspace*{").append(sAfter).append("}}").nl(); + } + + private boolean standardPaperSize(String sWidth, String sHeight) { + if (standardPaperSize1(sWidth,sHeight)) { + return true; + } + else if (standardPaperSize1(sHeight,sWidth)) { + palette.addGlobalOption("landscape"); + return true; + } + return false; + } + + private boolean standardPaperSize1(String sWidth, String sHeight) { + // The list of known paper sizes in LaTeX's standard classes is rather short + if (compare(sWidth, "210mm", "0.5mm") && compare(sHeight, "297mm", "0.5mm")) { + palette.addGlobalOption("a4paper"); + return true; + } + else if (compare(sWidth, "148mm", "0.5mm") && compare(sHeight, "210mm", "0.5mm")) { + palette.addGlobalOption("a5paper"); + return true; + } + else if (compare(sWidth, "176mm", "0.5mm") && compare(sHeight, "250mm", "0.5mm")) { + palette.addGlobalOption("b5paper"); + return true; + } + else if (compare(sWidth, "8.5in", "0.02in") && compare(sHeight, "11in", "0.02in")) { + palette.addGlobalOption("letterpaper"); + return true; + } + else if (compare(sWidth, "8.5in", "0.02in") && compare(sHeight, "14in", "0.02in")) { + palette.addGlobalOption("legalpaper"); + return true; + } + else if (compare(sWidth, "7.25in", "0.02in") && compare(sHeight, "10.5in", "0.02in")) { + palette.addGlobalOption("executivepaper"); + return true; + } + return false; + } + + private boolean compare(String sLength1, String sLength2, String sTolerance) { + return Misc.isLessThan(Misc.abs(Misc.sub(sLength1,sLength2)),sTolerance); + } + + /* Helper: Get display name, or original name if it doesn't exist */ + private String getDisplayName(String sName) { + String sDisplayName = ofr.getMasterPages().getDisplayName(sName); + return sDisplayName!=null ? sDisplayName : sName; + } + + + +} diff --git a/source/java/writer2latex/latex/ParConverter.java b/source/java/writer2latex/latex/ParConverter.java new file mode 100644 index 0000000..d16e991 --- /dev/null +++ b/source/java/writer2latex/latex/ParConverter.java @@ -0,0 +1,546 @@ +/************************************************************************ + * + * ParConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +//import java.util.Hashtable; + +import org.w3c.dom.Element; +//import org.w3c.dom.Node; +//import org.w3c.dom.NodeList; + +import writer2latex.util.*; +import writer2latex.office.*; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; +//import writer2latex.latex.util.HeadingMap; +import writer2latex.latex.util.StyleMap; + +/* <p>This class converts OpenDocument paragraphs (<code>text:p</code>) and + * paragraph styles/formatting into LaTeX</p> + * <p>Export of formatting depends on the option "formatting":</p> + * <ul> + * <li><code>ignore_all</code> + * <li><code>ignore_most</code> + * <li><code>convert_basic</code> + * <li><code>convert_most</code> + * <li><code>convert_all</code> + * </ul> + * <p>TODO: Captions and {foot|end}notes should also use this class + */ +public class ParConverter extends StyleConverter { + + private boolean bNeedArrayBslash = false; + + /** <p>Constructs a new <code>ParConverter</code>.</p> + */ + public ParConverter(OfficeReader ofr, LaTeXConfig config, + ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bNeedArrayBslash) { + // centering and raggedright redefines \\, fix this + // Note: aviods nameclash with tabularx (arraybackslash) + // TODO: Should perhaps choose to load tabularx instead? + decl.append("\\makeatletter").nl() + .append("\\newcommand\\arraybslash{\\let\\\\\\@arraycr}").nl() + .append("\\makeatother").nl(); + } + + + if (config.formatting()>=LaTeXConfig.CONVERT_MOST) { + // We typeset with \raggedbottom since OOo doesn't use rubber lengths + // TODO: Maybe turn vertical spacing from OOo into rubber lengths? + decl.append("\\raggedbottom").nl(); + } + + if (config.formatting()>=LaTeXConfig.CONVERT_MOST) { + decl.append("% Paragraph styles").nl(); + // First default paragraph style + palette.getCharSc().applyDefaultFont(ofr.getDefaultParStyle(),decl); + super.appendDeclarations(pack,decl); + } + } + + /** + * <p> Process a text:p tag</p> + * @param node The text:h element node containing the heading + * @param ldp The <code>LaTeXDocumentPortion</code> to add LaTeX code to + * @param oc The current context + * @param bLastInBlock If this is true, the paragraph is the + * last one in a block, and we need no trailing blank line (eg. right before + * \end{enumerate}). + */ + public void handleParagraph(Element node, LaTeXDocumentPortion ldp, Context oc, boolean bLastInBlock) { + if (palette.getMathmlCv().handleDisplayEquation(node,ldp)) { return; } + + // Get the style name for this paragraph + String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + String sDisplayName = ofr.getParStyles().getDisplayName(sStyleName); + + + // Check for strict handling of styles + if (config.otherStyles()!=LaTeXConfig.ACCEPT && !config.getParStyleMap().contains(sDisplayName)) { + if (config.otherStyles()==LaTeXConfig.WARNING) { + System.err.println("Warning: A paragraph with style "+sDisplayName+" was ignored"); + } + else if (config.otherStyles()==LaTeXConfig.ERROR) { + ldp.append("% Error in source document: A paragraph with style ") + .append(palette.getI18n().convert(sDisplayName,false,oc.getLang())) + .append(" was ignored").nl(); + } + // Ignore this paragraph: + return; + } + + // Empty paragraphs are often (mis)used to achieve vertical spacing in WYSIWYG + // word processors. Hence we translate an empty paragraph to \bigskip. + // This also solves the problem that LaTeX ignores empty paragraphs, Writer doesn't. + // In a well-structured document, an empty paragraph is probably a mistake, + // hence the configuration can specify that it should be ignored. + // Note: Don't use \bigskip in tables (this can lead to strange results) + if (OfficeReader.isWhitespaceContent(node)) { + // Always add page break; other formatting is ignored + BeforeAfter baPage = new BeforeAfter(); + StyleWithProperties style = ofr.getParStyle(sStyleName); + palette.getPageSc().applyPageBreak(style,true,baPage); + if (!oc.isInTable()) { ldp.append(baPage.getBefore()); } + if (!config.ignoreEmptyParagraphs()) { + if (!oc.isInTable()) { + ldp.nl().append("\\bigskip").nl(); + } + else { + ldp.append("~").nl(); + } + if (!bLastInBlock) { ldp.nl(); } + } + if (!oc.isInTable()) { ldp.append(baPage.getAfter()); } + return; + } + + Context ic = (Context) oc.clone(); + + // Always push the font used + palette.getI18n().pushSpecialTable(palette.getCharSc().getFontName(ofr.getParStyle(sStyleName))); + + // Apply the style + BeforeAfter ba = new BeforeAfter(); + if (oc.isInTable()) { + applyCellParStyle(sStyleName,ba,ic,OfficeReader.getCharacterCount(node)==0,bLastInBlock); + } + else { + applyParStyle(sStyleName,ba,ic,OfficeReader.getCharacterCount(node)==0); + } + + // Do conversion + ldp.append(ba.getBefore()); + palette.getInlineCv().traverseInlineText(node,ldp,ic); + ldp.append(ba.getAfter()); + // Add a blank line except within verbatim and last in a block: + if (!bLastInBlock && !ic.isVerbatim() && !ic.isInSimpleTable()) { ldp.nl(); } + + // Flush any pending index marks, reference marks and floating frames + palette.getFieldCv().flushReferenceMarks(ldp,oc); + palette.getIndexCv().flushIndexMarks(ldp,oc); + palette.getDrawCv().flushFloatingFrames(ldp,oc); + + // pop the font name + palette.getI18n().popSpecialTable(); + } + + private void applyCellParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar, boolean bLastInBlock) { + // Paragraph formatting for paragraphs within table cells + // We always use simple par styles here + + // Add newline if *between* paragraphs + if (!bLastInBlock) { ba.add("","\n"); } + + if (context.isInSimpleTable()) { + if (config.formatting()!=LaTeXConfig.IGNORE_ALL) { + // only character formatting! + StyleWithProperties style = ofr.getParStyle(sName); + if (style!=null) { + palette.getI18n().applyLanguage(style,true,true,ba); + palette.getCharSc().applyFont(style,true,true,ba,context); + if (ba.getBefore().length()>0) { ba.add(" ",""); } + } + } + } + else if (bNoTextPar && (config.formatting()==LaTeXConfig.CONVERT_BASIC || config.formatting()==LaTeXConfig.IGNORE_MOST) ) { + // only alignment! + StyleWithProperties style = ofr.getParStyle(sName); + if (style!=null) { + // Apply hard formatting attributes + // Note: Left justified text is exported as full justified text! + palette.getPageSc().applyPageBreak(style,false,ba); + String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true); + if (bLastInBlock && context.isInLastTableColumn()) { // no grouping needed, but need to fix problem with \\ + if ("center".equals(sTextAlign)) { ba.add("\\centering\\arraybslash ",""); } + else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft\\arraybslash ",""); } + bNeedArrayBslash = true; + } + else if (bLastInBlock) { // no grouping needed + if ("center".equals(sTextAlign)) { ba.add("\\centering ",""); } + else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft ",""); } + } + else { + if ("center".equals(sTextAlign)) { ba.add("{\\centering ","\\par}"); } + else if ("end".equals(sTextAlign)) { ba.add("{\\raggedleft ","\\par}"); } + } + } + } + else { + // Export character formatting + alignment only + BeforeAfter baText = new BeforeAfter(); + + // Apply hard formatting attributes + // Note: Left justified text is exported as full justified text! + StyleWithProperties style = ofr.getParStyle(sName); + if (style!=null) { + String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true); + if (bLastInBlock && context.isInLastTableColumn()) { // no grouping needed, but need to fix problem with \\ + if ("center".equals(sTextAlign)) { ba.add("\\centering\\arraybslash ",""); } + else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft\\arraybslash ",""); } + bNeedArrayBslash = true; + } + else if (bLastInBlock) { // no \par needed + if ("center".equals(sTextAlign)) { ba.add("\\centering ",""); } + else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft ",""); } + } + else { + if ("center".equals(sTextAlign)) { ba.add("\\centering ","\\par"); } + else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft ","\\par"); } + } + palette.getI18n().applyLanguage(style,true,true,baText); + palette.getCharSc().applyFont(style,true,true,baText,context); + } + + // Assemble the bits. If there is any hard character formatting + // or alignment we must group the contents. + if (!baText.isEmpty() && !bLastInBlock) { ba.add("{","}"); } + ba.add(baText.getBefore(),baText.getAfter()); + if (baText.getBefore().length()>0) { ba.add(" ",""); } + } + + // Update context + StyleWithProperties style = ofr.getParStyle(sName); + if (style==null) { return; } + context.updateFormattingFromStyle(style); + context.setVerbatim(styleMap.getVerbatim(sName)); + } + + + /** <p>Use a paragraph style in LaTeX.</p> + * @param <code>sName</code> the name of the text style + * @param <code>ba</code> a <code>BeforeAfter</code> to put code into + * @param <code>context</code> the current context. This method will use and update the formatting context + * @param <code>bNoTextPar</code> true if this paragraph has no text content (hence character formatting is not needed) + */ + private void applyParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar) { + applyParStyle(sName,ba,context,bNoTextPar,true); + } + + private void applyParStyle(String sName, BeforeAfter ba, Context context, boolean bNoTextPar, boolean bBreakInside) { + // No style specified? + if (sName==null) { return; } + + if (context.isInSimpleTable()) { + if (config.formatting()!=LaTeXConfig.IGNORE_ALL) { + // only character formatting! + StyleWithProperties style = ofr.getParStyle(sName); + if (style!=null) { + palette.getI18n().applyLanguage(style,true,true,ba); + palette.getCharSc().applyFont(style,true,true,ba,context); + if (ba.getBefore().length()>0) { ba.add(" ",""); } + } + } + } + else if (bNoTextPar && (config.formatting()==LaTeXConfig.CONVERT_BASIC || config.formatting()==LaTeXConfig.IGNORE_MOST) ) { + // Always end with a line break + ba.add("","\n"); + // only alignment! + StyleWithProperties style = ofr.getParStyle(sName); + if (style!=null) { + // Apply hard formatting attributes + // Note: Left justified text is exported as full justified text! + palette.getPageSc().applyPageBreak(style,false,ba); + String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true); + if ("center".equals(sTextAlign)) { ba.add("{\\centering ","\\par}"); } + else if ("end".equals(sTextAlign)) { ba.add("{\\raggedleft ","\\par}"); } + } + } + else { + // Always end with a line break + ba.add("","\n"); + // Apply the style + if (!styleMap.contains(sName)) { createParStyle(sName); } + String sBefore = styleMap.getBefore(sName); + String sAfter = styleMap.getAfter(sName); + ba.add(sBefore,sAfter); + // Add line breaks inside? + if (bBreakInside && styleMap.getLineBreak(sName)) { + if (sBefore.length()>0) { ba.add("\n",""); } + if (sAfter.length()>0 && !"}".equals(sAfter)) { ba.add("","\n"); } + } + } + + // Update context + StyleWithProperties style = ofr.getParStyle(sName); + if (style==null) { return; } + context.updateFormattingFromStyle(style); + context.setVerbatim(styleMap.getVerbatim(sName)); + } + + /** <p>Convert a paragraph style to LaTeX. </p> + * <p>A soft style is declared in <code>styleDeclarations</code> as + * <code>\newenvironment...</code></p> + * <p>A hard style is used by applying LaTeX code directly</p> + * @param <code>sName</code> the OOo name of the style + */ + private void createParStyle(String sName) { + // A paragraph style should always be created relative to main context + Context context = (Context) palette.getMainContext().clone(); + // The style may already be declared in the configuration: + String sDisplayName = ofr.getParStyles().getDisplayName(sName); + StyleMap sm = config.getParStyleMap(); + if (sm.contains(sDisplayName)) { + styleMap.put(sName,sm.getBefore(sDisplayName),sm.getAfter(sDisplayName), + sm.getLineBreak(sDisplayName),sm.getVerbatim(sDisplayName)); + return; + } + // Does the style exist? + StyleWithProperties style = ofr.getParStyle(sName); + if (style==null) { + styleMap.put(sName,"",""); + return; + } + // Convert the style! + switch (config.formatting()) { + case LaTeXConfig.CONVERT_MOST: + if (style.isAutomatic()) { + createAutomaticParStyle(style,context); + return; + } + case LaTeXConfig.CONVERT_ALL: + createSoftParStyle(style,context); + return; + case LaTeXConfig.CONVERT_BASIC: + case LaTeXConfig.IGNORE_MOST: + createSimpleParStyle(style,context); + return; + case LaTeXConfig.IGNORE_ALL: + default: + styleMap.put(sName,"",""); + } + } + + private void createAutomaticParStyle(StyleWithProperties style, Context context) { + // Hard paragraph formatting from this style should be ignored + // (because the user wants to ignore hard paragraph formatting + // or there is a style map for the parent.) + BeforeAfter ba = new BeforeAfter(); + BeforeAfter baPar = new BeforeAfter(); + BeforeAfter baText = new BeforeAfter(); + + // Apply paragraph formatting from parent + // If parent is verbatim, this is all + String sParentName = style.getParentName(); + if (styleMap.getVerbatim(sParentName)) { + styleMap.put(style.getName(),styleMap.getBefore(sParentName),styleMap.getAfter(sParentName), + styleMap.getLineBreak(sParentName),styleMap.getVerbatim(sParentName)); + return; + } + applyParStyle(sParentName,baPar,context,false,false); + + // Apply hard formatting properties: + palette.getPageSc().applyPageBreak(style,false,ba); + palette.getI18n().applyLanguage(style,true,false,baText); + palette.getCharSc().applyFont(style,true,false,baText,context); + + // Assemble the bits. If there is any hard character formatting + // we must group the contents. + if (baPar.isEmpty() && !baText.isEmpty()) { ba.add("{","}"); } + else { ba.add(baPar.getBefore(),baPar.getAfter()); } + ba.add(baText.getBefore(),baText.getAfter()); + boolean bLineBreak = styleMap.getLineBreak(sParentName); + if (!bLineBreak && !baText.isEmpty()) { ba.add(" ",""); } + styleMap.put(style.getName(),ba.getBefore(),ba.getAfter(),bLineBreak,false); + } + + private void createSimpleParStyle(StyleWithProperties style, Context context) { + // Export character formatting + alignment only + if (style.isAutomatic() && config.getParStyleMap().contains(ofr.getParStyles().getDisplayName(style.getParentName()))) { + createAutomaticParStyle(style,context); + return; + } + + BeforeAfter ba = new BeforeAfter(); + BeforeAfter baText = new BeforeAfter(); + + // Apply hard formatting attributes + // Note: Left justified text is exported as full justified text! + palette.getPageSc().applyPageBreak(style,false,ba); + String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true); + if ("center".equals(sTextAlign)) { baText.add("\\centering","\\par"); } + else if ("end".equals(sTextAlign)) { baText.add("\\raggedleft","\\par"); } + palette.getI18n().applyLanguage(style,true,true,baText); + palette.getCharSc().applyFont(style,true,true,baText,context); + + // Assemble the bits. If there is any hard character formatting + // or alignment we must group the contents. + if (!baText.isEmpty()) { ba.add("{","}"); } + ba.add(baText.getBefore(),baText.getAfter()); + styleMap.put(style.getName(),ba.getBefore(),ba.getAfter()); + } + + private void createSoftParStyle(StyleWithProperties style, Context context) { + // This style should be converted to an enviroment, except if + // it's automatic and there is a config style map for the parent + if (style.isAutomatic() && config.getParStyleMap().contains(ofr.getParStyles().getDisplayName(style.getParentName()))) { + createAutomaticParStyle(style,context); + } + + BeforeAfter ba = new BeforeAfter(); + applyParProperties(style,ba); + ba.add("\\writerlistparindent\\writerlistleftskip",""); + palette.getI18n().applyLanguage(style,true,true,ba); + ba.add("\\leavevmode",""); + palette.getCharSc().applyNormalFont(ba); + palette.getCharSc().applyFont(style,true,true,ba,context); + ba.add("\\writerlistlabel",""); + ba.add("\\ignorespaces",""); + // Declare the paragraph style (\newenvironment) + String sTeXName = "style" + styleNames.getExportName(style.getDisplayName()); + styleMap.put(style.getName(),"\\begin{"+sTeXName+"}","\\end{"+sTeXName+"}"); + declarations.append("\\newenvironment{").append(sTeXName) + .append("}{").append(ba.getBefore()).append("}{") + .append(ba.getAfter()).append("}").nl(); + } + + // Remaining methods are private helpers + + /** <p>Apply line spacing from a style.</p> + * @param <code>style</code> the paragraph style to use + * @param <code>ba</code> a <code>BeforeAfter</code> to put code into + */ + private void applyLineSpacing(StyleWithProperties style, BeforeAfter ba) { + if (style==null) { return; } + String sLineHeight = style.getProperty(XMLString.FO_LINE_HEIGHT); + if (sLineHeight==null || !sLineHeight.endsWith("%")) { return; } + float fPercent=Misc.getFloat(sLineHeight.substring(0,sLineHeight.length()-1),100); + // Do not allow less that 120% (LaTeX default) + if (fPercent<120) { fPercent = 120; } + ba.add("\\renewcommand\\baselinestretch{"+fPercent/120+"}",""); + } + + /** <p>Helper: Create a horizontal border. Currently unused</p> + */ + /*private String createBorder(String sLeft, String sRight, String sTop, + String sHeight, String sColor) { + BeforeAfter baColor = new BeforeAfter(); + palette.getColorCv().applyColor(sColor,false,baColor, new Context()); + return "{\\setlength\\parindent{0pt}\\setlength\\leftskip{" + sLeft + "}" + + "\\setlength\\baselineskip{0pt}\\setlength\\parskip{" + sHeight + "}" + + baColor.getBefore() + + "\\rule{\\textwidth-" + sLeft + "-" + sRight + "}{" + sHeight + "}" + + baColor.getAfter() + + "\\par}"; + }*/ + + /** <p>Apply margin+alignment properties from a style.</p> + * @param <code>style</code> the paragraph style to use + * @param <code>ba</code> a <code>BeforeAfter</code> to put code into + */ + private void applyMargins(StyleWithProperties style, BeforeAfter ba) { + // Read padding/margin/indentation properties: + //String sPaddingTop = style.getAbsoluteLength(XMLString.FO_PADDING_TOP); + //String sPaddingBottom = style.getAbsoluteLength(XMLString.FO_PADDING_BOTTOM); + //String sPaddingLeft = style.getAbsoluteLength(XMLString.FO_PADDING_LEFT); + //String sPaddingRight = style.getAbsoluteLength(XMLString.FO_PADDING_RIGHT); + String sMarginTop = style.getAbsoluteLength(XMLString.FO_MARGIN_TOP); + String sMarginBottom = style.getAbsoluteLength(XMLString.FO_MARGIN_BOTTOM); + String sMarginLeft = style.getAbsoluteLength(XMLString.FO_MARGIN_LEFT); + String sMarginRight = style.getAbsoluteLength(XMLString.FO_MARGIN_RIGHT); + String sTextIndent; + if ("true".equals(style.getProperty(XMLString.STYLE_AUTO_TEXT_INDENT))) { + sTextIndent = "2em"; + } + else { + sTextIndent = style.getAbsoluteLength(XMLString.FO_TEXT_INDENT); + } + // Read alignment properties: + boolean bRaggedLeft = false; // add 1fil to \leftskip + boolean bRaggedRight = false; // add 1fil to \rightskip + boolean bParFill = false; // add 1fil to \parfillskip + String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN); + if ("center".equals(sTextAlign)) { + bRaggedLeft = true; bRaggedRight = true; // centered paragraph + } + else if ("start".equals(sTextAlign)) { + bRaggedRight = true; bParFill = true; // left aligned paragraph + } + else if ("end".equals(sTextAlign)) { + bRaggedLeft = true; // right aligned paragraph + } + else if (!"justify".equals(style.getProperty(XMLString.FO_TEXT_ALIGN_LAST))) { + bParFill = true; // justified paragraph with ragged last line + } + // Create formatting: + String sRubberMarginTop = Misc.multiply("10%",sMarginTop); + if (Misc.length2px(sRubberMarginTop).equals("0")) { sRubberMarginTop="1pt"; } + String sRubberMarginBottom = Misc.multiply("10%",sMarginBottom); + if (Misc.length2px(sRubberMarginBottom).equals("0")) { sRubberMarginBottom="1pt"; } + ba.add("\\setlength\\leftskip{"+sMarginLeft+(bRaggedLeft?" plus 1fil":"")+"}",""); + ba.add("\\setlength\\rightskip{"+sMarginRight+(bRaggedRight?" plus 1fil":"")+"}",""); + ba.add("\\setlength\\parindent{"+sTextIndent+"}",""); + ba.add("\\setlength\\parfillskip{"+(bParFill?"0pt plus 1fil":"0pt")+"}",""); + ba.add("\\setlength\\parskip{"+sMarginTop+" plus "+sRubberMarginTop+"}", + "\\unskip\\vspace{"+sMarginBottom+" plus "+sRubberMarginBottom+"}"); + } + + public void applyAlignment(StyleWithProperties style, boolean bIsSimple, boolean bInherit, BeforeAfter ba) { + if (bIsSimple || style==null) { return; } + String sTextAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,bInherit); + if ("center".equals(sTextAlign)) { ba.add("\\centering",""); } + else if ("start".equals(sTextAlign)) { ba.add("\\raggedright",""); } + else if ("end".equals(sTextAlign)) { ba.add("\\raggedleft",""); } + } + + + /** <p>Apply all paragraph properties.</p> + * @param <code>style</code> the paragraph style to use + * @param <code>ba</code> a <code>BeforeAfter</code> to put code into + */ + private void applyParProperties(StyleWithProperties style, BeforeAfter ba) { + palette.getPageSc().applyPageBreak(style,true,ba); + ba.add("","\\par"); + applyLineSpacing(style,ba); + applyMargins(style,ba); + } + +} diff --git a/source/java/writer2latex/latex/SectionConverter.java b/source/java/writer2latex/latex/SectionConverter.java new file mode 100644 index 0000000..f695d9d --- /dev/null +++ b/source/java/writer2latex/latex/SectionConverter.java @@ -0,0 +1,135 @@ +/************************************************************************ + * + * SectionConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-17) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.util.*; +import writer2latex.office.*; +import writer2latex.latex.i18n.ClassicI18n; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; + +/** <p>This class creates LaTeX code from OOo sections. + * <p>Sections are converted to multicols environments using <code>multicol.sty</code> + */ +public class SectionConverter extends ConverterHelper { + + // Do we need multicols.sty? + private boolean bNeedMulticol = false; + + // Filenames for external sections + private ExportNameCollection fileNames = new ExportNameCollection(true); + + /** <p>Constructs a new <code>SectionStyleConverter</code>.</p> + */ + public SectionConverter(OfficeReader ofr, LaTeXConfig config, + ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bNeedMulticol) { pack.append("\\usepackage{multicol}").nl(); } + } + + /** <p> Process a section (text:section tag)</p> + * @param node The element containing the section + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleSection(Element node, LaTeXDocumentPortion ldp, Context oc) { + // We may need a hyperlink target, add this first + palette.getFieldCv().addTarget(node,"|region",ldp); + + // Create new document, if desired + String sFileName = null; + Element source = Misc.getChildByTagName(node,XMLString.TEXT_SECTION_SOURCE); + if (config.splitLinkedSections() && source!=null) { + sFileName = fileNames.getExportName(Misc.removeExtension(Misc.urlDecode(source.getAttribute(XMLString.XLINK_HREF)))); + } + else if (config.splitToplevelSections() && isToplevel(node)) { + //sFileName = fileNames.getExportName(palette.getOutFileName()+node.getAttribute(XMLString.TEXT_NAME)); + sFileName = fileNames.getExportName(node.getAttribute(XMLString.TEXT_NAME)); + } + + LaTeXDocumentPortion sectionLdp = ldp; + if (sFileName!=null) { + LaTeXDocument newDoc = new LaTeXDocument(sFileName,config.getWrapLinesAfter()); + if (config.getBackend()!=LaTeXConfig.XETEX) { + newDoc.setEncoding(ClassicI18n.writeJavaEncoding(config.getInputencoding())); + } + else { + newDoc.setEncoding("UTF-8"); + } + palette.addDocument(newDoc); + sectionLdp = newDoc.getContents(); + } + + // Apply the style + String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME); + BeforeAfter ba = new BeforeAfter(); + Context ic = (Context) oc.clone(); + applySectionStyle(sStyleName,ba,ic); + + // Do conversion + ldp.append(ba.getBefore()); + if (sFileName!=null) { + ldp.append("\\input{").append(sFileName).append("}").nl(); + } + palette.getBlockCv().traverseBlockText(node,sectionLdp,ic); + if (sectionLdp!=ldp) { sectionLdp.append("\\endinput").nl(); } + ldp.append(ba.getAfter()); + } + + // Create multicols environment as needed + private void applySectionStyle(String sStyleName, BeforeAfter ba, Context context) { + StyleWithProperties style = ofr.getSectionStyle(sStyleName); + // Don't nest multicols and require at least 2 columns + if (context.isInMulticols() || style==null || style.getColCount()<2) { return; } + int nCols = style.getColCount(); + bNeedMulticol = true; + context.setInMulticols(true); + ba.add("\\begin{multicols}{"+(nCols>10 ? 10 : nCols)+"}\n", "\\end{multicols}\n"); + } + + // return true if this node is *not* contained in a text:section element + private boolean isToplevel(Node node) { + Node parent = node.getParentNode(); + if (XMLString.TEXT_SECTION.equals(parent.getNodeName())) { + return false; + } + else if (XMLString.OFFICE_BODY.equals(parent.getNodeName())) { + return true; + } + return isToplevel(parent); + } + + + +} diff --git a/source/java/writer2latex/latex/StarMathConverter.java b/source/java/writer2latex/latex/StarMathConverter.java new file mode 100644 index 0000000..2eb6a50 --- /dev/null +++ b/source/java/writer2latex/latex/StarMathConverter.java @@ -0,0 +1,1618 @@ +/************************************************************************ + * + * StarMathConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * Version 1.0 (2009-02-17) + * + * All Rights Reserved. + */ + +package writer2latex.latex; + +import java.util.Hashtable; +import writer2latex.util.*; +import writer2latex.latex.i18n.ClassicI18n; +import writer2latex.latex.i18n.I18n; +import writer2latex.latex.LaTeXDocumentPortion; + +/* TO DO: + * better font handling, currently nested font attributes (eg. italic font fixed) don't work + * text inherits font from text surrounding formula - should this be changed? + * implement implicit left alignment of expressions starting with text + * improve subsup: \sideset should possibly be replaced by \multiscripts + * \multiscripts should only be used when absolutely neccessary + * maybe \leftidx should also be used sometimes + * alignment: how to make stack{{alignr x} % xxx} work?? + * alignment: fractions and binoms? + * ... + */ + +////////////////////////////////////////////////////////////////// +/* The converted formula requires LaTeX2e with the packages + * amsmath.sty, amssymb.sty, amsfonts.sty and (optionally) color.sty. + * Also the following control sequences must be defined for starmath symbols + * and constructions that are not provided by standard LaTeX2e+amsmath: + * \defeq, \lambdabar, \ddotsup, \multimapdotbothA, \multimapdotbothB, + * \llbracket, \rrbracket, \oiint, \oiiint, + * \mathoverstrike, \wideslash, \widebslash, \boldsubformula, + * \normalsubformula. + * These should be declared in the preamble. + * Some are defined in the packages stmaryrd.sty, pxfonts.sty, txfonts.sty, + * but fallback definitions are provided in ooomath.sty if these packages are + * not used. More info in the documentation on ooomath.sty. + */ + +////////////////////////////////////////////////////////////////// +// Helper Classes + +// Some Character classifications +class CharClasses{ + + static final char[] aDelimiterTable = + { ' ', '\t' , '\n', '\r', '+', '-', '*', '/', '=', '#', + '%', '\\', '"', '~', '`', '>', '<', '&', '|', '(', + ')', '{', '}', '[', ']', '^', '_', '\0'}; + + static boolean isDelimiter(char cChar){ + // return true iff cChar is '\0' or a delimiter + if (cChar=='\0') return false; + int i=0; + for (i=0;aDelimiterTable[i]!='\0';i++) + if (aDelimiterTable[i]==cChar) + break; + return aDelimiterTable[i]!='\0'; + } + + static boolean isEndOrLineEnd(char cChar){ + switch (cChar){ + case '\0': + case '\n': + case '\r': + return true; + default: + return false; + } + } + + static boolean isDigitOrDot(char cChar){ + return (cChar>='0' && cChar<='9') || cChar=='.' || cChar==','; + } +} + +// enumerate Tokens +class Token{ + public static final Token END=new Token(); + public static final Token LGROUP=new Token(); + public static final Token RGROUP=new Token(); + public static final Token LPARENT=new Token(); + public static final Token RPARENT=new Token(); + public static final Token LBRACKET=new Token(); + public static final Token RBRACKET=new Token(); + public static final Token PLUS=new Token(); + public static final Token MINUS=new Token(); + public static final Token MULTIPLY=new Token(); + public static final Token DIVIDEBY=new Token(); + public static final Token ASSIGN=new Token(); + public static final Token POUND=new Token(); + public static final Token SPECIAL=new Token(); + public static final Token SLASH=new Token(); + public static final Token BACKSLASH=new Token(); + public static final Token BLANK=new Token(); + public static final Token SBLANK=new Token(); + public static final Token RSUB=new Token(); + public static final Token RSUP=new Token(); + public static final Token CSUB=new Token(); + public static final Token CSUP=new Token(); + public static final Token LSUB=new Token(); + public static final Token LSUP=new Token(); + public static final Token GT=new Token(); + public static final Token LT=new Token(); + public static final Token AND=new Token(); + public static final Token OR=new Token(); + public static final Token INTERSECT=new Token(); + public static final Token UNION=new Token(); + public static final Token NEWLINE=new Token(); + public static final Token BINOM=new Token(); + public static final Token FROM=new Token(); + public static final Token TO=new Token(); + public static final Token INT=new Token(); + public static final Token SUM=new Token(); + public static final Token OPER=new Token(); + public static final Token ABS=new Token(); + public static final Token SQRT=new Token(); + public static final Token FACT=new Token(); + public static final Token NROOT=new Token(); + public static final Token OVER=new Token(); + public static final Token TIMES=new Token(); + public static final Token GE=new Token(); + public static final Token LE=new Token(); + public static final Token GG=new Token(); + public static final Token LL=new Token(); + public static final Token DOTSAXIS=new Token(); + public static final Token DOTSLOW=new Token(); + public static final Token DOTSVERT=new Token(); + public static final Token DOTSDIAG=new Token(); + public static final Token DOTSUP=new Token(); + public static final Token DOTSDOWN=new Token(); + public static final Token ACUTE=new Token(); + public static final Token BAR=new Token(); + public static final Token BREVE=new Token(); + public static final Token CHECK=new Token(); + public static final Token CIRCLE=new Token(); + public static final Token DOT=new Token(); + public static final Token DDOT=new Token(); + public static final Token DDDOT=new Token(); + public static final Token GRAVE=new Token(); + public static final Token HAT=new Token(); + public static final Token TILDE=new Token(); + public static final Token VEC=new Token(); + public static final Token UNDERLINE=new Token(); + public static final Token OVERLINE=new Token(); + public static final Token OVERSTRIKE=new Token(); + public static final Token ITALIC=new Token(); + public static final Token NITALIC=new Token(); + public static final Token BOLD=new Token(); + public static final Token NBOLD=new Token(); + public static final Token PHANTOM=new Token(); + public static final Token FONT=new Token(); + public static final Token SIZE=new Token(); + public static final Token COLOR=new Token(); + public static final Token ALIGNL=new Token(); + public static final Token ALIGNC=new Token(); + public static final Token ALIGNR=new Token(); + public static final Token LEFT=new Token(); + public static final Token RIGHT=new Token(); + public static final Token LANGLE=new Token(); + public static final Token LBRACE=new Token(); + public static final Token LLINE=new Token(); + public static final Token LDLINE=new Token(); + public static final Token LCEIL=new Token(); + public static final Token LFLOOR=new Token(); + public static final Token NONE=new Token(); + public static final Token MLINE=new Token(); + public static final Token RANGLE=new Token(); + public static final Token RBRACE=new Token(); + public static final Token RLINE=new Token(); + public static final Token RDLINE=new Token(); + public static final Token RCEIL=new Token(); + public static final Token RFLOOR=new Token(); + public static final Token SIN=new Token(); + public static final Token COS=new Token(); + public static final Token TAN=new Token(); + public static final Token COT=new Token(); + public static final Token FUNC=new Token(); + public static final Token STACK=new Token(); + public static final Token MATRIX=new Token(); + public static final Token DPOUND=new Token(); + public static final Token PLACE=new Token(); + public static final Token TEXT=new Token(); + public static final Token NUMBER=new Token(); + public static final Token CHARACTER=new Token(); + public static final Token IDENT=new Token(); + public static final Token NEQ=new Token(); + public static final Token EQUIV=new Token(); + public static final Token DEF=new Token(); + public static final Token PROP=new Token(); + public static final Token SIM=new Token(); + public static final Token SIMEQ=new Token(); + public static final Token APPROX=new Token(); + public static final Token PARALLEL=new Token(); + public static final Token ORTHO=new Token(); + public static final Token IN=new Token(); + public static final Token NOTIN=new Token(); + public static final Token SUBSET=new Token(); + public static final Token SUBSETEQ=new Token(); + public static final Token SUPSET=new Token(); + public static final Token SUPSETEQ=new Token(); + public static final Token PLUSMINUS=new Token(); + public static final Token MINUSPLUS=new Token(); + public static final Token OPLUS=new Token(); + public static final Token OMINUS=new Token(); + public static final Token DIV=new Token(); + public static final Token OTIMES=new Token(); + public static final Token ODIVIDE=new Token(); + public static final Token TRANSL=new Token(); + public static final Token TRANSR=new Token(); + public static final Token IINT=new Token(); + public static final Token IIINT=new Token(); + public static final Token LINT=new Token(); + public static final Token LLINT=new Token(); + public static final Token LLLINT=new Token(); + public static final Token PROD=new Token(); + public static final Token COPROD=new Token(); + public static final Token FORALL=new Token(); + public static final Token EXISTS=new Token(); + public static final Token LIM=new Token(); + public static final Token NABLA=new Token(); + public static final Token TOWARD=new Token(); + public static final Token SINH=new Token(); + public static final Token COSH=new Token(); + public static final Token TANH=new Token(); + public static final Token COTH=new Token(); + public static final Token ASIN=new Token(); + public static final Token ACOS=new Token(); + public static final Token ATAN=new Token(); + public static final Token LN=new Token(); + public static final Token LOG=new Token(); + public static final Token UOPER=new Token(); + public static final Token BOPER=new Token(); + public static final Token BLACK=new Token(); + public static final Token WHITE=new Token(); + public static final Token RED=new Token(); + public static final Token GREEN=new Token(); + public static final Token BLUE=new Token(); + public static final Token CYAN=new Token(); + public static final Token MAGENTA=new Token(); + public static final Token YELLOW=new Token(); + public static final Token FIXED=new Token(); + public static final Token SANS=new Token(); + public static final Token SERIF=new Token(); + public static final Token POINT=new Token(); + public static final Token ASINH=new Token(); + public static final Token ACOSH=new Token(); + public static final Token ATANH=new Token(); + public static final Token ACOTH=new Token(); + public static final Token ACOT=new Token(); + public static final Token EXP=new Token(); + public static final Token CDOT=new Token(); + public static final Token ODOT=new Token(); + public static final Token LESLANT=new Token(); + public static final Token GESLANT=new Token(); + public static final Token NSUBSET=new Token(); + public static final Token NSUPSET=new Token(); + public static final Token NSUBSETEQ=new Token(); + public static final Token NSUPSETEQ=new Token(); + public static final Token PARTIAL=new Token(); + public static final Token NEG=new Token(); + public static final Token NI=new Token(); + public static final Token BACKEPSILON=new Token(); + public static final Token ALEPH=new Token(); + public static final Token IM=new Token(); + public static final Token RE=new Token(); + public static final Token WP=new Token(); + public static final Token EMPTYSET=new Token(); + public static final Token INFINITY=new Token(); + public static final Token ESCAPE=new Token(); + public static final Token LIMSUP=new Token(); + public static final Token LIMINF=new Token(); + public static final Token NDIVIDES=new Token(); + public static final Token DRARROW=new Token(); + public static final Token DLARROW=new Token(); + public static final Token DLRARROW=new Token(); + public static final Token UNDERBRACE=new Token(); + public static final Token OVERBRACE=new Token(); + public static final Token CIRC=new Token(); + //public static final Token TOP=new Token(); + public static final Token HBAR=new Token(); + public static final Token LAMBDABAR=new Token(); + public static final Token LEFTARROW=new Token(); + public static final Token RIGHTARROW=new Token(); + public static final Token UPARROW=new Token(); + public static final Token DOWNARROW=new Token(); + public static final Token DIVIDES=new Token(); + public static final Token SETN=new Token(); + public static final Token SETZ=new Token(); + public static final Token SETQ=new Token(); + public static final Token SETR=new Token(); + public static final Token SETC=new Token(); + public static final Token WIDEVEC=new Token(); + public static final Token WIDETILDE=new Token(); + public static final Token WIDEHAT=new Token(); + public static final Token WIDESLASH=new Token(); + public static final Token WIDEBACKSLASH=new Token(); + public static final Token LDBRACKET=new Token(); + public static final Token RDBRACKET=new Token(); + public static final Token UNKNOWN=new Token(); +} + +// enumerate Token groups +class TGroup{ + public static final TGroup NONE=new TGroup(); + public static final TGroup OPER=new TGroup(); + public static final TGroup RELATION=new TGroup(); + public static final TGroup SUM=new TGroup(); + public static final TGroup PRODUCT=new TGroup(); + public static final TGroup UNOPER=new TGroup(); + public static final TGroup POWER =new TGroup(); + public static final TGroup ATTRIBUT=new TGroup(); + public static final TGroup ALIGN =new TGroup(); + public static final TGroup FUNCTION=new TGroup(); + public static final TGroup BLANK =new TGroup(); + public static final TGroup LBRACES=new TGroup(); + public static final TGroup RBRACES=new TGroup(); + public static final TGroup COLOR =new TGroup(); + public static final TGroup FONT=new TGroup(); + public static final TGroup STANDALONE=new TGroup(); + public static final TGroup LIMIT=new TGroup(); + public static final TGroup FONTATTR=new TGroup(); +} + +// Token tables +class SmTokenTableEntry{ // This is simply a struct + String sIdent; + Token eType; + String sLaTeX; + TGroup eGroup1, eGroup2; + int nLevel; + + SmTokenTableEntry(String sIdent, Token eType, String sLaTeX, + TGroup eGroup1, TGroup eGroup2, int nLevel){ + this.sIdent=sIdent; + this.eType=eType; + this.sLaTeX=sLaTeX; + this.eGroup1=eGroup1; + this.eGroup2=eGroup2; + this.nLevel=nLevel; + } + + SmTokenTableEntry(String sIdent, Token eType, String sLaTeX, + TGroup eGroup1, int nLevel){ + this(sIdent,eType,sLaTeX,eGroup1,TGroup.NONE,nLevel); + } + + SmTokenTableEntry(String sIdent, Token eType, String sLaTeX, + int nLevel){ + this(sIdent,eType,sLaTeX,TGroup.NONE,TGroup.NONE,nLevel); + } +} + +class SmTokenTable{ + private SmTokenTableEntry[] table; + + SmTokenTable(SmTokenTableEntry[] table){this.table=table;} + + boolean lookup(String sIdent, boolean bIgnoreCase, SmToken token){ + for (int i=0; i<table.length; i++){ + if (bIgnoreCase ? table[i].sIdent.equalsIgnoreCase(sIdent) : table[i].sIdent.equals(sIdent)){ + token.assign(table[i].eType, table[i].sLaTeX, table[i].eGroup1, table[i].eGroup2, table[i].nLevel); + return true; + } + } + return false; + } + + static final SmTokenTableEntry[] keywords= + {new SmTokenTableEntry( "Im",Token.IM, "\\Im ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "Re" , Token.RE, "\\Re ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "abs", Token.ABS, "", TGroup.UNOPER, 5), + new SmTokenTableEntry( "arcosh", Token.ACOSH, "\\operatorname{arcosh}", TGroup.FUNCTION, 5 ), + new SmTokenTableEntry( "arcoth", Token.ACOTH, "\\operatorname{arsinh}", TGroup.FUNCTION, 5 ), + new SmTokenTableEntry( "acute", Token.ACUTE, "\\acute", TGroup.ATTRIBUT, 5 ), + new SmTokenTableEntry( "aleph" , Token.ALEPH, "\\aleph ", TGroup.STANDALONE, 5 ), + new SmTokenTableEntry( "alignc", Token.ALIGNC, "", TGroup.ALIGN, 0), + new SmTokenTableEntry( "alignl", Token.ALIGNL, "", TGroup.ALIGN, 0), + new SmTokenTableEntry( "alignr", Token.ALIGNR, "", TGroup.ALIGN, 0), + new SmTokenTableEntry( "and", Token.AND, "\\wedge ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "approx", Token.APPROX, "\\approx ", TGroup.RELATION, 0), + new SmTokenTableEntry( "arccos", Token.ACOS, "\\arccos ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "arccot", Token.ACOT, "\\arccot ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "arcsin", Token.ASIN, "\\arcsin ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "arctan", Token.ATAN, "\\arctan ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "arsinh", Token.ASINH, "\\operatorname{arsinh}", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "artanh", Token.ATANH, "\\operatorname{artanh}", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "backepsilon" , Token.BACKEPSILON, "\\backepsilon ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "bar", Token.BAR, "\\bar", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "binom", Token.BINOM, "" , 5 ), + new SmTokenTableEntry( "black", Token.BLACK, "black", TGroup.COLOR, 0), + new SmTokenTableEntry( "blue", Token.BLUE, "blue", TGroup.COLOR, 0), + new SmTokenTableEntry( "bold", Token.BOLD, "\\boldsubformula", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "boper", Token.BOPER, "", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "breve", Token.BREVE, "\\breve", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "bslash", Token.BACKSLASH, "\\backslash ", TGroup.PRODUCT, 0 ), + new SmTokenTableEntry( "cdot", Token.CDOT, "\\cdot ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "check", Token.CHECK, "\\check", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "circ" , Token.CIRC, "\\circ ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "circle", Token.CIRCLE, "\\overset{\\circ}", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "color", Token.COLOR, "", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "coprod", Token.COPROD, "\\coprod ", TGroup.OPER, 5), + new SmTokenTableEntry( "cos", Token.COS, "\\cos ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "cosh", Token.COSH, "\\cosh ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "cot", Token.COT, "\\cot ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "coth", Token.COTH, "\\coth ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "csub", Token.CSUB, "", TGroup.POWER, 0), + new SmTokenTableEntry( "csup", Token.CSUP, "", TGroup.POWER, 0), + new SmTokenTableEntry( "cyan", Token.CYAN, "cyan", TGroup.COLOR, 0), + new SmTokenTableEntry( "dddot", Token.DDDOT, "\\dddot", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "ddot", Token.DDOT, "\\ddot", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "def", Token.DEF, "\\defeq ", TGroup.RELATION, 0), + new SmTokenTableEntry( "div", Token.DIV, "\\div ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "divides", Token.DIVIDES, "|", TGroup.RELATION, 0), + new SmTokenTableEntry( "dlarrow" , Token.DLARROW, "\\Leftarrow ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "dlrarrow" , Token.DLRARROW, "\\Leftrightarrow ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "dot", Token.DOT, "\\dot", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "dotsaxis", Token.DOTSAXIS, "\\cdots ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "dotsdiag", Token.DOTSDIAG, "\\ddots ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "dotsdown", Token.DOTSDOWN, "\\ddots ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "dotslow", Token.DOTSLOW, "\\ldots ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "dotsup", Token.DOTSUP, "\\ddotsup ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "dotsvert", Token.DOTSVERT, "\\vdots ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "downarrow" , Token.DOWNARROW, "\\downarrow ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "drarrow" , Token.DRARROW, "\\Rightarrow ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "emptyset" , Token.EMPTYSET, "\\emptyset ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "equiv", Token.EQUIV, "\\equiv ", TGroup.RELATION, 0), + new SmTokenTableEntry( "exists", Token.EXISTS, "\\exists ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "exp", Token.EXP, "\\exp ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "fact", Token.FACT, "!", TGroup.UNOPER, 5), + new SmTokenTableEntry( "fixed", Token.FIXED, "\\mathtt", TGroup.FONT, 0), + new SmTokenTableEntry( "font", Token.FONT, "", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "forall", Token.FORALL, "\\forall ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "from", Token.FROM, "", TGroup.LIMIT, 0), + new SmTokenTableEntry( "func", Token.FUNC, "", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "ge", Token.GE, "\\geq ", TGroup.RELATION, 0), + new SmTokenTableEntry( "geslant", Token.GESLANT, "\\geqslant ", TGroup.RELATION, 0 ), + new SmTokenTableEntry( "gg", Token.GG, "\\gg ", TGroup.RELATION, 0), + new SmTokenTableEntry( "grave", Token.GRAVE, "\\grave", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "green", Token.GREEN, "green", TGroup.COLOR, 0), + new SmTokenTableEntry( "gt", Token.GT, ">", TGroup.RELATION, 0), + new SmTokenTableEntry( "hat", Token.HAT, "\\hat", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "hbar" , Token.HBAR, "\\hbar ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "iiint", Token.IIINT, "\\iiint ", TGroup.OPER, 5), + new SmTokenTableEntry( "iint", Token.IINT, "\\iint ", TGroup.OPER, 5), + new SmTokenTableEntry( "in", Token.IN, "\\in ", TGroup.RELATION, 0), + new SmTokenTableEntry( "infinity" , Token.INFINITY, "\\infty ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "infty" , Token.INFINITY, "\\infty ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "int", Token.INT, "\\int ", TGroup.OPER, 5), + new SmTokenTableEntry( "intersection", Token.INTERSECT, "\\cap ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "ital", Token.ITALIC, "\\normalsubformula", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "italic", Token.ITALIC, "\\normalsubformula", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "lambdabar" , Token.LAMBDABAR, "\\lambdabar ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "langle", Token.LANGLE, "\\langle ", TGroup.LBRACES, 5), + new SmTokenTableEntry( "lbrace", Token.LBRACE, "\\{", TGroup.LBRACES, 5), + new SmTokenTableEntry( "lceil", Token.LCEIL, "\\lceil ", TGroup.LBRACES, 5), + new SmTokenTableEntry( "ldbracket", Token.LDBRACKET, "\\llbracket ", TGroup.LBRACES, 5), + new SmTokenTableEntry( "ldline", Token.LDLINE, "\\|", TGroup.LBRACES, 5), + new SmTokenTableEntry( "le", Token.LE, "\\leq ", TGroup.RELATION, 0), + new SmTokenTableEntry( "left", Token.LEFT, "", 5), + new SmTokenTableEntry( "leftarrow" , Token.LEFTARROW, "\\leftarrow ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "leslant", Token.LESLANT, "\\leqslant ", TGroup.RELATION, 0 ), + new SmTokenTableEntry( "lfloor", Token.LFLOOR, "\\lfloor ", TGroup.LBRACES, 5), + new SmTokenTableEntry( "lim", Token.LIM, "\\lim ", TGroup.OPER, 5), + new SmTokenTableEntry( "liminf", Token.LIMINF, "\\liminf ", TGroup.OPER, 5), + new SmTokenTableEntry( "limsup", Token.LIMSUP, "\\limsup ", TGroup.OPER, 5), + new SmTokenTableEntry( "lint", Token.LINT, "\\oint ", TGroup.OPER, 5), + new SmTokenTableEntry( "ll", Token.LL, "\\ll ", TGroup.RELATION, 0), + new SmTokenTableEntry( "lline", Token.LLINE, "|", TGroup.LBRACES, 5), + new SmTokenTableEntry( "llint", Token.LLINT, "\\oiint ", TGroup.OPER, 5), + new SmTokenTableEntry( "lllint", Token.LLLINT, "\\oiiint ", TGroup.OPER, 5), + new SmTokenTableEntry( "ln", Token.LN, "\\ln ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "log", Token.LOG, "\\log ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "lsub", Token.LSUB, "", TGroup.POWER, 0), + new SmTokenTableEntry( "lsup", Token.LSUP, "", TGroup.POWER, 0), + new SmTokenTableEntry( "lt", Token.LT, "<", TGroup.RELATION, 0), + new SmTokenTableEntry( "magenta", Token.MAGENTA, "magenta", TGroup.COLOR, 0), + new SmTokenTableEntry( "matrix", Token.MATRIX, "", 5), + new SmTokenTableEntry( "minusplus", Token.MINUSPLUS, "\\mp ", TGroup.UNOPER, TGroup.SUM, 5), + new SmTokenTableEntry( "mline", Token.MLINE, "", 0), + new SmTokenTableEntry( "nabla", Token.NABLA, "\\nabla ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "nbold", Token.NBOLD, "\\normalsubformula", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "ndivides", Token.NDIVIDES, "\\nmid ", TGroup.RELATION, 0), + new SmTokenTableEntry( "neg", Token.NEG, "\\neg ", TGroup.UNOPER, 5 ), + new SmTokenTableEntry( "neq", Token.NEQ, "\\neq ", TGroup.RELATION, 0), + new SmTokenTableEntry( "newline", Token.NEWLINE, "", 0), + new SmTokenTableEntry( "ni", Token.NI, "\\ni ", TGroup.RELATION, 0), + new SmTokenTableEntry( "nitalic", Token.NITALIC, "\\mathrm", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "none", Token.NONE, ".", TGroup.LBRACES, TGroup.RBRACES, 0), + new SmTokenTableEntry( "notin", Token.NOTIN, "\\notin ", TGroup.RELATION, 0), + new SmTokenTableEntry( "nsubset", Token.NSUBSET, "\\not\\subset ", TGroup.RELATION, 0 ), + new SmTokenTableEntry( "nsupset", Token.NSUPSET, "\\not\\supset ", TGroup.RELATION, 0 ), + new SmTokenTableEntry( "nsubseteq", Token.NSUBSETEQ, "\\nsubseteq ", TGroup.RELATION, 0 ), + new SmTokenTableEntry( "nsupseteq", Token.NSUPSETEQ, "\\nsupseteq ", TGroup.RELATION, 0 ), + new SmTokenTableEntry( "nroot", Token.NROOT, "", TGroup.UNOPER, 5), + new SmTokenTableEntry( "odivide", Token.ODIVIDE, "\\oslash ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "odot", Token.ODOT, "\\odot ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "ominus", Token.OMINUS, "\\ominus ", TGroup.SUM, 0), + new SmTokenTableEntry( "oper", Token.OPER, "", TGroup.OPER, 5), + new SmTokenTableEntry( "oplus", Token.OPLUS, "\\oplus ", TGroup.SUM, 0), + new SmTokenTableEntry( "or", Token.OR, "\\vee ", TGroup.SUM, 0), + new SmTokenTableEntry( "ortho", Token.ORTHO, "\\perp ", TGroup.RELATION, 0), + new SmTokenTableEntry( "otimes", Token.OTIMES, "\\otimes ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "over", Token.OVER, "", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "overbrace", Token.OVERBRACE, "", TGroup.PRODUCT, 5), + new SmTokenTableEntry( "overline", Token.OVERLINE, "\\overline", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "overstrike", Token.OVERSTRIKE, "\\mathoverstrike", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "owns", Token.NI, "\\ni ", TGroup.RELATION, 0), + new SmTokenTableEntry( "parallel", Token.PARALLEL, "\\parallel ", TGroup.RELATION, 0), + new SmTokenTableEntry( "partial", Token.PARTIAL, "\\partial ", TGroup.STANDALONE, 5 ), + new SmTokenTableEntry( "phantom", Token.PHANTOM, "\\phantom", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "plusminus", Token.PLUSMINUS, "\\pm ", TGroup.UNOPER, TGroup.SUM, 5), + new SmTokenTableEntry( "prod", Token.PROD, "\\prod ", TGroup.OPER, 5), + new SmTokenTableEntry( "prop", Token.PROP, "\\propto ", TGroup.RELATION, 0), + new SmTokenTableEntry( "rangle", Token.RANGLE, "\\rangle ", TGroup.RBRACES, 0), + new SmTokenTableEntry( "rbrace", Token.RBRACE, "\\}", TGroup.RBRACES, 0), + new SmTokenTableEntry( "rceil", Token.RCEIL, "\\rceil ", TGroup.RBRACES, 0), + new SmTokenTableEntry( "rdbracket", Token.RDBRACKET, "\\rrbracket ", TGroup.RBRACES, 0), + new SmTokenTableEntry( "rdline", Token.RDLINE, "\\|", TGroup.RBRACES, 0), + new SmTokenTableEntry( "red", Token.RED, "red", TGroup.COLOR, 0), + new SmTokenTableEntry( "rfloor", Token.RFLOOR, "\\rfloor", TGroup.RBRACES, 0), + new SmTokenTableEntry( "right", Token.RIGHT, "", 0), + new SmTokenTableEntry( "rightarrow" , Token.RIGHTARROW, "\\rightarrow ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "rline", Token.RLINE, "|", TGroup.RBRACES, 0), + new SmTokenTableEntry( "rsub", Token.RSUB, "", TGroup.POWER, 0), + new SmTokenTableEntry( "rsup", Token.RSUP, "", TGroup.POWER, 0), + new SmTokenTableEntry( "sans", Token.SANS, "\\mathsf", TGroup.FONT, 0), + new SmTokenTableEntry( "serif", Token.SERIF, "\\mathrm", TGroup.FONT, 0), + new SmTokenTableEntry( "setC" , Token.SETC, "\\mathbb{C}", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "setN" , Token.SETN, "\\mathbb{N}", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "setQ" , Token.SETQ, "\\mathbb{Q}", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "setR" , Token.SETR, "\\mathbb{R}", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "setZ" , Token.SETZ, "\\mathbb{Z}", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "setminus", Token.BACKSLASH, "\\setminus ", TGroup.PRODUCT, 0 ), + new SmTokenTableEntry( "sim", Token.SIM, "\\sim ", TGroup.RELATION, 0), + new SmTokenTableEntry( "simeq", Token.SIMEQ, "\\simeq ", TGroup.RELATION, 0), + new SmTokenTableEntry( "sin", Token.SIN, "\\sin ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "sinh", Token.SINH, "\\sinh ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "size", Token.SIZE, "", TGroup.FONTATTR, 5), + new SmTokenTableEntry( "slash", Token.SLASH, "/", TGroup.PRODUCT, 0 ), + new SmTokenTableEntry( "sqrt", Token.SQRT, "", TGroup.UNOPER, 5), + new SmTokenTableEntry( "stack", Token.STACK, "", 5), + new SmTokenTableEntry( "sub", Token.RSUB, "", TGroup.POWER, 0), + new SmTokenTableEntry( "subset", Token.SUBSET, "\\subset ", TGroup.RELATION, 0), + new SmTokenTableEntry( "subseteq", Token.SUBSETEQ, "\\subseteq ", TGroup.RELATION, 0), + new SmTokenTableEntry( "sum", Token.SUM, "\\sum ", TGroup.OPER, 5), + new SmTokenTableEntry( "sup", Token.RSUP, "", TGroup.POWER, 0), + new SmTokenTableEntry( "supset", Token.SUPSET, "\\supset ", TGroup.RELATION, 0), + new SmTokenTableEntry( "supseteq", Token.SUPSETEQ, "\\supseteq ", TGroup.RELATION, 0), + new SmTokenTableEntry( "tan", Token.TAN, "\\tan ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "tanh", Token.TANH, "\\tanh ", TGroup.FUNCTION, 5), + new SmTokenTableEntry( "tilde", Token.TILDE, "\\tilde ", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "times", Token.TIMES, "\\times ", TGroup.PRODUCT, 0), + new SmTokenTableEntry( "to", Token.TO, "", TGroup.LIMIT, 0), + new SmTokenTableEntry( "toward", Token.TOWARD, "\\to ", TGroup.RELATION, 0), + new SmTokenTableEntry( "transl", Token.TRANSL, "\\multimapdotbothA ", TGroup.RELATION, 0), + new SmTokenTableEntry( "transr", Token.TRANSR, "\\multimapdotbothB ", TGroup.RELATION, 0), + new SmTokenTableEntry( "underbrace", Token.UNDERBRACE, "", TGroup.PRODUCT, 5), + new SmTokenTableEntry( "underline", Token.UNDERLINE, "\\underline", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "union", Token.UNION, "\\cup ", TGroup.SUM, 0), + new SmTokenTableEntry( "uoper", Token.UOPER, "", TGroup.UNOPER, 5), + new SmTokenTableEntry( "uparrow" , Token.UPARROW, "\\uparrow ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "vec", Token.VEC, "\\vec", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "white", Token.WHITE, "white", TGroup.COLOR, 0), + new SmTokenTableEntry( "widebslash", Token.WIDEBACKSLASH, "", TGroup.PRODUCT, 0 ), + new SmTokenTableEntry( "widehat", Token.WIDEHAT, "\\widehat ", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "widetilde", Token.WIDETILDE, "\\widetilde", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "wideslash", Token.WIDESLASH, "", TGroup.PRODUCT, 0 ), + new SmTokenTableEntry( "widevec", Token.WIDEVEC, "\\overrightarrow", TGroup.ATTRIBUT, 5), + new SmTokenTableEntry( "wp" , Token.WP, "\\wp ", TGroup.STANDALONE, 5), + new SmTokenTableEntry( "yellow", Token.YELLOW, "yellow", TGroup.COLOR, 0)}; + + static final SmTokenTableEntry[] symbols= + {new SmTokenTableEntry("ALPHA",Token.SPECIAL,"A",5), + new SmTokenTableEntry("BETA",Token.SPECIAL,"B",5), + new SmTokenTableEntry("CHI",Token.SPECIAL,"X",5), + new SmTokenTableEntry("DELTA",Token.SPECIAL,"\\Delta ",5), + new SmTokenTableEntry("EPSILON",Token.SPECIAL,"E",5), + new SmTokenTableEntry("ETA",Token.SPECIAL,"H",5), + new SmTokenTableEntry("GAMMA",Token.SPECIAL,"\\Gamma ",5), + new SmTokenTableEntry("IOTA",Token.SPECIAL,"I",5), + new SmTokenTableEntry("KAPPA",Token.SPECIAL,"K",5), + new SmTokenTableEntry("LAMBDA",Token.SPECIAL,"\\Lambda ",5), + new SmTokenTableEntry("MU",Token.SPECIAL,"M",5), + new SmTokenTableEntry("NU",Token.SPECIAL,"N",5), + new SmTokenTableEntry("OMEGA",Token.SPECIAL,"\\Omega ",5), + new SmTokenTableEntry("OMICRON",Token.SPECIAL,"O",5), + new SmTokenTableEntry("PHI",Token.SPECIAL,"\\Phi ",5), + new SmTokenTableEntry("PI",Token.SPECIAL,"\\Pi ",5), + new SmTokenTableEntry("PSI",Token.SPECIAL,"\\Psi ",5), + new SmTokenTableEntry("RHO",Token.SPECIAL,"P",5), + new SmTokenTableEntry("SIGMA",Token.SPECIAL,"\\Sigma ",5), + new SmTokenTableEntry("TAU",Token.SPECIAL,"T",5), + new SmTokenTableEntry("THETA",Token.SPECIAL,"\\Theta ",5), + new SmTokenTableEntry("UPSILON",Token.SPECIAL,"\\Upsilon ",5), + new SmTokenTableEntry("XI",Token.SPECIAL,"\\Xi ",5), + new SmTokenTableEntry("ZETA",Token.SPECIAL,"Z",5), + new SmTokenTableEntry("alpha",Token.SPECIAL,"\\alpha ",5), + new SmTokenTableEntry("beta",Token.SPECIAL,"\\beta ",5), + new SmTokenTableEntry("chi",Token.SPECIAL,"\\chi ",5), + new SmTokenTableEntry("delta",Token.SPECIAL,"\\delta ",5), + new SmTokenTableEntry("epsilon",Token.SPECIAL,"\\epsilon ",5), + new SmTokenTableEntry("eta",Token.SPECIAL,"\\eta ",5), + new SmTokenTableEntry("gamma",Token.SPECIAL,"\\gamma ",5), + new SmTokenTableEntry("iota",Token.SPECIAL,"\\iota ",5), + new SmTokenTableEntry("kappa",Token.SPECIAL,"\\kappa ",5), + new SmTokenTableEntry("lambda",Token.SPECIAL,"\\lambda ",5), + new SmTokenTableEntry("mu",Token.SPECIAL,"\\mu ",5), + new SmTokenTableEntry("nu",Token.SPECIAL,"\\nu ",5), + new SmTokenTableEntry("omega",Token.SPECIAL,"\\omega ",5), + new SmTokenTableEntry("omicron",Token.SPECIAL,"o",5), + new SmTokenTableEntry("phi",Token.SPECIAL,"\\phi ",5), + new SmTokenTableEntry("pi",Token.SPECIAL,"\\pi ",5), + new SmTokenTableEntry("psi",Token.SPECIAL,"\\psi ",5), + new SmTokenTableEntry("rho",Token.SPECIAL,"\\rho ",5), + new SmTokenTableEntry("sigma",Token.SPECIAL,"\\sigma ",5), + new SmTokenTableEntry("tau",Token.SPECIAL,"\\tau ",5), + new SmTokenTableEntry("theta",Token.SPECIAL,"\\theta ",5), + new SmTokenTableEntry("upsilon",Token.SPECIAL,"\\upsilon ",5), + new SmTokenTableEntry("varepsilon",Token.SPECIAL,"\\varepsilon ",5), + new SmTokenTableEntry("varphi",Token.SPECIAL,"\\varphi ",5), + new SmTokenTableEntry("varpi",Token.SPECIAL,"\\varpi ",5), + new SmTokenTableEntry("varrho",Token.SPECIAL,"\\varrho ",5), + new SmTokenTableEntry("varsigma",Token.SPECIAL,"\\varsigma ",5), + new SmTokenTableEntry("vartheta",Token.SPECIAL,"\\vartheta ",5), + new SmTokenTableEntry("xi",Token.SPECIAL,"\\xi ",5), + new SmTokenTableEntry("zeta",Token.SPECIAL,"\\zeta ",5), + new SmTokenTableEntry("and",Token.SPECIAL,"\\wedge ",5), + new SmTokenTableEntry("angle",Token.SPECIAL,"\\sphericalangle ",5), + new SmTokenTableEntry("element",Token.SPECIAL,"\\in ",5), + new SmTokenTableEntry("identical",Token.SPECIAL,"\\equiv ",5), + new SmTokenTableEntry("infinite",Token.SPECIAL,"\\infty ",5), + new SmTokenTableEntry("noelement",Token.SPECIAL,"\\notin ",5), + new SmTokenTableEntry("notequal",Token.SPECIAL,"\\neq ",5), + new SmTokenTableEntry("or",Token.SPECIAL,"\\vee ",5), + new SmTokenTableEntry("perthousand",Token.SPECIAL,"\\text{\\textperthousand}",5), + new SmTokenTableEntry("strictlygreaterthan",Token.SPECIAL,"\\gg ",5), + new SmTokenTableEntry("strictlylessthan",Token.SPECIAL,"\\ll ",5), + new SmTokenTableEntry("tendto",Token.SPECIAL,"\\to ",5)}; +} + +// Token (this is simply a struct) +class SmToken{ + Token eType; + String sLaTeX; + TGroup eGroup1, eGroup2; + int nLevel; + boolean bSingleChar=false; // only used for identifiers + + SmToken(){ + this.assign(Token.UNKNOWN,"",0); + } + + void assign(Token eType, String sLaTeX, TGroup eGroup1, TGroup eGroup2, int nLevel){ + this.eType=eType; + this.sLaTeX=sLaTeX; + this.eGroup1=eGroup1; + this.eGroup2=eGroup2; + this.nLevel=nLevel; + } + + void assign(Token eType, String sLaTeX, TGroup eGroup1, int nLevel){ + assign(eType, sLaTeX, eGroup1, TGroup.NONE, nLevel); + } + + void assign(Token eType, String sLaTeX, int nLevel){ + assign(eType, sLaTeX, TGroup.NONE, TGroup.NONE, nLevel); + } +} + + +/////////////////////////////////////////////////////////////////// +// The converter class +public final class StarMathConverter implements writer2latex.api.StarMathConverter { + + // Variables + private SmTokenTable keywords=new SmTokenTable(SmTokenTable.keywords); + private SmTokenTable symbols=new SmTokenTable(SmTokenTable.symbols); + private LaTeXConfig config; + private Hashtable configSymbols; + private boolean bUseColor; + private SmToken curToken=new SmToken(); // contains the data of the current token + private SimpleInputBuffer buffer; // contains the starmath formula + //private Float fBaseSize; // base size for the formula (usually 12pt) + private I18n i18n; + + // Flags to track need for ooomath.sty definitions + private boolean bDefeq = false; + private boolean bLambdabar = false; + private boolean bDdotsup = false; + private boolean bMultimapdotbothA = false; + private boolean bMultimapdotbothB = false; + private boolean bLlbracket = false; + private boolean bRrbracket = false; + private boolean bOiint = false; + private boolean bOiiint = false; + private boolean bWideslash = false; + private boolean bWidebslash = false; + private boolean bBoldsubformula = false; + private boolean bNormalsubformula = false; + private boolean bMultiscripts = false; + private boolean bMathoverstrike = false; + + // Constructor for stand alone StarMath converter + public StarMathConverter() { + config = new LaTeXConfig(); + i18n = new ClassicI18n(config); + configSymbols = config.getMathSymbols(); + bUseColor = config.useColor(); + } + + StarMathConverter(I18n i18n, LaTeXConfig config){ + this.config = config; + this.i18n = i18n; + configSymbols = config.getMathSymbols(); + bUseColor = config.useColor(); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + if (bDefeq) { + decl.append("\\newcommand\\defeq{\\stackrel{\\mathrm{def}}{=}}").nl(); + } + if (bLambdabar) { + decl.append("\\newcommand\\lambdabar{\\mathchar'26\\mkern-10mu\\lambda}").nl(); + } + if (bDdotsup) { + decl.append("\\newcommand\\ddotsup{\\mathinner{\\mkern1mu\\raise1pt\\vbox{\\kern7pt\\hbox{.}}\\mkern2mu\\raise4pt\\hbox{.}\\mkern2mu\\raise7pt\\hbox{.}\\mkern1mu}}").nl(); + } + if (bMultimapdotbothA) { + decl.append("\\providecommand\\multimapdotbothA{\\bullet\\kern-0.4em-\\kern-0.4em\\circ}").nl(); + } + if (bMultimapdotbothB) { + decl.append("\\providecommand\\multimapdotbothB{\\circ\\kern-0.4em-\\kern-0.4em\\bullet}").nl(); + } + if (bLlbracket) { + decl.append("\\providecommand\\llbracket{[}").nl(); + } + if (bRrbracket) { + decl.append("\\providecommand\\rrbracket{]}").nl(); + } + if (bOiint) { + decl.append("\\providecommand\\oiint{\\oint}").nl(); + } + if (bOiiint) { + decl.append("\\providecommand\\oiiint{\\oint}").nl(); + } + if (bWideslash) { + decl.append("\\newcommand\\wideslash[2]{{}^{#1}/_{#2}}").nl(); + } + if (bWidebslash) { + decl.append("\\newcommand\\widebslash[2]{{}_{#1}\\backslash^{#2}}").nl(); + } + if (bBoldsubformula) { + decl.append("\\newcommand\\boldsubformula[1]{\\text{\\mathversion{bold}$#1$}}").nl(); + } + if (bNormalsubformula) { + decl.append("\\newcommand\\normalsubformula[1]{\\text{\\mathversion{normal}$#1$}}").nl(); + } + if (bMultiscripts || bMathoverstrike) { + decl.append("\\newlength{\\idxmathdepth}\\newlength{\\idxmathtotal}\\newlength{\\idxmathwidth}\\newlength{\\idxraiseme}").nl(); + decl.append("\\newcommand{\\idxdheight}[1]{\\protect\\settoheight{\\idxmathtotal}{\\(\\displaystyle#1\\)}\\protect\\settodepth{\\idxmathdepth}{\\(\\displaystyle#1\\)}\\protect\\settowidth{\\idxmathwidth}{\\(\\displaystyle#1\\)}\\protect\\addtolength{\\idxmathtotal}{\\idxmathdepth}\\protect\\setlength{\\idxraiseme}{\\idxmathtotal/2-\\idxmathdepth}}").nl(); + decl.append("\\newcommand{\\idxtheight}[1]{\\protect\\settoheight{\\idxmathtotal}{\\(\\textstyle #1\\)}\\protect\\settodepth{\\idxmathdepth}{\\(\\textstyle #1\\)}\\protect\\settowidth{\\idxmathwidth}{\\(\\textstyle#1\\)}\\protect\\addtolength{\\idxmathtotal}{\\idxmathdepth}\\protect\\setlength{\\idxraiseme}{\\idxmathtotal/2-\\idxmathdepth}}").nl(); + decl.append("\\newcommand{\\idxsheight}[1]{\\protect\\settoheight{\\idxmathtotal}{\\(\\scriptstyle #1\\)}\\protect\\settodepth{\\idxmathdepth}{\\(\\scriptstyle #1\\)}\\protect\\settowidth{\\idxmathwidth}{\\(\\scriptstyle#1\\)}\\protect\\addtolength{\\idxmathtotal}{\\idxmathdepth}\\protect\\setlength{\\idxraiseme}{\\idxmathtotal/2-\\idxmathdepth}}").nl(); + decl.append("\\newcommand{\\idxssheight}[1]{\\protect\\settoheight{\\idxmathtotal}{\\(\\scriptscriptstyle #1\\)}\\protect\\settodepth{\\idxmathdepth}{\\(\\scriptscriptstyle #1\\)}\\protect\\settowidth{\\idxmathwidth}{\\(\\scriptscriptstyle#1\\)}\\protect\\addtolength{\\idxmathtotal}{\\idxmathdepth}\\protect\\setlength{\\idxraiseme}{\\idxmathtotal/2-\\idxmathdepth}}").nl(); + } + if (bMultiscripts) { + decl.append("\\newcommand\\multiscripts[5]{\\mathchoice") + .append("{\\idxdheight{#4}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#1\\underset{#2}{\\overset{#3}{#4}}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#5}") + .append("{\\idxtheight{#4}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#1\\underset{#2}{\\overset{#3}{#4}}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#5}") + .append("{\\idxsheight{#4}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#1\\underset{#2}{\\overset{#3}{#4}}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#5}") + .append("{\\idxssheight{#4}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#1\\underset{#2}{\\overset{#3}{#4}}\\rule[-\\idxmathdepth]{0mm}{\\idxmathtotal}#5}}") + .nl(); + } + if (bMathoverstrike) { + decl.append("\\newcommand\\mathoverstrike[1]{\\mathchoice") + .append("{\\idxdheight{#1}\\rlap{\\rule[\\idxraiseme]{\\idxmathwidth}{0.4pt}}{#1}}") + .append("{\\idxtheight{#1}\\rlap{\\rule[\\idxraiseme]{\\idxmathwidth}{0.4pt}}{#1}}") + .append("{\\idxsheight{#1}\\rlap{\\rule[\\idxraiseme]{\\idxmathwidth}{0.4pt}}{#1}}") + .append("{\\idxssheight{#1}\\rlap{\\rule[\\idxraiseme]{\\idxmathwidth}{0.4pt}}{#1}}}") + .nl(); + } + } + + // Implement writer2latex.api.StarMathConverter + public writer2latex.api.Config getConfig() { + return config; + } + + public String getPreamble() { + LaTeXDocumentPortion decl = new LaTeXDocumentPortion(false); + LaTeXDocumentPortion pack = new LaTeXDocumentPortion(false); + i18n.appendDeclarations(pack,decl); + appendDeclarations(pack,decl); + return pack.toString()+decl.toString(); + } + + + + ///////////////////////////////////////// + // Tokenizer + + private boolean tokenInGroup(TGroup eGroup){ + return curToken.eGroup1==eGroup || curToken.eGroup2==eGroup; + } + + private void skipWhiteSpaces(){ + while (Character.isWhitespace(buffer.peekChar())) buffer.getChar(); + } + + private void skipComment(){ + if (buffer.peekChar()!='%' || buffer.peekFollowingChar()!='%') return; + while (!CharClasses.isEndOrLineEnd(buffer.peekChar())) buffer.getChar(); + } + + private void nextToken(){ + // read next token from buffer and update curToken + int nStart; + do // move to first significant character + { nStart=buffer.getIndex(); + skipWhiteSpaces(); + skipComment(); + } while (nStart<buffer.getIndex()); + if (buffer.peekChar()=='\0'){ // End of input + curToken.assign(Token.END,"",0); + return; + } + else if (buffer.peekChar()=='"'){ // Text + String sText=""; + buffer.getChar(); + while (buffer.peekChar()!='"' && buffer.peekChar()!='\0'){ + sText+=buffer.getChar(); // temporary... + } + buffer.getChar(); + // TODO: Language should be inherited from surrounding text + curToken.assign(Token.TEXT, i18n.convert(sText,false,"en"), 5); + return; + } + else if (buffer.peekChar()=='%'){ // Special + buffer.getChar(); + String sIdent=buffer.getIdentifier(); + if (configSymbols.containsKey(sIdent)) { // symbol defined in configuration + curToken.assign(Token.SPECIAL, (String) configSymbols.get(sIdent), 5); + } + else if (!symbols.lookup(sIdent,false,curToken)) + curToken.assign(Token.IDENT, i18n.convert(sIdent,true,"en"), 5); + } + else if (Character.isLetter(buffer.peekChar())){ // Identifier + String sIdent=buffer.getIdentifier(); + if (!keywords.lookup(sIdent,true,curToken)) { + curToken.assign(Token.IDENT, i18n.convert(sIdent,true,"en"), 5); + curToken.bSingleChar = sIdent.length()==1; + } + return; + } + else if (CharClasses.isDigitOrDot(buffer.peekChar())){ // Number + String sNumber=buffer.getNumber(); + curToken.assign(Token.NUMBER, sNumber, 5); + return; + } + else { + char cChar=buffer.getChar(); + switch (cChar) { + case '<': + if (buffer.peekChar()=='<'){ + buffer.getChar(); + curToken.assign(Token.LL, "\\ll ", TGroup.RELATION, 0); + } + else if (buffer.peekChar()=='='){ + buffer.getChar(); + curToken.assign(Token.LE, "\\le ", TGroup.RELATION, 0); + } + else if (buffer.peekChar()=='>'){ + buffer.getChar(); + curToken.assign(Token.NEQ, "\\neq ", TGroup.RELATION, 0); + } + else if (buffer.peekChar()=='?' && buffer.peekFollowingChar()=='>'){ + buffer.getChar(); buffer.getChar(); + curToken.assign(Token.PLACE, "\\Box ", TGroup.STANDALONE, 5); // no group in parse.cxx + } + else { + curToken.assign(Token.LT, "<", TGroup.RELATION, 0); + } + return; + case '>': + if (buffer.peekChar()=='>'){ + buffer.getChar(); + curToken.assign(Token.GG, "\\gg ", TGroup.RELATION, 0); + } + else if (buffer.peekChar()=='='){ + buffer.getChar(); + curToken.assign(Token.GE, "\\ge ", TGroup.RELATION, 0); + } + else { + curToken.assign(Token.GT, ">", TGroup.RELATION, 0); + } + return; + case '[': + curToken.assign(Token.LBRACKET, "[", TGroup.LBRACES, 5); + return; + case '\\': + curToken.assign(Token.ESCAPE, "", 5); + return; + case ']': + curToken.assign(Token.RBRACKET, "]", TGroup.RBRACES, 0); + return; + case '^': + curToken.assign(Token.RSUP, "", TGroup.POWER, 0); + return; + case '_': + curToken.assign(Token.RSUB, "", TGroup.POWER, 0); + return; + case '`': + curToken.assign(Token.SBLANK, "\\;", TGroup.BLANK, 5); + return; + case '{': + curToken.assign(Token.LGROUP, "{", 5); + return; + case '|': + curToken.assign(Token.OR, "\\vee ", TGroup.SUM, 0); + return; + case '}': + curToken.assign(Token.RGROUP, "}", 0); + return; + case '~': + curToken.assign(Token.BLANK, "\\ ", TGroup.BLANK, 5); + return; + case '#': + if (buffer.peekChar()=='#'){ + buffer.getChar(); + curToken.assign(Token.DPOUND, "", 0); + } + else { + curToken.assign(Token.POUND, "", 0); + } + return; + case '&': + curToken.assign(Token.AND, "\\wedge ", TGroup.PRODUCT, 0); + return; + case '(': + curToken.assign(Token.LPARENT, "(", TGroup.LBRACES, 5); + return; + case ')': + curToken.assign(Token.RPARENT, ")", TGroup.RBRACES, 0); + return; + case '*': + curToken.assign(Token.MULTIPLY, "\\ast ", TGroup.PRODUCT, 0); + return; + case '+': + if (buffer.peekChar()=='-'){ + buffer.getChar(); + curToken.assign(Token.PLUSMINUS, "\\pm ", TGroup.UNOPER, TGroup.SUM, 5); + } + else { + curToken.assign(Token.PLUS, "+", TGroup.UNOPER, TGroup.SUM, 5); + } + return; + case '-': + if (buffer.peekChar()=='+'){ + buffer.getChar(); + curToken.assign(Token.MINUSPLUS, "\\mp ", TGroup.UNOPER, TGroup.SUM, 5); + } + else { + curToken.assign(Token.MINUS, "-", TGroup.UNOPER, TGroup.SUM, 5); + } + return; + //case '.': // not relevant... ?? + // curToken.assign(Token.POINT, ".", 0); + // return; + case '/': + curToken.assign(Token.DIVIDEBY, "/", TGroup.PRODUCT, 0); + return; + case '=': + curToken.assign(Token.ASSIGN, "=", TGroup.RELATION, 0); + return; + default: + Character cCharObject=new Character(cChar); + curToken.assign(Token.CHARACTER,i18n.convert(cCharObject.toString(),true,"en"),5); + return; + } + + } + } + + //////////////////////////////////////////////// + // Grammar + + private String table(float fSize, Token eAlign){ + StringBuffer bufTable=new StringBuffer(); + String sLine=line(fSize,eAlign); + if (curToken.eType==Token.NEWLINE){ // more than one line + bufTable.append("\\begin{gathered}").append(sLine); + while (curToken.eType==Token.NEWLINE){ + nextToken(); + bufTable.append("\\\\").append(line(fSize,eAlign)); + } + return bufTable.append("\\end{gathered}").toString(); + } + else { // only one line + return sLine; + } + } + + private String align(float fSize, Token eAlign,boolean bUseAlignment,boolean bNeedNull){ + // Alignment works very different in StarMath and LaTeX: + // In LaTeX alignment is accomplished using suitable \hfill's in appropriate spots. + // Hence we need to pass on the current alignment as a parameter to decide where to \hfill. + // bUseAlignment requires us to add a suitable \hfill (set true by table, matrix and stack). + // bNeedNull requires us to add \null (an empty hbox) at the end (the matrix environment + // needs this). + // Currently fractions and binoms are *not* aligned. + // In the other constructions alignment doesn't work if it's put inside a group: + // stack{{alignl a}#aaaa} does not work, while stack{alignl a#aaaa} does work. + + if (tokenInGroup(TGroup.ALIGN)){ + eAlign=curToken.eType; + nextToken(); + } + if (bUseAlignment && eAlign==Token.ALIGNL){ + if (bNeedNull){ + return expression(fSize,eAlign)+"\\hfill\\null "; + } + else { + return expression(fSize,eAlign)+"\\hfill "; + } + } + else if (bUseAlignment && eAlign==Token.ALIGNR){ + return "\\hfill "+expression(fSize,eAlign); + } + else { // center alignment (default!) or no alignment + return expression(fSize,eAlign); + } + } + + private String line(float fSize, Token eAlign){ + if (curToken.eType!=Token.NEWLINE && curToken.eType!=Token.END){ + // Add implicit left alignment for expressions starting with text + // (Note: Don't pass on this alignment to subexpressions!) + if (curToken.eType==Token.TEXT) { + return expression(fSize,eAlign)+"\\hfill "; + } + else { + return align(fSize,eAlign,true,false); + } + } + else { // empty line + return "{}"; // LaTeX doesn't like empty lines in gather + } + } + + private String expression(float fSize, Token eAlign){ + StringBuffer bufExpression=new StringBuffer().append(relation(fSize,eAlign)); + while (curToken.nLevel>=5){ + bufExpression.append(relation(fSize,eAlign)); + } + return bufExpression.toString(); + } + + private String relation(float fSize, Token eAlign){ + StringBuffer bufRelation=new StringBuffer().append(sum(fSize,eAlign)); + while (tokenInGroup(TGroup.RELATION)){ + if (curToken.eType==Token.TRANSL) { bMultimapdotbothA=true; } + else if (curToken.eType==Token.TRANSR) { bMultimapdotbothB=true; } + else if (curToken.eType==Token.DEF) { bDefeq=true; } + bufRelation.append(opsubsup(fSize,eAlign)).append(sum(fSize,eAlign)); + } + return bufRelation.toString(); + } + + private String sum(float fSize, Token eAlign){ + StringBuffer bufSum=new StringBuffer().append(product(fSize,eAlign)); + while (tokenInGroup(TGroup.SUM)){ + bufSum.append(opsubsup(fSize,eAlign)).append(product(fSize,eAlign)); + } + return bufSum.toString(); + } + + private String product(float fSize, Token eAlign){ + String sProduct=power(fSize,eAlign,true); + sProduct=sProduct.substring(1,sProduct.length()-1); + // a small hack to avoid double {}: Require {}, then remove them + // and add them below if they are needed. + while (tokenInGroup(TGroup.PRODUCT)){ + if (curToken.eType==Token.OVER){ + nextToken(); + sProduct="\\frac{"+sProduct+"}"+power(fSize,eAlign,true); + } else if (curToken.eType==Token.BOPER){ + nextToken(); + sProduct+=special()+power(fSize,eAlign,false); + } else if (curToken.eType==Token.OVERBRACE){ + nextToken(); + sProduct="\\overbrace{"+sProduct+"}^"+power(fSize,eAlign,true); + } else if (curToken.eType==Token.UNDERBRACE){ + nextToken(); + sProduct="\\underbrace{"+sProduct+"}_"+power(fSize,eAlign,true); + } else if (curToken.eType==Token.WIDESLASH){ + bWideslash=true; + nextToken(); + sProduct="\\wideslash{"+sProduct+"}"+power(fSize,eAlign,true); + } else if (curToken.eType==Token.WIDEBACKSLASH){ + bWidebslash=true; + nextToken(); + sProduct="\\widebslash{"+sProduct+"}"+power(fSize,eAlign,true); + } else { + sProduct+=opsubsup(fSize,eAlign)+power(fSize,eAlign,false); + } + } + return sProduct; + + } + + private String tosub(String s){ + return s!=null ? "_"+s : ""; + } + + private String tosup(String s){ + return s!=null ? "^"+s : ""; + } + + private String subsup(float fSize, Token eAlign,String sBody, TGroup eActiveGroup){ + // sBody is the string to attach scripts to + // eActiveGroup must be TGroup.LIMIT or TGroup.POWER + // in the former case sBody must contain a large operator (sum, int...) + Token eScriptType; + String sLsub=null, sLsup=null, sCsub=null, sCsup=null, sRsub=null, sRsup=null; + while (tokenInGroup(eActiveGroup)){ + eScriptType=curToken.eType; + nextToken(); + if (eScriptType==Token.FROM) sCsub="{"+relation(fSize,eAlign)+"}"; + else if (eScriptType==Token.TO) sCsup="{"+relation(fSize,eAlign)+"}"; + else if (eScriptType==Token.LSUB) sLsub=term(fSize,eAlign,true); + else if (eScriptType==Token.LSUP) sLsup=term(fSize,eAlign,true); + else if (eScriptType==Token.CSUB) sCsub=term(fSize,eAlign,true); + else if (eScriptType==Token.CSUP) sCsup=term(fSize,eAlign,true); + else if (eScriptType==Token.RSUB) sRsub=term(fSize,eAlign,true); + else if (eScriptType==Token.RSUP) sRsup=term(fSize,eAlign,true); + } + if (sLsub==null && sLsup==null && sCsub==null && sCsup==null && sRsub==null && sRsup==null){ + return sBody; + } + if (eActiveGroup==TGroup.LIMIT){ + if (sLsub==null && sLsup==null && sRsub==null && sRsup==null){ + // ordinary limits + return sBody+tosub(sCsub)+tosup(sCsup); + } + else { // nontrivial case: use \sideset + // problem: always typesets the operator in \displaystyle + // solution: use \multiscripts instead??? + return "\\sideset{"+tosub(sLsub)+tosup(sLsup)+"}{"+tosub(sRsub)+tosup(sRsup)+"}" + +sBody+"\\limits"+tosub(sCsub)+tosup(sCsup); + } + } + else { + if (sLsub==null && sLsup==null && sCsub==null && sCsup==null){ + // ordinary scripts + return sBody+tosub(sRsub)+tosup(sRsup); + } + else if (sLsub==null && sLsup==null && sRsub==null && sRsup==null){ + // scripts above/below + if (sCsub==null){ + return "\\overset"+sCsup+"{"+sBody+"}"; + } + else if (sCsup==null){ + return "\\underset"+sCsub+"{"+sBody+"}"; + } + else { + return "\\overset"+sCsup+"{\\underset"+sCsub+"{"+sBody+"}}"; + } + } + else {// general case: use \multiscripts + bMultiscripts=true; + if (sCsub==null) {sCsub="{}";} + if (sCsup==null) {sCsup="{}";} + return "\\multiscripts{"+tosub(sLsub)+tosup(sLsup)+"}"+sCsub+sCsup + +"{"+sBody+"}{"+tosub(sRsub)+tosup(sRsup)+"}"; + } + } + } + + private String opsubsup(float fSize, Token eAlign){ + String sOperator=curToken.sLaTeX; + nextToken(); + return subsup(fSize,eAlign,sOperator,TGroup.POWER); + } + + private String power(float fSize, Token eAlign,boolean bNeedGroup){ + // bNeedGroup is true, if the power needs to be enclosed in braces + // Since we don't want to add unnecessary braces the responsibility + // is delegated to power - we need to look ahead to determine if {} + // should be added. + boolean bTermIsGroup=curToken.eType==Token.LGROUP; + String sTerm=term(fSize,eAlign); + if (bNeedGroup && (!bTermIsGroup || tokenInGroup(TGroup.POWER))){ + return "{"+subsup(fSize,eAlign,sTerm,TGroup.POWER)+"}"; + } + else { + return subsup(fSize,eAlign,sTerm,TGroup.POWER); + } + } + + private String blank(){ + StringBuffer bufBlank=new StringBuffer(); + while (tokenInGroup(TGroup.BLANK)){ + bufBlank.append(curToken.sLaTeX); + nextToken(); + } + return bufBlank.toString(); + } + + private String term(float fSize, Token eAlign, boolean bNeedGroup){ + // Special version of term used to avoid double {{grouping}} + // if bNeedGroup=true we must return {term} in braces + if (bNeedGroup && !(curToken.eType==Token.LGROUP)){ + return "{"+term(fSize,eAlign)+"}"; + } + /*else if (!bNeedGroup && curToken.eType==Token.LGROUP){ + String sTerm=term(fSize,eAlign); + return sTerm.substring(1,sTerm.length()-2); // renove unwanted {} + }*/ + else { + return term(fSize,eAlign); + } + } + + private String term(float fSize, Token eAlign){ + String sContent; + if (curToken.eType==Token.ESCAPE) + return escape(); + else if (curToken.eType==Token.LGROUP){ + nextToken(); + if (curToken.eType!=Token.RGROUP) + sContent=align(fSize,eAlign,false,false); + else + sContent=""; // empty group + if (curToken.eType==Token.RGROUP) + nextToken(); + // otherwise there is an error in the formula + // we close the group anyway to make us TeX'able. + return "{"+sContent+"}"; + } + else if (curToken.eType==Token.LEFT) + return scalebrace(fSize,eAlign); + else if (tokenInGroup(TGroup.BLANK)) + return blank(); + else if (curToken.eType==Token.TEXT){ + sContent=curToken.sLaTeX; + nextToken(); + return "\\text{"+sContent+"}"; + } + else if (curToken.eType==Token.CHARACTER || curToken.eType==Token.NUMBER + || tokenInGroup(TGroup.STANDALONE)){ + if (curToken.eType==Token.LAMBDABAR) { bLambdabar=true; } + if (curToken.eType==Token.DOTSUP) { bDdotsup=true; } + sContent=curToken.sLaTeX; + nextToken(); + return sContent; + } + else if (curToken.eType==Token.IDENT){ + sContent=curToken.sLaTeX; + boolean bSingleChar = curToken.bSingleChar; + nextToken(); + return bSingleChar ? sContent : "\\mathit{"+sContent+"}"; + } + else if (curToken.eType==Token.SPECIAL) + return special(); + else if (curToken.eType==Token.BINOM) + return binom(fSize,eAlign); + else if (curToken.eType==Token.STACK) + return stack(fSize,eAlign); + else if (curToken.eType==Token.MATRIX) + return matrix(fSize,eAlign); + else if (tokenInGroup(TGroup.LBRACES)) + return brace(fSize,eAlign); + else if (tokenInGroup(TGroup.OPER)) + return operator(fSize,eAlign); + else if (tokenInGroup(TGroup.UNOPER)) + return unoper(fSize,eAlign); + else if (tokenInGroup(TGroup.ATTRIBUT) || tokenInGroup(TGroup.FONTATTR)) + return attributes(fSize,eAlign); + else if (tokenInGroup(TGroup.FUNCTION)) + return function(); + else { // error in formula + nextToken(); + return "?"; + } + } + + private String escape(){ + String sEscape; + nextToken(); + if ((tokenInGroup(TGroup.LBRACES) || tokenInGroup(TGroup.RBRACES)) + && curToken.eType!=Token.NONE){ + sEscape=curToken.sLaTeX; + nextToken(); + } + else if (curToken.eType==Token.LGROUP){ + sEscape="\\{"; + nextToken(); + } + else if (curToken.eType==Token.RGROUP){ + sEscape="\\}"; + nextToken(); + } + else { // error in formula + sEscape=""; + } + return sEscape; + } + + private String operator(float fSize, Token eAlign){ + String sOperator=oper(); + if (tokenInGroup(TGroup.LIMIT) || tokenInGroup(TGroup.POWER)){ + // Note: TGroup.LIMIT and TGroup.POWER are always in eGroup1, so this is OK: + return subsup(fSize,eAlign,sOperator,curToken.eGroup1)+power(fSize,eAlign,false); + } + else { + return sOperator+power(fSize,eAlign,false); + } + } + + private String oper(){ + if (curToken.eType==Token.LLINT) { bOiint=true; } + else if (curToken.eType==Token.LLLINT) { bOiiint=true; } + String sOper; + if (curToken.eType==Token.OPER){ + nextToken(); + if (curToken.eType==Token.SPECIAL) + sOper="\\operatornamewithlimits{"+curToken.sLaTeX+"}"; + else + sOper="\\operatornamewithlimits{?}"; // error in formula + } + else { + sOper=curToken.sLaTeX; + } + nextToken(); + return sOper; + } + + private String unoper(float fSize, Token eAlign){ + if (curToken.eType==Token.ABS){ + nextToken(); + return "\\left|"+power(fSize,eAlign,false)+"\\right|"; + } + else if (curToken.eType==Token.SQRT){ + nextToken(); + return "\\sqrt"+power(fSize,eAlign,true); + } + else if (curToken.eType==Token.NROOT){ + nextToken(); + return "\\sqrt["+power(fSize,eAlign,false)+"]"+power(fSize,eAlign,true); + } + else if (curToken.eType==Token.UOPER){ + nextToken(); + return special()+power(fSize,eAlign,false); + } + else if (curToken.eType==Token.FACT){ + String sOperator=opsubsup(fSize,eAlign); + return power(fSize,eAlign,false)+sOperator; + } + else { // must be PLUS, MINUS, PLUSMINUS, MINUSPLUS or NEG + return opsubsup(fSize,eAlign)+power(fSize,eAlign,false); + } + } + + private String attributes(float fSize, Token eAlign){ + String sAttribute; + if (curToken.eType==Token.FONT){ + nextToken(); + if (tokenInGroup(TGroup.FONT)){ + sAttribute=curToken.sLaTeX; + nextToken(); + return sAttribute+"{"+term(fSize,eAlign)+"}"; + } + else { // error in formula + return "?"; + } + } + else if (curToken.eType==Token.COLOR){ + nextToken(); + if (tokenInGroup(TGroup.COLOR)){ + sAttribute=curToken.sLaTeX; // the color name + nextToken(); + if (bUseColor) { + return "\\textcolor{"+sAttribute+"}{"+term(fSize,eAlign)+"}"; + // note: despite the name, \textcolor also works in math mode! + } + else { + return term(fSize,eAlign); + } + } + else { // error in formula + return "?"; + } + } + else if (curToken.eType==Token.SIZE){ + nextToken(); + if (curToken.eType==Token.PLUS){ + nextToken(); + if (curToken.eType==Token.NUMBER){ + fSize+=Misc.getFloat(curToken.sLaTeX,0); + nextToken(); + } // else error in formula: ignore + } + else if(curToken.eType==Token.MINUS){ + nextToken(); + if (curToken.eType==Token.NUMBER){ + fSize-=Misc.getFloat(curToken.sLaTeX,0); + nextToken(); + } // else error in formula: ignore + } + else if(curToken.eType==Token.MULTIPLY){ + nextToken(); + if (curToken.eType==Token.NUMBER){ + fSize*=Misc.getFloat(curToken.sLaTeX,1); + nextToken(); + } // else error in formula: ignore + } + else if(curToken.eType==Token.DIVIDEBY){ + nextToken(); + if (curToken.eType==Token.NUMBER){ + float f=Misc.getFloat(curToken.sLaTeX,1); + if (f!=0) {fSize/=f;} + nextToken(); + } // else error in formula: ignore + } + else if (curToken.eType==Token.NUMBER){ + fSize=Misc.getFloat(curToken.sLaTeX,fSize); + nextToken(); + } // else error in formula: ignore + return term(fSize,eAlign); + // currently only reads the size, it is not used + // should use fSize/fBaseSize to change to + // \displaystyle, \textstyle, \scriptstyle, \scriptscriptstyle + } + else { // must be ATTRIBUT or FONTATTR + if (curToken.eType == Token.OVERSTRIKE) { bMathoverstrike=true; } + else if (curToken.eType == Token.BOLD) { bBoldsubformula=true; } + else if (curToken.eType == Token.NBOLD) { bNormalsubformula=true; } + else if (curToken.eType == Token.ITALIC) { bNormalsubformula=true; } + sAttribute=curToken.sLaTeX; + nextToken(); + return sAttribute+"{"+term(fSize,eAlign)+"}"; + } + } + + private String scalebrace(float fSize, Token eAlign){ + String sLeft, sRight, sBody; + nextToken(); + if (tokenInGroup(TGroup.LBRACES) || tokenInGroup(TGroup.RBRACES)){ + if (curToken.eType==Token.LDBRACKET) { bLlbracket=true; } + else if (curToken.eType==Token.RDBRACKET) { bRrbracket=true; } + sLeft=new String(curToken.sLaTeX); + nextToken(); + sBody=scalebracebody(fSize,eAlign); + if (curToken.eType==Token.RIGHT) { + nextToken(); + if (tokenInGroup(TGroup.LBRACES) || tokenInGroup(TGroup.RBRACES)){ + if (curToken.eType==Token.LDBRACKET) { bLlbracket=true; } + else if (curToken.eType==Token.RDBRACKET) { bRrbracket=true; } + sRight=new String(curToken.sLaTeX); + nextToken(); + } + else { // no brace after right! + sRight="."; + } + } + else { // no right! + return "."; + } + return "\\left"+sLeft+sBody+"\\right"+sRight; + } + else { // no brace after left! + return "?"; + } + } + + private String brace(float fSize, Token eAlign){ + String sLeft, sRight, sBody; + if (curToken.eType==Token.LDBRACKET) { bLlbracket=true; } + sLeft=new String(curToken.sLaTeX); + nextToken(); + sBody=bracebody(fSize,eAlign); + if (tokenInGroup(TGroup.RBRACES)){ + if (curToken.eType==Token.RDBRACKET) { bRrbracket=true; } + sRight=new String(curToken.sLaTeX); + nextToken(); + return sLeft+sBody+sRight; + } + else { // no right brace! (This is an error, we don't care); + return sLeft+sBody; + } + } + + + private String scalebracebody(float fSize, Token eAlign){ + if (curToken.eType==Token.MLINE){ + nextToken(); + return "\\left|"+scalebracebody(fSize,eAlign)+"\\right."; + } + else if (curToken.eType!=Token.RIGHT && curToken.eType!=Token.END){ + return align(fSize,eAlign,false,false)+scalebracebody(fSize,eAlign); + } + else { // Finished recursion + return ""; + } + } + + private String bracebody(float fSize, Token eAlign){ + if (curToken.eType==Token.MLINE){ + nextToken(); + return "|"+bracebody(fSize,eAlign); + } + else if (!tokenInGroup(TGroup.RBRACES) && curToken.eType!=Token.END){ + return align(fSize,eAlign,false,false)+bracebody(fSize,eAlign); + } + else { // Finished recursion + return ""; + } + } + + private String function(){ + String sFunction; + if (curToken.eType==Token.FUNC){ + nextToken(); + if (curToken.eType==Token.IDENT){ + sFunction="\\operatorname{"+curToken.sLaTeX+"}"; + nextToken(); + } + else { // error in formula + sFunction=""; + } + } + else { + sFunction=curToken.sLaTeX; + nextToken(); + } + return sFunction; + } + + private String binom(float fSize, Token eAlign){ + nextToken(); + return "\\genfrac{}{}{0pt}{0}{"+sum(fSize,eAlign)+"}{"+sum(fSize,eAlign)+"}"; + } + + private String stack(float fSize, Token eAlign){ + nextToken(); + if (curToken.eType==Token.LGROUP){ + StringBuffer bufStack=new StringBuffer().append("\\begin{matrix}"); + do { + nextToken(); + bufStack.append(align(fSize,eAlign,true,true)); + if (curToken.eType==Token.POUND) bufStack.append("\\\\"); + } while (curToken.eType==Token.POUND); + if (curToken.eType==Token.RGROUP) nextToken(); // otherwise error in formula - ignore + return bufStack.append("\\end{matrix}").toString(); + } + else { // error in formula + return ""; + } + } + + private String matrix(float fSize, Token eAlign){ + nextToken(); + if (curToken.eType==Token.LGROUP){ + StringBuffer bufMatrix=new StringBuffer().append("\\begin{matrix}"); + do { + nextToken(); + bufMatrix.append(align(fSize,eAlign,true,true)); + if (curToken.eType==Token.POUND) bufMatrix.append("&"); + else if (curToken.eType==Token.DPOUND) bufMatrix.append("\\\\"); + } while (curToken.eType==Token.POUND || curToken.eType==Token.DPOUND); + if (curToken.eType==Token.RGROUP) nextToken(); // otherwise error in formula- ignore + return bufMatrix.append("\\end{matrix}").toString(); + } + else { // error in formula + return ""; + } + } + + private String special() { + String sSpecial=curToken.sLaTeX; + nextToken(); + return sSpecial; + } + + + //////////////////////////////////////////////// + // Finally, the converter itself + public String convert(String sStarMath){ + String sExport=""; + buffer=new SimpleInputBuffer(sStarMath); + nextToken(); + sExport=table(12.0F,Token.ALIGNC); + return sExport.length()==0 ? " " : sExport; // don't return an empty formula! + } +} + + diff --git a/source/java/writer2latex/latex/StyleConverter.java b/source/java/writer2latex/latex/StyleConverter.java new file mode 100644 index 0000000..284fe7d --- /dev/null +++ b/source/java/writer2latex/latex/StyleConverter.java @@ -0,0 +1,51 @@ +/************************************************************************ + * + * StyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.latex; + +import writer2latex.latex.util.StyleMap; +import writer2latex.util.ExportNameCollection; +import writer2latex.office.OfficeReader; + +/** + * <p>This is an abstract superclass for style converters.</p> + */ +public abstract class StyleConverter extends ConverterHelper { + + // Names and maps + necessary declarations for these styles + protected ExportNameCollection styleNames = new ExportNameCollection(false); + protected StyleMap styleMap = new StyleMap(); + protected LaTeXDocumentPortion declarations = new LaTeXDocumentPortion(false); + + protected StyleConverter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + decl.append(declarations); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/TableConverter.java b/source/java/writer2latex/latex/TableConverter.java new file mode 100644 index 0000000..7b69e97 --- /dev/null +++ b/source/java/writer2latex/latex/TableConverter.java @@ -0,0 +1,366 @@ +/************************************************************************ + * + * TableConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; +import writer2latex.util.*; +import writer2latex.office.*; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; + +/** <p>This class converts OpenDocument tables to LaTeX.</p> + * <p>The following LaTeX packages are used; some of them are optional</p> + * <p>array.sty, longtable.sty, supertabular.sty, tabulary.sty, hhline.sty, + * colortbl.sty.</p> + * <p>Options:</p> + * <ul> + * <li>use_longtable = true|false</li> + * <li>use_supertabular = true|false</li> + * <li>use_tabulary = true|false</li> + * <li>use_colortbl = true|false</li> + * <li>float_tables = true|false</li> + * <li>float_options = <string></li> + * <li>table_content = accept|ignore|warning|error</li> + * </ul> + * + */ +public class TableConverter extends ConverterHelper { + + private boolean bNeedLongtable = false; + private boolean bNeedSupertabular = false; + private boolean bNeedTabulary = false; + private boolean bNeedColortbl = false; + private boolean bContainsTables = false; + + /** <p>Constructs a new <code>TableConverter</code>.</p> + */ + public TableConverter(OfficeReader ofr, LaTeXConfig config, + ConverterPalette palette) { + super(ofr,config,palette); + } + + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + pack.append("\\usepackage{array}").nl(); // TODO: Make this optional + if (bNeedLongtable) { pack.append("\\usepackage{longtable}").nl(); } + if (bNeedSupertabular) { pack.append("\\usepackage{supertabular}").nl(); } + if (bNeedTabulary) { pack.append("\\usepackage{tabulary}").nl(); } + pack.append("\\usepackage{hhline}").nl(); // TODO: Make this optional + if (bNeedColortbl) { pack.append("\\usepackage{colortbl}").nl(); } + + // Set padding for table cells (1mm is default in OOo!) + // For vertical padding we can only specify a relative size + if (bContainsTables) { + decl.append("\\setlength\\tabcolsep{1mm}").nl(); + decl.append("\\renewcommand\\arraystretch{1.3}").nl(); + } + } + + // Export a lonely table caption + public void handleCaption(Element node, LaTeXDocumentPortion ldp, Context oc) { + ldp.append("\\captionof{table}"); + palette.getCaptionCv().handleCaptionBody(node,ldp,oc,true); + } + + /** <p> Process a table (table:table or table:sub-table tag)</p> + * @param node The element containing the table + * @param ldp the <code>LaTeXDocumentPortion</code> to which + * LaTeX code should be added + * @param oc the current context + */ + public void handleTable(Element node, Element caption, boolean bCaptionAbove, + LaTeXDocumentPortion ldp, Context oc) { + + // Export table, if allowed by the configuration + switch (config.tableContent()) { + case LaTeXConfig.ACCEPT: + new SingleTableConverter().handleTable(node,caption,bCaptionAbove,ldp,oc); + bContainsTables = true; + break; + case LaTeXConfig.IGNORE: + // Ignore table silently + break; + case LaTeXConfig.WARNING: + System.err.println("Warning: Tables are not allowed"); + break; + case LaTeXConfig.ERROR: + ldp.append("% Error in document: A table was ignored"); + } + } + + // Inner class to convert a single table + private class SingleTableConverter { + private TableReader table; + private TableFormatter formatter; + private Element caption; + private boolean bCaptionAbove; + private BeforeAfter baTable; + private BeforeAfter baTableAlign; + + private void handleTable(Element node, Element caption, boolean bCaptionAbove, + LaTeXDocumentPortion ldp, Context oc) { + // Store the caption + this.caption = caption; + this.bCaptionAbove = bCaptionAbove; + + // Read the table + table = ofr.getTableReader(node); + + // Get formatter and update flags according to formatter + formatter = new TableFormatter(ofr,config,palette,table,!oc.isInMulticols(),oc.isInTable()); + bContainsTables = true; + bNeedLongtable |= formatter.isLongtable(); + bNeedSupertabular |= formatter.isSupertabular(); + bNeedTabulary |= formatter.isTabulary(); + bNeedColortbl |= formatter.isColortbl(); + + // Update the context + Context ic = (Context) oc.clone(); + ic.setInTable(true); + ic.setInSimpleTable(formatter.isSimple()); + // Never allow footnotes in tables + // (longtable.sty *does* allow footnotes in body, but not in head - + // only consistent solution is to disallow all footnotes) + ic.setNoFootnotes(true); + + // Get table declarations + baTable = new BeforeAfter(); + baTableAlign = new BeforeAfter(); + formatter.applyTableStyle(baTable,baTableAlign); + + // Convert table + if (formatter.isSupertabular()) { + handleSupertabular(ldp,ic); + } + else if (formatter.isLongtable()) { + handleLongtable(ldp,ic); + } + else if (config.floatTables() && !ic.isInFrame() && !table.isSubTable()) { + handleTableFloat(ldp,ic); + } + else { + handleTabular(ldp,ic); + } + + // Insert any pending footnotes + palette.getNoteCv().flushFootnotes(ldp,oc); + } + + private void handleSupertabular(LaTeXDocumentPortion ldp, Context oc) { + ldp.append(baTableAlign.getBefore()); + + // Caption + if (caption!=null) { + handleCaption(bCaptionAbove ? "\\topcaption" : "\\bottomcaption", + ldp,oc); + } + + // Table head + ldp.append("\\tablehead{"); + handleHeaderRows(ldp,oc); + ldp.append("}"); + + // The table + handleHyperTarget(ldp); + ldp.append(baTable.getBefore()).nl(); + handleBodyRows(ldp,oc); + ldp.append(baTable.getAfter()).nl(); + + ldp.append(baTableAlign.getAfter()); + } + + private void handleLongtable(LaTeXDocumentPortion ldp, Context oc) { + handleHyperTarget(ldp); + ldp.append(baTable.getBefore()).nl(); + + // Caption above + if (caption!=null && bCaptionAbove) { + handleCaption("\\caption",ldp,oc); + ldp.append("\\\\").nl(); + handleHeaderRows(ldp,oc); + ldp.nl().append("\\endfirsthead").nl(); + } + + // Table head + if (table.getFirstBodyRow()>0) { + handleHeaderRows(ldp,oc); + ldp.nl().append("\\endhead").nl(); + } + + // Caption below + if (caption!=null && !bCaptionAbove) { + handleCaption("\\caption",ldp,oc); + ldp.append("\\endlastfoot").nl(); + } + + // Table body + handleBodyRows(ldp,oc); + + ldp.append(baTable.getAfter()).nl(); + } + + private void handleTableFloat(LaTeXDocumentPortion ldp, Context oc) { + ldp.append("\\begin{table}"); + if (config.getFloatOptions().length()>0) { + ldp.append("[").append(config.getFloatOptions()).append("]"); + } + ldp.nl(); + + ldp.append(baTableAlign.getBefore()); + + // Caption above + if (caption!=null && bCaptionAbove) { + handleCaption("\\caption",ldp,oc); + } + + // The table + handleHyperTarget(ldp); + ldp.append(baTable.getBefore()).nl(); + handleHeaderRows(ldp,oc); + ldp.nl(); + handleBodyRows(ldp,oc); + ldp.append(baTable.getAfter()).nl(); + + // Caption below + if (caption!=null && !bCaptionAbove) { + handleCaption("\\caption",ldp,oc); + } + + ldp.append(baTableAlign.getAfter()); + + ldp.append("\\end{table}").nl(); + } + + private void handleTabular(LaTeXDocumentPortion ldp, Context oc) { + ldp.append(baTableAlign.getBefore()); + + // Caption above + if (caption!=null && bCaptionAbove) { + TableConverter.this.handleCaption(caption,ldp,oc); + } + + // The table + handleHyperTarget(ldp); + ldp.append(baTable.getBefore()).nl(); + if (table.getFirstBodyRow()>0) { + handleHeaderRows(ldp,oc); + ldp.nl(); + } + handleBodyRows(ldp,oc); + ldp.append(baTable.getAfter()).nl(); + + // Caption below + if (caption!=null && !bCaptionAbove) { + TableConverter.this.handleCaption(caption,ldp,oc); + } + + ldp.append(baTableAlign.getAfter()); + } + + private void handleCaption(String sCommand, LaTeXDocumentPortion ldp, Context oc) { + ldp.append(sCommand); + palette.getCaptionCv().handleCaptionBody(caption,ldp,oc,false); + } + + private void handleHyperTarget(LaTeXDocumentPortion ldp) { + // We may need a hyperlink target + if (!table.isSubTable()) { + palette.getFieldCv().addTarget(table.getTableName(),"|table",ldp); + } + } + + private void handleHeaderRows(LaTeXDocumentPortion ldp, Context oc) { + // Note: does *not* add newline after last row + if (table.getFirstBodyRow()>0) { + + // Add interrow material before first row: + String sInter = formatter.getInterrowMaterial(0); + if (sInter.length()>0) { + ldp.append(sInter).nl(); + } + + // Add header rows + handleRows(0,table.getFirstBodyRow(),ldp,oc); + } + } + + private void handleBodyRows(LaTeXDocumentPortion ldp, Context oc) { + if (table.getFirstBodyRow()==0) { + // No head, add interrow material before first row: + String sInter = formatter.getInterrowMaterial(0); + if (sInter.length()>0) { + ldp.append(sInter).nl(); + } + } + + // Add body rows + handleRows(table.getFirstBodyRow(),table.getRowCount(),ldp,oc); + ldp.nl(); + } + + private void handleRows(int nStart, int nEnd, LaTeXDocumentPortion ldp, Context oc) { + int nColCount = table.getColCount(); + for (int nRow=nStart; nRow<nEnd; nRow++) { + // Export columns in this row + Context icRow = (Context) oc.clone(); + BeforeAfter baRow = new BeforeAfter(); + formatter.applyRowStyle(nRow,baRow,icRow); + if (!baRow.isEmpty()) { + ldp.append(baRow.getBefore()); + if (!formatter.isSimple()) { ldp.nl(); } + } + int nCol = 0; + while (nCol<nColCount) { + Element cell = (Element) table.getCell(nRow,nCol); + if (XMLString.TABLE_TABLE_CELL.equals(cell.getNodeName())) { + Context icCell = (Context) icRow.clone(); + BeforeAfter baCell = new BeforeAfter(); + formatter.applyCellStyle(nRow,nCol,baCell,icCell); + ldp.append(baCell.getBefore()); + if (nCol==nColCount-1) { icCell.setInLastTableColumn(true); } + palette.getBlockCv().traverseBlockText(cell,ldp,icCell); + ldp.append(baCell.getAfter()); + } + // Otherwise ignore; the cell is covered by a \multicolumn entry. + // (table:covered-table-cell) + int nColSpan = Misc.getPosInteger(cell.getAttribute( + XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1); + if (nCol+nColSpan<nColCount) { + if (formatter.isSimple()) { ldp.append(" & "); } + else { ldp.append(" &").nl(); } + } + nCol+=nColSpan; + } + ldp.append("\\\\").append(formatter.getInterrowMaterial(nRow+1)); + // Add newline, except after last row + if (nRow<nEnd-1) { ldp.nl(); } + } + } + + } + + +} diff --git a/source/java/writer2latex/latex/TableFormatter.java b/source/java/writer2latex/latex/TableFormatter.java new file mode 100644 index 0000000..2f153ea --- /dev/null +++ b/source/java/writer2latex/latex/TableFormatter.java @@ -0,0 +1,451 @@ +/************************************************************************ + * + * TableFormatter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-19) + * + */ + +package writer2latex.latex; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.util.*; +import writer2latex.office.*; +import writer2latex.latex.util.BeforeAfter; +import writer2latex.latex.util.Context; + +/** + * <p>This class converts OOo table styles to LaTeX.</p> + * <p> In OOo the table style is distributed on table, column and cell styles. + * <p> In LaTeX we have to rearrange this information slightly, so this class + * takes care of that.</p> + */ +public class TableFormatter extends ConverterHelper { + + //private boolean bApplyCellFormat; + private TableReader table; + private char[][] cAlign; + private char[] cGlobalAlign; + private boolean[][] bHBorder; + private boolean[][] bVBorder; + private boolean[] bGlobalVBorder; + private String[] sRowColor; + private String[][] sCellColor; + private String[] sColumnWidth; + private boolean bIsLongtable; + private boolean bIsSupertabular; + private boolean bIsTabulary; + private boolean bIsColortbl; + private boolean bIsSimple; + + /** <p>Constructor: Create from a TableReader.</p> + */ + public TableFormatter(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette, + TableReader table, boolean bAllowPageBreak, boolean bIsInTable) { + super(ofr,config,palette); + this.table = table; + //bApplyCellFormat = config.formatting()>=LaTeXConfig.CONVERT_MOST; + int nRowCount = table.getRowCount(); + int nColCount = table.getColCount(); + int nSimpleTableLimit = config.getSimpleTableLimit(); + + // Step 1: Collect alignment and identify simple tables + bIsSimple = true; + cAlign = new char[nRowCount][nColCount]; + cGlobalAlign = new char[nColCount]; + // Keep track of characters to be counted + int[] nPendingChars = new int[nRowCount]; + int[] nPendingColSpan = new int[nRowCount]; + int nTableWidth = 0; + + for (int nCol=0; nCol<nColCount; nCol++) { + // Collect chars to be counted in this column + for (int nRow=0; nRow<nRowCount; nRow++) { + Element cell = table.getCell(nRow, nCol); + if (Misc.isElement(cell, XMLString.TABLE_TABLE_CELL)) { + // Now we're here: Collect alignment + if (OfficeReader.isSingleParagraph(cell)) { + Node par = Misc.getChildByTagName(cell,XMLString.TEXT_P); + StyleWithProperties style = ofr.getParStyle(Misc.getAttribute(par,XMLString.TEXT_STYLE_NAME)); + cAlign[nRow][nCol] = 'l'; + if (style!=null) { + String sAlign = style.getProperty(XMLString.FO_TEXT_ALIGN,true); + if ("center".equals(sAlign)) { cAlign[nRow][nCol] = 'c'; } + else if ("end".equals(sAlign)) { cAlign[nRow][nCol] = 'r'; } + } + + } + else { + // Found cell with more than one paragraph + bIsSimple = false; + } + // Collect characters (the cell contains this many characters that should be distributed over that many columns) + nPendingChars[nRow] = OfficeReader.getCharacterCount(cell); + nPendingColSpan[nRow] = Misc.getPosInteger(cell.getAttribute(XMLString.TABLE_NUMBER_COLUMNS_SPANNED), 1); + } + } + // Determine the number of characters to count *now* (because they cannot be postponed to next column) + int nColChars = 0; + for (int nRow=0; nRow<nRowCount; nRow++) { + if (nPendingColSpan[nRow]==1) { + nColChars = Math.max(nColChars, nPendingChars[nRow]); + } + } + // Reduce pending chars and increase table width + nTableWidth += nColChars; + for (int nRow=0; nRow<nRowCount; nRow++) { + if (nPendingColSpan[nRow]>=1) { + nPendingChars[nRow] = Math.max(0, nPendingChars[nRow]-nColChars); + nPendingColSpan[nRow]--; + } + } + } + if (nTableWidth>nSimpleTableLimit) bIsSimple = false; + + // Step 2: Create global alignment + for (int nCol=0; nCol<nColCount; nCol++) { + int nCenter = 0; + int nRight = 0; + for (int nRow=0; nRow<nRowCount; nRow++) { + if (cAlign[nRow][nCol]=='c') { nCenter++; } + else if (cAlign[nRow][nCol]=='r') { nRight++; } + } + cGlobalAlign[nCol] = 'l'; + int nLeft = nColCount-nCenter-nRight; + if (nCenter>nLeft) { + if (nRight>nLeft) { cGlobalAlign[nCol] = 'r'; } + else { cGlobalAlign[nCol] = 'c'; } + } + else if (nRight>nLeft) { + cGlobalAlign[nCol] = 'r'; + } + } + + // Step 3: Initialize borders: + bHBorder = new boolean[nRowCount+1][nColCount]; + for (int nRow=0; nRow<=nRowCount; nRow++) { + for (int nCol=0; nCol<nColCount; nCol++) { + bHBorder[nRow][nCol] = false; + } + } + bVBorder = new boolean[nRowCount][nColCount+1]; + for (int nRow=0; nRow<nRowCount; nRow++) { + for (int nCol=0; nCol<=nColCount; nCol++) { + bVBorder[nRow][nCol] = false; + } + } + + // Step 4: Collect borders from cell styles: + for (int nRow=0; nRow<nRowCount; nRow++) { + int nCol = 0; + while (nCol<nColCount) { + Node cell = table.getCell(nRow,nCol); + String sStyleName = Misc.getAttribute(cell,XMLString.TABLE_STYLE_NAME); + StyleWithProperties style = ofr.getCellStyle(sStyleName); + int nColSpan = Misc.getPosInteger(Misc.getAttribute(cell, + XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1); + boolean bLeft = false; + boolean bRight = false; + boolean bTop = false; + boolean bBottom = false; + if (style!=null) { + String sBorder = style.getProperty(XMLString.FO_BORDER); + if (sBorder!=null && !"none".equals(sBorder)) { + bLeft = true; bRight = true; bTop = true; bBottom = true; + } + sBorder = style.getProperty(XMLString.FO_BORDER_LEFT); + if (sBorder!=null && !"none".equals(sBorder)) { + bLeft = true; + } + sBorder = style.getProperty(XMLString.FO_BORDER_RIGHT); + if (sBorder!=null && !"none".equals(sBorder)) { + bRight = true; + } + sBorder = style.getProperty(XMLString.FO_BORDER_TOP); + if (sBorder!=null && !"none".equals(sBorder)) { + bTop = true; + } + sBorder = style.getProperty(XMLString.FO_BORDER_BOTTOM); + if (sBorder!=null && !"none".equals(sBorder)) { + bBottom = true; + } + } + bVBorder[nRow][nCol] |= bLeft; + bVBorder[nRow][nCol+nColSpan] |= bRight; + do { + bHBorder[nRow][nCol] |= bTop; + bHBorder[nRow+1][nCol] |= bBottom; + nCol++; + } while (--nColSpan>0); + } + } + + // Step 5: Create global vertical borders based on simple majority + // (in order to minimize the number of \multicolum{1} entries) + bGlobalVBorder = new boolean[nColCount+1]; + for (int nCol=0; nCol<=nColCount; nCol++) { + int nBalance = 0; + for (int nRow=0; nRow<nRowCount; nRow++) { + nBalance += bVBorder[nRow][nCol] ? 1 : -1; + } + bGlobalVBorder[nCol] = nBalance>0; + } + + // Step 6: Get background colors + sRowColor = new String[nRowCount]; + sCellColor = new String[nRowCount][nColCount]; + if (config.useColortbl()) { + // Table background + String sTableColor = null; + StyleWithProperties tableStyle = ofr.getTableStyle(table.getTableStyleName()); + if (tableStyle!=null) { + sTableColor = tableStyle.getProperty(XMLString.FO_BACKGROUND_COLOR); + } + + // Row background + for (int nRow=0; nRow<nRowCount; nRow++) { + StyleWithProperties rowStyle = ofr.getRowStyle(table.getRow(nRow).getStyleName()); + if (rowStyle!=null) { + sRowColor[nRow] = rowStyle.getProperty(XMLString.FO_BACKGROUND_COLOR); + } + if (sRowColor[nRow]==null) { + sRowColor[nRow] = sTableColor; + } + if (sRowColor[nRow]!=null) { + bIsColortbl = true; + } + } + + // Cell background + for (int nRow=0; nRow<nRowCount; nRow++) { + for (int nCol=0; nCol<nColCount; nCol++) { + StyleWithProperties cellStyle = ofr.getCellStyle(Misc.getAttribute(table.getCell(nRow,nCol),XMLString.TABLE_STYLE_NAME)); + if (cellStyle!=null) { + sCellColor[nRow][nCol] = cellStyle.getProperty(XMLString.FO_BACKGROUND_COLOR); + if (sCellColor[nRow][nCol]!=null) { + bIsColortbl = true; + if (sCellColor[nRow][nCol].equals(sRowColor[nRow])) { + // Avoid redundant cell background + sCellColor[nRow][nCol] = null; + } + } + } + } + } + + } + + // Step 7: Read column style information + sColumnWidth = new String[nColCount]; + for (int nCol=0; nCol<nColCount; nCol++) { + StyleWithProperties colStyle + = ofr.getColumnStyle(table.getCol(nCol).getStyleName()); + if (colStyle!=null) { + sColumnWidth[nCol] + = colStyle.getProperty(XMLString.STYLE_COLUMN_WIDTH); + } + if (sColumnWidth[nCol]==null) { // Emergency! should never happen! + sColumnWidth[nCol]="2cm"; + } + } + + // Step 8: Identify longtable, supertabular or tabulary + bIsLongtable = false; bIsSupertabular = false; bIsTabulary = false; + if (!table.isSubTable() && !bIsInTable) { + String sStyleName = table.getTableStyleName(); + StyleWithProperties style = ofr.getTableStyle(sStyleName); + boolean bMayBreak = style==null || + !"false".equals(style.getProperty(XMLString.STYLE_MAY_BREAK_BETWEEN_ROWS)); + if (config.useLongtable() && bMayBreak && bAllowPageBreak) { + bIsLongtable = true; + } + else if (config.useSupertabular() && bMayBreak && bAllowPageBreak) { + bIsSupertabular = true; + } + else if (!bIsSimple && config.useTabulary()) { + bIsTabulary = true; + } + } + + } + + /** is this a longtable? */ + public boolean isLongtable() { return bIsLongtable; } + + /** is this a supertabular? */ + public boolean isSupertabular() { return bIsSupertabular; } + + /** is this a tabulary? */ + public boolean isTabulary() { return bIsTabulary; } + + /** is this a colortbl? */ + public boolean isColortbl() { return bIsColortbl; } + + /** is this a simple table (lcr columns rather than p{})? */ + public boolean isSimple() { return bIsSimple; } + + /** + * <p>Create table environment based on table style.</p> + * <p>Returns eg. "\begin{longtable}{m{2cm}|m{4cm}}", "\end{longtable}".</p> + */ + public void applyTableStyle(BeforeAfter ba, BeforeAfter baAlign) { + // Read formatting info from table style + // Only supported properties are alignment and may-break-between-rows. + String sStyleName = table.getTableStyleName(); + StyleWithProperties style = ofr.getTableStyle(sStyleName); + char cAlign = 'c'; + if (style!=null && !table.isSubTable()) { + String s = style.getProperty(XMLString.TABLE_ALIGN); + if ("left".equals(s)) { cAlign='l'; } + else if ("right".equals(s)) { cAlign='r'; } + } + String sAlign="center"; + switch (cAlign) { + case 'c': sAlign="center"; break; + case 'r': sAlign="flushright"; break; + case 'l': sAlign="flushleft"; + } + + // Create table alignment (for supertabular, tabular and tabulary) + if (!bIsLongtable && !table.isSubTable()) { + baAlign.add("\\begin{"+sAlign+"}\n","\\end{"+sAlign+"}\n"); + } + + // Create table declaration + if (bIsLongtable) { + ba.add("\\begin{longtable}["+cAlign+"]", "\\end{longtable}"); + } + else if (bIsSupertabular) { + ba.add("\\begin{supertabular}","\\end{supertabular}"); + } + else if (bIsTabulary) { + ba.add("\\begin{tabulary}{"+table.getTableWidth()+"}","\\end{tabulary}"); + } + else if (!table.isSubTable()) { + ba.add("\\begin{tabular}","\\end{tabular}"); + } + else { // subtables should occupy the entire width, including padding! + ba.add("\\hspace*{-\\tabcolsep}\\begin{tabular}", + "\\end{tabular}\\hspace*{-\\tabcolsep}"); + } + + // columns + ba.add("{",""); + if (bGlobalVBorder[0]) { ba.add("|",""); } + int nColCount = table.getColCount(); + for (int nCol=0; nCol<nColCount; nCol++){ + if (bIsSimple) { + ba.add(Character.toString(cGlobalAlign[nCol]),""); + } + else if (!bIsTabulary) { + // note: The column width in OOo includes padding, which we subtract + ba.add("m{"+Misc.add(sColumnWidth[nCol],"-0.2cm")+"}",""); + } + else { + ba.add("J",""); + } + if (bGlobalVBorder[nCol+1]) { ba.add("|",""); } + } + ba.add("}",""); + } + + /** <p>Create interrow material</p> */ + public String getInterrowMaterial(int nRow) { + int nColCount = table.getColCount(); + int nCount = 0; + for (int nCol=0; nCol<nColCount; nCol++) { + if (bHBorder[nRow][nCol]) { nCount++; } + } + if (nCount==0) { // no borders at this row + return ""; + } + else if (nCount==nColCount) { // complete set of borders + return "\\hline"; + } + else { // individual borders for each column + StringBuffer buf = new StringBuffer(); + buf.append("\\hhline{"); + for (int nCol=0; nCol<nColCount; nCol++) { + if (bHBorder[nRow][nCol]) { buf.append("-"); } + else { buf.append("~"); } + } + buf.append("}"); +/* TODO: hhline.sty should be optional, and i not used, do as before: + boolean bInCline = false; + for (int nCol=0; nCol<nColCount; nCol++) { + if (bInCline && !bHBorder[nRow][nCol]) { // close \cline + buf.append(nCol).append("}"); + bInCline = false; + } + else if (!bInCline && bHBorder[nRow][nCol]) { // open \cline + buf.append("\\cline{").append(nCol+1).append("-"); + bInCline = true; + } + } + if (bInCline) { buf.append(nColCount).append("}"); } +*/ + return buf.toString(); + } + } + + /** <p>Get material to put before a table row (background color) + */ + public void applyRowStyle(int nRow, BeforeAfter ba, Context context) { + palette.getColorCv().applyBgColor("\\rowcolor",sRowColor[nRow],ba,context); + } + + /** Get material to put before and after a table cell. + * In case of columnspan or different borders this will contain a \multicolumn command. + */ + public void applyCellStyle(int nRow, int nCol, BeforeAfter ba, Context context) { + Node cell = table.getCell(nRow,nCol); + int nColSpan = Misc.getPosInteger(Misc.getAttribute(cell, + XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1); + // Construct column declaration as needed + boolean bNeedLeft = (nCol==0) && (bVBorder[nRow][0]!=bGlobalVBorder[0]); + boolean bNeedRight = bVBorder[nRow][nCol+1]!=bGlobalVBorder[nCol+1]; + boolean bNeedAlign = bIsSimple && cGlobalAlign[nCol]!=cAlign[nRow][nCol]; + // calculate column width + String sTotalColumnWidth = sColumnWidth[nCol]; + for (int i=nCol+1; i<nCol+nColSpan; i++) { + sTotalColumnWidth = Misc.add(sTotalColumnWidth,sColumnWidth[i]); + } + sTotalColumnWidth = Misc.add(sTotalColumnWidth,"-0.2cm"); + + if (bNeedAlign || bNeedLeft || bNeedRight || nColSpan>1) { + ba.add("\\multicolumn{"+nColSpan+"}{",""); + if (nCol==0 && bVBorder[nRow][0]) { ba.add("|",""); } + if (bIsSimple) { + ba.add(Character.toString(cAlign[nRow][nCol]),""); + } + else { + ba.add("m{"+sTotalColumnWidth+"}",""); + } + if (bVBorder[nRow][nCol+nColSpan]) { ba.add("|",""); } + ba.add("}{","}"); + } + + palette.getColorCv().applyBgColor("\\cellcolor",sCellColor[nRow][nCol],ba,context); + + } +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/config/clean.xml b/source/java/writer2latex/latex/config/clean.xml new file mode 100644 index 0000000..1058554 --- /dev/null +++ b/source/java/writer2latex/latex/config/clean.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- clean.xml + This is a sample configuration file for Writer2LaTeX. + The options are set to produce a clean + LaTeX file from an arbitrary Writer document + - at the expense of loss of formatting. + An even cleaner LaTeX file is produced with ultraclean.xml. + --> + +<config> + <option name="documentclass" value="article" /> + <option name="backend" value="generic" /> + <option name="inputencoding" value="ascii" /> + <option name="multilingual" value="false" /> + <option name="use_ooomath" value="false" /> + <option name="use_color" value="true" /> + <option name="use_colortbl" value="false" /> + <option name="use_geometry" value="true" /> + <option name="use_fancyhdr" value="false" /> + <option name="use_hyperref" value="true" /> + <option name="use_caption" value="true" /> + <option name="use_endnotes" value="false" /> + <option name="use_bibtex" value="true" /> + <option name="bibtex_style" value="plain" /> + <option name="formatting" value="ignore_most" /> + <option name="page_formatting" value="convert_geometry" /> + <option name="ignore_empty_paragraphs" value="true" /> + <option name="ignore_hard_page_breaks" value="false" /> + <option name="ignore_hard_line_breaks" value="false" /> + <option name="ignore_double_spaces" value="true" /> + <option name="debug" value="false" /> + <heading-map max-level="5"> + <heading-level-map writer-level="1" name="section" level="1" /> + <heading-level-map writer-level="2" name="subsection" level="2" /> + <heading-level-map writer-level="3" name="subsubsection" level="3" /> + <heading-level-map writer-level="4" name="paragraph" level="4" /> + <heading-level-map writer-level="5" name="subparagraph" level="5" /> + </heading-map> + <custom-preamble /> + + <!-- Style maps: These rules defines how styles in OOo are mapped to LaTeX code. + A number of predefined Writer styles are converted --> + + <!-- "Title" is mapped to \maketitle. If the user chooses to export meta data, + the author and date will be inserted automatically --> + <style-map name="Title" class="paragraph" before="\title{" after="} \maketitle" line-break="false" /> + + <!-- "Quotations" is mapped to a quotation environment --> + <style-map name="Quotations" family="paragraph-block" next="Quotations" before="\begin{quotation}" after="\end{quotation}" /> + <style-map name="Quotations" family="paragraph" before="" after="" /> + + <!-- Preformatted Text is mapped to a verbatim environment + Note the attribute verbatim, which instructs OOo to output the content + verbatim (characters not available in the inputencoding will be replaced + by question marks; other content will be lost). --> + <style-map name="Preformatted Text" family="paragraph-block" next="Preformatted Text" before="\begin{verbatim}" after="\end{verbatim}" /> + <style-map name="Preformatted Text" family="paragraph" before="" after="" verbatim="true" /> + + <!-- "Horizontal line" is mapped to a \hrule --> + <style-map name="Horizontal Line" family="paragraph" before="" after=" \begin{center}\hrule\end{center}" /> + + <!-- "Emphasis" text style is mapped to \emph --> + <style-map name="Emphasis" family="text" before="\emph{" after="}" /> + + <!-- "Strong Emphasis" text style is mapped to \textbf --> + <style-map name="Strong Emphasis" family="text" before="\textbf{" after="}" /> + + <!-- "Teletype" text style is mapped to \texttt --> + <style-map name="Teletype" family="text" before="\texttt{" after="}" /> + + <!-- "List Heading" and "List Contents" are mapped to a description environment --> + <style-map name="List Heading" family="paragraph-block" next="List Heading;List Contents" before="\begin{description}" after="\end{description}"/> + <style-map name="List Heading" family="paragraph" before="\item[" after="]" line-break="false" /> + <style-map name="List Contents" family="paragraph" before="" after="" /> + +</config> + diff --git a/source/java/writer2latex/latex/config/default.xml b/source/java/writer2latex/latex/config/default.xml new file mode 100644 index 0000000..dda78e6 --- /dev/null +++ b/source/java/writer2latex/latex/config/default.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- default.xml + This is a sample configuration file for Writer2LaTeX. + It sets no options, thus using defaults everywhere + --> + +<config/> + diff --git a/source/java/writer2latex/latex/config/pdfprint.xml b/source/java/writer2latex/latex/config/pdfprint.xml new file mode 100644 index 0000000..51b5585 --- /dev/null +++ b/source/java/writer2latex/latex/config/pdfprint.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- pdfprint.xml + This is a sample configuration file for Writer2LaTeX. + The output will be for pdfTeX, using all the packages pifont, ifsym, + wasysym, eurosym, hyperref, endnotes and ulem. --> + +<config> + <option name="documentclass" value="article" /> + <option name="backend" value="pdftex" /> + <option name="inputencoding" value="ascii" /> + <option name="use_geometry" value="true" /> + <option name="use_fancyhdr" value="true" /> + <option name="use_ooomath" value="true" /> + <option name="use_pifont" value="true" /> + <option name="use_ifsym" value="true" /> + <option name="use_wasysym" value="true" /> + <option name="use_eurosym" value="true" /> + <option name="use_color" value="true" /> + <option name="use_colortbl" value="true" /> + <option name="use_hyperref" value="true" /> + <option name="use_endnotes" value="true" /> + <option name="use_ulem" value="true" /> + <option name="use_lastpage" value="true" /> + <option name="formatting" value="convert_all" /> + <option name="page_formatting" value="convert_all" /> + <option name="ignore_empty_paragraphs" value="false" /> + <option name="ignore_hard_page_breaks" value="false" /> + <option name="ignore_hard_line_breaks" value="false" /> + <option name="ignore_double_spaces" value="false" /> + <option name="debug" value="false" /> + <heading-map max-level="5"> + <heading-level-map writer-level="1" name="section" level="1" /> + <heading-level-map writer-level="2" name="subsection" level="2" /> + <heading-level-map writer-level="3" name="subsubsection" level="3" /> + <heading-level-map writer-level="4" name="paragraph" level="4" /> + <heading-level-map writer-level="5" name="subparagraph" level="5" /> + </heading-map> + <!-- We add \sloppy to avoid overful hboxes. To get better results, + this should be removed and overful hboxes fixed by hand. --> + <custom-preamble>\sloppy</custom-preamble> + +</config> + diff --git a/source/java/writer2latex/latex/config/pdfscreen.xml b/source/java/writer2latex/latex/config/pdfscreen.xml new file mode 100644 index 0000000..1578fbe --- /dev/null +++ b/source/java/writer2latex/latex/config/pdfscreen.xml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- pdfscreen.xml + This is a sample configuration file for Writer2LaTeX. + The output will be for pdfTeX, using all the packages pifont, ifsym, + wasysym, eurosym, hyperref, endnotes and ulem. + The package pdfscreen.sty will be loaded to create a pdf file + suitable for viewing on screen. --> + +<config> + <option name="documentclass" value="article" /> + <option name="backend" value="pdftex" /> + <option name="inputencoding" value="ascii" /> + <option name="use_ooomath" value="true" /> + <option name="use_pifont" value="true" /> + <option name="use_ifsym" value="true" /> + <option name="use_wasysym" value="true" /> + <option name="use_bbding" value="false" /> + <option name="use_eurosym" value="true" /> + <option name="use_color" value="true" /> + <option name="use_colortbl" value="true" /> + <option name="use_hyperref" value="true" /> + <option name="use_endnotes" value="true" /> + <option name="use_ulem" value="true" /> + <option name="use_lastpage" value="true" /> + <option name="formatting" value="convert_all" /> + <option name="page_formatting" value="ignore_all" /> + <option name="ignore_empty_paragraphs" value="false" /> + <option name="ignore_hard_page_breaks" value="false" /> + <option name="ignore_hard_line_breaks" value="false" /> + <option name="ignore_double_spaces" value="true" /> + <option name="debug" value="false" /> + <heading-map max-level="5"> + <heading-level-map writer-level="1" name="section" level="1" /> + <heading-level-map writer-level="2" name="subsection" level="2" /> + <heading-level-map writer-level="3" name="subsubsection" level="3" /> + <heading-level-map writer-level="4" name="paragraph" level="4" /> + <heading-level-map writer-level="5" name="subparagraph" level="5" /> + </heading-map> + + <!-- load pdfscreen.sty with suitable options. (Note that export of page + formatting is disabled above; pdfscreen.sty takes care of page setup.) + The lines are relatively short, so we add \sloppy to avoid overful hboxes. + --> + <custom-preamble>\usepackage{palatino} +\usepackage[bluelace,screen,nopanel,sectionbreak]{pdfscreen} +%\hypersetup{pdfpagemode={FullScreen}} +\margins{0.5in}{0.5in}{0.5in}{0.5in} +\screensize{6in}{8in} +\sloppy</custom-preamble> +</config> diff --git a/source/java/writer2latex/latex/config/ultraclean.xml b/source/java/writer2latex/latex/config/ultraclean.xml new file mode 100644 index 0000000..a3ef9f8 --- /dev/null +++ b/source/java/writer2latex/latex/config/ultraclean.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- ultraclean.xml + This is a sample configuration file for Writer2LaTeX. + The options are set to produce the cleanest possible + LaTeX file from an arbitrary Writer document + - by removing practically all formatting. + --> + + +<config> + <option name="documentclass" value="article" /> + <option name="backend" value="generic" /> + <option name="inputencoding" value="ascii" /> + <option name="multilingual" value="false" /> + <option name="use_ooomath" value="false" /> + <option name="use_color" value="false" /> + <option name="use_colortbl" value="false" /> + <option name="use_geometry" value="false" /> + <option name="use_fancyhdr" value="false" /> + <option name="use_hyperref" value="false" /> + <option name="use_caption" value="true" /> + <option name="use_endnotes" value="false" /> + <option name="use_bibtex" value="true" /> + <option name="bibtex_style" value="plain" /> + <option name="formatting" value="ignore_all" /> + <option name="page_formatting" value="ignore_all" /> + <option name="ignore_empty_paragraphs" value="true" /> + <option name="ignore_hard_page_breaks" value="true" /> + <option name="ignore_hard_line_breaks" value="true" /> + <option name="ignore_double_spaces" value="true" /> + <option name="debug" value="false" /> + <heading-map max-level="5"> + <heading-level-map writer-level="1" name="section" level="1" /> + <heading-level-map writer-level="2" name="subsection" level="2" /> + <heading-level-map writer-level="3" name="subsubsection" level="3" /> + <heading-level-map writer-level="4" name="paragraph" level="4" /> + <heading-level-map writer-level="5" name="subparagraph" level="5" /> + </heading-map> + <custom-preamble /> + + <!-- Style maps: These rules defines how styles in OOo are mapped to LaTeX code. + A number of predefined Writer styles are converted --> + + <!-- "Title" is mapped to \maketitle. If the user chooses to export meta data, + the author and date will be inserted automatically --> + <style-map name="Title" class="paragraph" before="\title{" after="} \maketitle" line-break="false" /> + + <!-- "Quotations" is mapped to a quotation environment --> + <style-map name="Quotations" family="paragraph-block" next="Quotations" before="\begin{quotation}" after="\end{quotation}" /> + <style-map name="Quotations" family="paragraph" before="" after="" /> + + <!-- Preformatted Text is mapped to a verbatim environment + Note the attribute verbatim, which instructs OOo to output the content + verbatim (characters not available in the inputencoding will be replaced + by question marks; other content will be lost). --> + <style-map name="Preformatted Text" family="paragraph-block" next="Preformatted Text" before="\begin{verbatim}" after="\end{verbatim}" /> + <style-map name="Preformatted Text" family="paragraph" before="" after="" verbatim="true" /> + + <!-- "Horizontal line" is mapped to a \hrule --> + <style-map name="Horizontal Line" family="paragraph" before="" after=" \begin{center}\hrule\end{center}" /> + + <!-- "Emphasis" text style is mapped to \emph --> + <style-map name="Emphasis" family="text" before="\emph{" after="}" /> + + <!-- "Strong Emphasis" text style is mapped to \textbf --> + <style-map name="Strong Emphasis" family="text" before="\textbf{" after="}" /> + + <!-- "Teletype" text style is mapped to \texttt --> + <style-map name="Teletype" family="text" before="\texttt{" after="}" /> + + <!-- "List Heading" and "List Contents" are mapped to a description environment --> + <style-map name="List Heading" family="paragraph-block" next="List Heading;List Contents" before="\begin{description}" after="\end{description}"/> + <style-map name="List Heading" family="paragraph" before="\item[" after="]" line-break="false" /> + <style-map name="List Contents" family="paragraph" before="" after="" /> + +</config> + diff --git a/source/java/writer2latex/latex/i18n/ClassicI18n.java b/source/java/writer2latex/latex/i18n/ClassicI18n.java new file mode 100644 index 0000000..cc4a2a8 --- /dev/null +++ b/source/java/writer2latex/latex/i18n/ClassicI18n.java @@ -0,0 +1,697 @@ +/************************************************************************ + * + * ClassicI18n.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-16) + * + */ + +package writer2latex.latex.i18n; + +import java.io.*; +import java.util.Hashtable; +import java.util.Stack; +//import java.util.Vector; +//import java.util.Enumeration; +import java.util.Iterator; + +import javax.xml.parsers.SAXParserFactory; +import javax.xml.parsers.SAXParser; + +import writer2latex.util.CSVList; +import writer2latex.office.*; +import writer2latex.latex.LaTeXConfig; +import writer2latex.latex.LaTeXDocumentPortion; +import writer2latex.latex.ConverterPalette; +import writer2latex.latex.util.BeforeAfter; + +/** This class (and the helpers in the same package) takes care of i18n in + * Writer2LaTeX. In classic LaTeX, i18n is a mixture of inputencodings, fontencodings + * and babel languages. The class ClassicI18n thus manages these, and in particular + * implements a Unicode->LaTeX translation that can handle different + * inputencodings and fontencodings. + * The translation is table driven, using symbols.xml (embedded in the jar) + * Various sections of symbols.xml handles different cases: + * <ul> + * <li>common symbols in various font encodings such as T1, T2A, LGR etc.</li> + * <li>input encodings such as ISO-8859-1 (latin-1), ISO-8859-7 (latin/greek) etc.</li> + * <li>additional symbol fonts such as wasysym, dingbats etc.</li> + * <li>font-specific symbols, eg. for 8-bit fonts/private use area</li> + * </ul> + * The class uses the packages inputenc, fontenc, babel, tipa, bbding, + * ifsym, pifont, eurosym, amsmath, wasysym, amssymb, amsfonts and textcomp + * in various combinations depending on the configuration. + */ +public class ClassicI18n extends I18n { + // **** Static data and methods: Inputencodings **** + public static final int ASCII = 0; + public static final int LATIN1 = 1; // ISO Latin 1 (ISO-8859-1) + public static final int LATIN2 = 2; // ISO Latin 1 (ISO-8859-1) + public static final int ISO_8859_7 = 3; // ISO latin/greek + public static final int CP1250 = 4; // Microsoft Windows Eastern European + public static final int CP1251 = 5; // Microsoft Windows Cyrillic + public static final int KOI8_R = 6; // Latin/russian + public static final int UTF8 = 7; // UTF-8 + + // Read an inputencoding from a string + public static final int readInputenc(String sInputenc) { + if ("ascii".equals(sInputenc)) return ASCII; + else if ("latin1".equals(sInputenc)) return LATIN1; + else if ("latin2".equals(sInputenc)) return LATIN2; + else if ("iso-8859-7".equals(sInputenc)) return ISO_8859_7; + else if ("cp1250".equals(sInputenc)) return CP1250; + else if ("cp1251".equals(sInputenc)) return CP1251; + else if ("koi8-r".equals(sInputenc)) return KOI8_R; + else if ("utf8".equals(sInputenc)) return UTF8; + else return ASCII; // unknown = ascii + } + + // Return the LaTeX name of an inputencoding + public static final String writeInputenc(int nInputenc) { + switch (nInputenc) { + case ASCII : return "ascii"; + case LATIN1 : return "latin1"; + case LATIN2 : return "latin2"; + case ISO_8859_7 : return "iso-8859-7"; + case CP1250 : return "cp1250"; + case CP1251 : return "cp1251"; + case KOI8_R : return "koi8-r"; + case UTF8 : return "utf8"; + default : return "???"; + } + } + + // Return the java i18n name of an inputencoding + public static final String writeJavaEncoding(int nInputenc) { + switch (nInputenc) { + case ASCII : return "ASCII"; + case LATIN1 : return "ISO8859_1"; + case LATIN2 : return "ISO8859_2"; + case ISO_8859_7 : return "ISO8859_7"; + case CP1250 : return "Cp1250"; + case CP1251 : return "Cp1251"; + case KOI8_R : return "KOI8_R"; + case UTF8 : return "UTF-8"; + default : return "???"; + } + } + + // **** Static data and methods: Fontencodings **** + private static final int T1_ENC = 1; + private static final int T2A_ENC = 2; + private static final int T3_ENC = 4; + private static final int LGR_ENC = 8; + private static final int ANY_ENC = 15; + + // read set of font encodings from a string + public static final int readFontencs(String sFontencs) { + sFontencs = sFontencs.toUpperCase(); + if ("ANY".equals(sFontencs)) return ANY_ENC; + int nFontencs = 0; + if (sFontencs.indexOf("T1")>=0) nFontencs+=T1_ENC; + if (sFontencs.indexOf("T2A")>=0) nFontencs+=T2A_ENC; + if (sFontencs.indexOf("T3")>=0) nFontencs+=T3_ENC; + if (sFontencs.indexOf("LGR")>=0) nFontencs+=LGR_ENC; + return nFontencs; + } + + // return string representation of a single font encoding + /*private static final String writeFontenc(int nFontenc) { + switch (nFontenc) { + case T1_ENC: return "T1"; + case T2A_ENC: return "T2A"; + case T3_ENC: return "T3"; + case LGR_ENC: return "LGR"; + } + return null; + }*/ + + // check that a given set of font encodings contains a specific font encoding + private static final boolean supportsFontenc(int nFontencs, int nFontenc) { + return (nFontencs & nFontenc) != 0; + } + + // get one fontencoding from a set of fontencodings + private static final int getFontenc(int nFontencs) { + if (supportsFontenc(nFontencs,T1_ENC)) return T1_ENC; + if (supportsFontenc(nFontencs,T2A_ENC)) return T2A_ENC; + if (supportsFontenc(nFontencs,T3_ENC)) return T3_ENC; + if (supportsFontenc(nFontencs,LGR_ENC)) return LGR_ENC; + return 0; + } + + // get the font encoding for a specific iso language + private static final int getFontenc(String sLang) { + // Greek uses "local greek" encoding + if ("el".equals(sLang)) return LGR_ENC; + // Russian, ukrainian, bulgarian and serbian uses T2A encoding + else if ("ru".equals(sLang)) return T2A_ENC; + else if ("uk".equals(sLang)) return T2A_ENC; + else if ("bg".equals(sLang)) return T2A_ENC; + else if ("sr".equals(sLang)) return T2A_ENC; + // Other languages uses T1 encoding + else return T1_ENC; + } + + // return cs for a fontencoding + private static final String getFontencCs(int nFontenc) { + switch (nFontenc) { + case T1_ENC: return "\\textlatin"; // requires babel + case T2A_ENC: return "\\textcyrillic"; // requires babel with russian, bulgarian or ukrainian option + case T3_ENC: return "\\textipa"; // requires tipa.sty + case LGR_ENC: return "\\textgreek"; // requires babel with greek option + default: return null; + } + } + + // End of static part of I18n! + + // **** Global variables **** + private Hashtable babelLanguages; // mappings iso->babel language + + // Unicode translation + private Hashtable tableSet; // all tables + private UnicodeTable table; // currently active table (top of stack) + private Stack tableStack; // stack of active tables + private UnicodeStringParser ucparser; // Unicode string parser + + // Collected data + private int nDefaultFontenc; // Fontenc for the default language + private boolean bT2A = false; // Do we use cyrillic letters? + private boolean bGreek = false; // Do we use greek letters? + private boolean bPolytonicGreek = false; // Do we use polytonic greek letters? + + // **** Constructors **** + + /** Construct a new ClassicI18n as ConverterHelper + * @param ofr the OfficeReader to get language information from + * @param config the configuration which determines the symbols to use + * @param palette the ConverterPalette (unused) + */ + public ClassicI18n(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + // We don't need the palette and the office reader is only used to + // identify the default language + + // Set up table for iso->babel translation + prepareBabelLanguages(); + + nDefaultFontenc = getFontenc(sDefaultLanguage); + + // Unicode stuff + ucparser = new UnicodeStringParser(); + + String sSymbols="ascii"; // always load common symbols + if (config.getInputencoding()!=ASCII) { + sSymbols+="|"+writeInputenc(config.getInputencoding()); + } + + if (config.useWasysym()) sSymbols+="|wasysym"; + if (config.useBbding()) sSymbols+="|bbding"; + if (config.useIfsym()) sSymbols+="|ifsym"; + if (config.usePifont()) sSymbols+="|dingbats"; + if (config.useEurosym()) sSymbols+="|eurosym"; + if (config.useTipa()) sSymbols+="|tipa"; + + tableSet = new Hashtable(); + UnicodeTableHandler handler=new UnicodeTableHandler(tableSet, sSymbols); + SAXParserFactory factory=SAXParserFactory.newInstance(); + InputStream is = this.getClass().getResourceAsStream("symbols.xml"); + try { + SAXParser saxParser=factory.newSAXParser(); + saxParser.parse(is,handler); + } + catch (Throwable t){ + System.err.println("Oops - Unable to read symbols.xml"); + t.printStackTrace(); + } + // put root table at top of stack + tableStack = new Stack(); + tableStack.push((UnicodeTable) tableSet.get("root")); + table = (UnicodeTable) tableSet.get("root"); + } + + /** Construct a new I18n for general use + * @param config the configuration which determines the symbols to use + */ + public ClassicI18n(LaTeXConfig config) { + this (null, config, null); + } + + /** Add declarations to the preamble to load the required packages + * @param pack usepackage declarations + * @param decl other declarations + */ + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + pack.append("\\usepackage[") + .append(writeInputenc(config.getInputencoding())) + .append("]{inputenc}").nl(); + + // usepackage fontenc + CSVList fontencs = new CSVList(','); + if (bT2A) { fontencs.addValue("T2A"); } + if (bGreek) { fontencs.addValue("LGR"); } + if (config.useTipa()) { fontencs.addValue("T3"); } + fontencs.addValue("T1"); + pack.append("\\usepackage[").append(fontencs.toString()) + .append("]{fontenc}").nl(); + + // usepackage babel + + // If the document contains "anonymous" greek letters we need greek in any case + // If the document contains "anonymous cyrillic letters we need one of the + // languages russian, ukrainian or bulgarian + if (greek() && !languages.contains("el")) languages.add("el"); + if (cyrillic() && !(languages.contains("ru") || languages.contains("uk") || languages.contains("bg"))) { + languages.add("ru"); + } + + // Load babel with the used languages + CSVList babelopt = new CSVList(","); + Iterator langiter = languages.iterator(); + while (langiter.hasNext()) { + String sLang = (String) langiter.next(); + if (!sLang.equals(sDefaultLanguage)) { + if ("el".equals(sLang) && this.polytonicGreek()) { + babelopt.addValue("polutonikogreek"); + } + else { + babelopt.addValue(getBabelLanguage(sLang)); + } + } + } + + // The default language must be the last one + if (sDefaultLanguage!=null) { + if ("el".equals(sDefaultLanguage) && this.polytonicGreek()) { + babelopt.addValue("polutonikogreek"); + } + else { + babelopt.addValue(getBabelLanguage(sDefaultLanguage)); + } + } + + if (!babelopt.isEmpty()) { + pack.append("\\usepackage[") + .append(babelopt.toString()) + .append("]{babel}").nl(); + } + + // usepackage tipa + if (config.useTipa()) { + pack.append("\\usepackage[noenc]{tipa}").nl() + .append("\\usepackage{tipx}").nl(); + } + + // usepackage bbding (Has to avoid some nameclashes.) + if (config.useBbding()) { + pack.append("\\usepackage{bbding}").nl() + .append("\\let\\bbCross\\Cross\\let\\Cross\\undefined").nl() + .append("\\let\\bbSquare\\Square\\let\\Square\\undefined").nl() + .append("\\let\\bbTrianbleUp\\TriangleUp\\let\\TriangleUp\\undefined").nl() + .append("\\let\\bbTrianlgeDown\\TriangleDown\\let\\TriangleDown\\undefined").nl(); + } + + // usepackage ifsym + if (config.useIfsym()) { + pack.append("\\usepackage[geometry,weather,misc,clock]{ifsym}").nl(); + } + + // usepackage pifont + if (config.usePifont()) { pack.append("\\usepackage{pifont}").nl(); } + + // usepackage eurosym + if (config.useEurosym()) { pack.append("\\usepackage{eurosym}").nl(); } + + // usepackage amsmath (always!) + pack.append("\\usepackage{amsmath}").nl(); + + // usepackage wasysym (*must* be loaded between amsmath and amsfonts!) + if (config.useWasysym()) { + pack.append("\\usepackage{wasysym}").nl(); + } + + // usepackage amssymb, amsfonts, textcomp (always!) + pack.append("\\usepackage{amssymb,amsfonts,textcomp}").nl(); + + } + + /** Apply a language language + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + public void applyLanguage(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba) { + if (!bAlwaysUseDefaultLang && style!=null) { + String sISOLang = style.getProperty(XMLString.FO_LANGUAGE,bInherit); + if (sISOLang!=null) { + languages.add(sISOLang); + String sLang = getBabelLanguage(sISOLang); + if (sLang!=null) { + if (bDecl) { + ba.add("\\selectlanguage{"+sLang+"}",""); + //ba.add("\\begin{otherlanguage}{"+sLang+"}","\\end{otherlanguage}"); + } + else { + ba.add("\\foreignlanguage{"+sLang+"}{","}"); + } + } + } + } + } + + /** Push a font to the font stack + * @param sName the name of the font + */ + public void pushSpecialTable(String sName) { + // If no name is specified we should keep the current table + // Otherwise try to find the table, and use root if it's not available + if (sName!=null) { + table = (UnicodeTable) tableSet.get(sName); + if (table==null) { table = (UnicodeTable) tableSet.get("root"); } + } + tableStack.push(table); + } + + /** Pop a font from the font stack + */ + public void popSpecialTable() { + tableStack.pop(); + table = (UnicodeTable) tableStack.peek(); + } + + /** Get the number of characters defined in the current table + * (for informational purposes only) + * @return the number of characters + */ + public int getCharCount() { return table.getCharCount(); } + + /** Convert a string of characters into LaTeX + * @param s the source string + * @param bMathMode true if the string should be rendered in math mode + * @param sLang the iso language of the string + * @return the LaTeX string + */ + public String convert(String s, boolean bMathMode, String sLang){ + if (!bAlwaysUseDefaultLang && sLang!=null) { languages.add(sLang); } + StringBuffer buf=new StringBuffer(); + int nFontenc = bAlwaysUseDefaultLang ? nDefaultFontenc : getFontenc(sLang); + int nLen = s.length(); + int i = 0; + int nStart = i; + while (i<nLen) { + ReplacementTrieNode node = stringReplace.get(s,i,nLen); + if (node!=null) { + if (i>nStart) { + convert(s,nStart,i,bMathMode,sLang,buf,nFontenc); + } + boolean bOtherFontenc = !supportsFontenc(node.getFontencs(),nFontenc); + if (bOtherFontenc) { + buf.append(getFontencCs(getFontenc(node.getFontencs()))).append("{"); + } + buf.append(node.getLaTeXCode()); + if (bOtherFontenc) { + buf.append("}"); + } + i += node.getInputLength(); + nStart = i; + } + else { + i++; + } + } + if (nStart<nLen) { + convert(s,nStart,nLen,bMathMode,sLang,buf,nFontenc); + } + return buf.toString(); + } + + private void convert(String s, int nStart, int nEnd, boolean bMathMode, String sLang, StringBuffer buf, int nFontenc) { + int nCurFontenc = nFontenc; + ucparser.reset(table,s,nStart,nEnd); + boolean bProtectDashes = true; + boolean bTempMathMode = false; + while (ucparser.next()) { + char c = ucparser.getChar(); + if (bMathMode) { + buf.append(convertMathChar(c,nFontenc)); + } + else if (greekMath(c,nFontenc) || (table.hasMathChar(c) && !table.hasTextChar(c))) { + if (!bTempMathMode) { // switch to math mode + buf.append("$"); + bTempMathMode = true; + } + buf.append(convertMathChar(c,nFontenc)); + bProtectDashes = false; + } + else if (table.hasTextChar(c)) { + if (bTempMathMode) { // switch to text mode + buf.append("$"); + bTempMathMode = false; + } + int nFontencs = table.getFontencs(c); + if (supportsFontenc(nFontencs,nCurFontenc)) { + // The text character is valid in the current font encoding + // Note: Change of font encoding is greedy - change? + + // Prevent unwanted --- ligatures + if (table.isDashes(c)) { + if (bProtectDashes) { buf.append("{}"); } + bProtectDashes = true; + } + else { + bProtectDashes = false; + } + + setFlags(c,nCurFontenc); + if (ucparser.hasCombiningChar()) { + char cc = ucparser.getCombiningChar(); + if (supportsFontenc(table.getFontencs(cc),nCurFontenc)) { + buf.append(table.getTextChar(cc)).append("{") + .append(table.getTextChar(c)).append("}"); + } + else { // ignore combining char if not valid in this font encoding + buf.append(table.getTextChar(c)); + } + } + else { + buf.append(table.getTextChar(c)); + } + } + else { + // The text character is valid in another font encoding + + bProtectDashes = table.isDashes(c); + + int nFontenc1 = getFontenc(nFontencs); + setFlags(c,nFontenc1); + if (nCurFontenc!=nFontenc) { // end "other font encoding" + buf.append("}"); + } + if (nFontenc1!=nFontenc) { // start "other font encoding" + buf.append(getFontencCs(nFontenc1)).append("{"); + } + + if (ucparser.hasCombiningChar()) { + char cc = ucparser.getCombiningChar(); + if (supportsFontenc(table.getFontencs(cc),nCurFontenc)) { + buf.append(table.getTextChar(cc)).append("{") + .append(table.getTextChar(c)).append("}"); + } + else { // ignore combining char if not valid in this font encoding + buf.append(table.getTextChar(c)); + } + } + else { + buf.append(table.getTextChar(c)); + } + nCurFontenc = nFontenc1; + } + } + else { + buf.append(notFound(c,nCurFontenc)); + } + } + + if (bTempMathMode) { // turn of math mode + buf.append("$"); + } + + if (nCurFontenc!=nFontenc) { // end unfinished "other font encoding" + buf.append("}"); + } + + } + + // convert a single math character + private String convertMathChar(char c, int nFontenc) { + if (table.hasMathChar(c)) { + return table.getMathChar(c); + } + else if (table.hasTextChar(c)) { // use text mode as a fallback + int nFontencs = table.getFontencs(c); + if (supportsFontenc(nFontencs,nFontenc)) { + // The text character is valid in the current font encoding + setFlags(c,nFontenc); + if (table.getCharType(c)==UnicodeCharacter.COMBINING) { + return "\\text{" + table.getTextChar(c) +"{}}"; + } + else { + return "\\text{" + table.getTextChar(c) +"}"; + } + } + else { + // The text character is valid in another font encoding + int nFontenc1 = getFontenc(nFontencs); + setFlags(c,nFontenc1); + if (table.getCharType(c)==UnicodeCharacter.COMBINING) { + return "\\text{" + getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"{}}}"; + } + else { + return "\\text{" + getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"}}"; + } + } + } + else { + return "\\text{" + notFound(c,nFontenc) + "}"; + } + } + + // Missing symbol + private String notFound(char c,int nFontenc) { + //String sErrorMsg = "[Warning: Missing symbol " + Integer.toHexString(c).toUpperCase() +"]"; + String sErrorMsg = "["+Integer.toHexString(c).toUpperCase() +"?]"; + if (nFontenc==T1_ENC) return sErrorMsg; + else return "\\textlatin{"+sErrorMsg+"}"; + } + + + // Convert a single character + /*private String convert(char c, boolean bMathMode, String sLang){ + int nFontenc = bAlwaysUseDefaultLang ? nDefaultFontenc : getFontenc(sLang); + if (bMathMode) { + return convertMathChar(c,nFontenc); + } + else if (greekMath(c,nFontenc) || (table.hasMathChar(c) && !table.hasTextChar(c))) { + return "$" + convertMathChar(c,nFontenc) + "$"; + } + else if (table.hasTextChar(c)) { + int nFontencs = table.getFontencs(c); + if (supportsFontenc(nFontencs,nFontenc)) { + // The text character is valid in the current font encoding + setFlags(c,nFontenc); + if (table.getCharType(c)==UnicodeCharacter.COMBINING) { + return table.getTextChar(c)+"{}"; + } + else { + return table.getTextChar(c); + } + } + else { + // The text character is valid in another font encoding + int nFontenc1 = getFontenc(nFontencs); + setFlags(c,nFontenc1); + if (table.getCharType(c)==UnicodeCharacter.COMBINING) { + return getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"{}}"; + } + else { + return getFontencCs(nFontenc1) + "{" + table.getTextChar(c) +"}"; + } + } + } + else { + return notFound(c,nFontenc); + } + }*/ + + + + // **** Languages **** + + // Convert iso language to babel language + // todo: include iso country + // todo: support automatic choice of inputenc (see comments)? + private String getBabelLanguage(String sLang) { + if (babelLanguages.containsKey(sLang)) { + return (String) babelLanguages.get(sLang); + } + else { + return "english"; // interpret unknown languages as English + } + } + + private void prepareBabelLanguages() { + babelLanguages = new Hashtable(); + babelLanguages.put("en", "english"); // latin1 + babelLanguages.put("bg", "bulgarian"); // cp1251? + babelLanguages.put("cs", "czech"); // latin2 + babelLanguages.put("da", "danish"); // latin1 + babelLanguages.put("de", "ngerman"); // latin1 + babelLanguages.put("el", "greek"); // iso-8859-7 + babelLanguages.put("es", "spanish"); // latin1 + babelLanguages.put("fi", "finnish"); // latin1 (latin9?) + babelLanguages.put("fr", "french"); // latin1 (latin9?) + babelLanguages.put("ga", "irish"); // latin1 + babelLanguages.put("hr", "croatian"); // latin2 + babelLanguages.put("hu", "magyar"); // latin2 + babelLanguages.put("la", "latin"); // ascii + babelLanguages.put("is", "icelandic"); // latin1 + babelLanguages.put("it", "italian"); // latin1 + babelLanguages.put("nl", "dutch"); // latin1 + babelLanguages.put("no", "norsk"); // latin1 + babelLanguages.put("pl", "polish"); // latin2 + babelLanguages.put("pt", "portuges"); // latin1 + babelLanguages.put("ro", "romanian"); // latin2 + babelLanguages.put("ru", "russian"); // cp1251? + babelLanguages.put("sk", "slovak"); // latin2 + babelLanguages.put("sl", "slovene"); // latin2 + babelLanguages.put("sr", "serbian"); // cp1251? + babelLanguages.put("sv", "swedish"); // latin1 + babelLanguages.put("tr", "turkish"); + babelLanguages.put("uk", "ukrainian"); // cp1251? + } + + // **** Helpers to collect various information **** + + // Did we use cyrillic? + private boolean cyrillic() { return bT2A; } + + // Did we use greek? + private boolean greek() { return bGreek; } + + // Did we use polytonic greek? + private boolean polytonicGreek() { return bPolytonicGreek; } + + // Outside greek text, greek letters may be rendered in math mode, + // if the user requires that in the configuration. + private boolean greekMath(char c, int nFontenc) { + return bGreekMath && nFontenc!=LGR_ENC && table.getFontencs(c)==LGR_ENC; + } + + // Set cyrillic and greek flags + private void setFlags(char c, int nFontenc) { + if ((c>='\u1F00') && (c<='\u1FFF')) bPolytonicGreek = true; + if (nFontenc==LGR_ENC) bGreek = true; + if (nFontenc==T2A_ENC) bT2A = true; + } + +} diff --git a/source/java/writer2latex/latex/i18n/I18n.java b/source/java/writer2latex/latex/i18n/I18n.java new file mode 100644 index 0000000..ac514ac --- /dev/null +++ b/source/java/writer2latex/latex/i18n/I18n.java @@ -0,0 +1,118 @@ +/************************************************************************ + * + * I18n.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-16) + * + */ + +package writer2latex.latex.i18n; + +import java.util.HashSet; + +import writer2latex.office.*; +import writer2latex.latex.LaTeXConfig; +import writer2latex.latex.LaTeXDocumentPortion; +import writer2latex.latex.ConverterPalette; +import writer2latex.latex.util.BeforeAfter; + +/** This abstract class takes care of i18n in the LaTeX export. + * Since i18n is handled quite differently in LaTeX "Classic" + * and XeTeX, we use two different classes + */ +public abstract class I18n { + // **** Global variables **** + + // Configuration items + protected LaTeXConfig config; + protected ReplacementTrie stringReplace; + protected boolean bGreekMath; // Use math mode for greek letters + protected boolean bAlwaysUseDefaultLang; // Ignore sLang parameter to convert() + + // Collected data + protected String sDefaultLanguage; // The default iso language to use + protected HashSet languages = new HashSet(); // All languages used + + // **** Constructors **** + + /** Construct a new I18n as ConverterHelper + * @param ofr the OfficeReader to get language information from + * @param config the configuration which determines the symbols to use + * @param palette the ConverterPalette (unused) + */ + public I18n(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + // We don't need the palette and the office reader is only used to + // identify the default language + + // Set up config items + this.config = config; + stringReplace = config.getStringReplace(); + bGreekMath = config.greekMath(); + bAlwaysUseDefaultLang = !config.multilingual(); + + // Default language + if (ofr!=null) { + if (config.multilingual()) { + // Read the default language from the default paragraph style + StyleWithProperties style = ofr.getDefaultParStyle(); + if (style!=null) { + sDefaultLanguage = style.getProperty(XMLString.FO_LANGUAGE); + } + } + else { + // the most common language is the only language + sDefaultLanguage = ofr.getMajorityLanguage(); + } + } + if (sDefaultLanguage==null) { sDefaultLanguage="en"; } + } + + /** Add declarations to the preamble to load the required packages + * @param pack usepackage declarations + * @param decl other declarations + */ + public abstract void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl); + + /** Apply a language language + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + public abstract void applyLanguage(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba); + + /** Push a font to the font stack + * @param sName the name of the font + */ + public abstract void pushSpecialTable(String sName); + + /** Pop a font from the font stack + */ + public abstract void popSpecialTable(); + + /** Convert a string of characters into LaTeX + * @param s the source string + * @param bMathMode true if the string should be rendered in math mode + * @param sLang the iso language of the string + * @return the LaTeX string + */ + public abstract String convert(String s, boolean bMathMode, String sLang); +} diff --git a/source/java/writer2latex/latex/i18n/Package.html b/source/java/writer2latex/latex/i18n/Package.html new file mode 100644 index 0000000..2995355 --- /dev/null +++ b/source/java/writer2latex/latex/i18n/Package.html @@ -0,0 +1,16 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.xhtml.i18n</title> +</head> + +<body> +<p>This package takes care of i18n for LaTeX.</p> +<p>In LaTeX, i18n is a mixture of inputencodings, fontencodings + and babel languages. In particualar, the package provides a Unicode->LaTeX + translation that can handle different inputencodings and fontencodings.</p> +<p>The pacakge could (with modification) in theory be used in other programs + that convert unicode to LaTeX.</p> +</body> +</html> diff --git a/source/java/writer2latex/latex/i18n/ReplacementTrie.java b/source/java/writer2latex/latex/i18n/ReplacementTrie.java new file mode 100644 index 0000000..b09b324 --- /dev/null +++ b/source/java/writer2latex/latex/i18n/ReplacementTrie.java @@ -0,0 +1,56 @@ +/************************************************************************ + * + * ReplacementTrie.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2006 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2006-11-02) + * + */ + +package writer2latex.latex.i18n; + +/** This class contains a trie of string -> LaTeX code replacements +*/ +public class ReplacementTrie extends ReplacementTrieNode { + + public ReplacementTrie() { + super('*',0); + } + + public ReplacementTrieNode get(String sInput) { + return get(sInput,0,sInput.length()); + } + + public ReplacementTrieNode get(String sInput, int nStart, int nEnd) { + if (sInput.length()==0) { return null; } + else { return super.get(sInput,nStart,nEnd); } + } + + public void put(String sInput, String sLaTeXCode, int nFontencs) { + if (sInput.length()==0) { return; } + else { super.put(sInput,sLaTeXCode,nFontencs); } + } + + public String[] getInputStrings() { + return null; //TODO + } + + +} diff --git a/source/java/writer2latex/latex/i18n/ReplacementTrieNode.java b/source/java/writer2latex/latex/i18n/ReplacementTrieNode.java new file mode 100644 index 0000000..1e6a05f --- /dev/null +++ b/source/java/writer2latex/latex/i18n/ReplacementTrieNode.java @@ -0,0 +1,127 @@ +/************************************************************************ + * + * ReplacementTrieNode.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2006 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2006-11-02) + * + */ + +package writer2latex.latex.i18n; + +/** This class contains a node in a trie of string -> LaTeX code replacements +*/ +public class ReplacementTrieNode { + + private char cLetter; + private int nInputLength; + private String sLaTeXCode = null; + private int nFontencs = 0; + private ReplacementTrieNode son = null; + private ReplacementTrieNode brother = null; + + public ReplacementTrieNode(char cLetter, int nInputLength) { + this.cLetter = cLetter; + this.nInputLength = nInputLength; + } + + public char getLetter() { return this.cLetter; } + + public int getInputLength() { return this.nInputLength; } + + public String getLaTeXCode() { return this.sLaTeXCode; } + + public int getFontencs() { return this.nFontencs; } + + protected void setLaTeXCode(String sLaTeXCode) { + this.sLaTeXCode = sLaTeXCode; + } + + protected void setFontencs(int nFontencs) { + this.nFontencs = nFontencs; + } + + protected ReplacementTrieNode getFirstChild() { + return this.son; + } + + protected ReplacementTrieNode getNextSibling() { + return this.brother; + } + + protected ReplacementTrieNode getChildByLetter(char cLetter) { + ReplacementTrieNode child = this.getFirstChild(); + while (child!=null) { + if (cLetter==child.getLetter()) { return child; } + child = child.getNextSibling(); + } + return null; + } + + protected void appendChild(ReplacementTrieNode node) { + if (son==null) { son = node; } + else { son.appendSibling(node); } + } + + protected void appendSibling(ReplacementTrieNode node) { + if (brother==null) { brother = node; } + else { brother.appendSibling(node); } + } + + protected ReplacementTrieNode get(String sInput, int nStart, int nEnd) { + if (nStart>=nEnd) { return null; } + char c = sInput.charAt(nStart); + ReplacementTrieNode child = this.getFirstChild(); + while (child!=null) { + if (child.getLetter()==c) { + if (child.getLaTeXCode()!=null) { return child; } + else { return child.get(sInput,nStart+1,nEnd); } + } + child = child.getNextSibling(); + } + return null; + } + + protected void put(String sInput, String sLaTeXCode, int nFontencs) { + char c = sInput.charAt(0); + ReplacementTrieNode child = this.getChildByLetter(c); + if (child==null) { + child = new ReplacementTrieNode(c,this.getInputLength()+1); + this.appendChild(child); + } + if (sInput.length()>1) { + child.put(sInput.substring(1),sLaTeXCode,nFontencs); + } + else { + child.setLaTeXCode(sLaTeXCode); + child.setFontencs(nFontencs); + } + } + + public String toString() { + String s = Character.toString(cLetter); + if (brother!=null) { s+=brother.toString(); } + if (son!=null) { s+="\nInputLength "+(nInputLength+1)+", "+son.toString(); } + else { s+="\n"; } + return s; + } + + +} diff --git a/source/java/writer2latex/latex/i18n/UnicodeCharacter.java b/source/java/writer2latex/latex/i18n/UnicodeCharacter.java new file mode 100644 index 0000000..7e8217e --- /dev/null +++ b/source/java/writer2latex/latex/i18n/UnicodeCharacter.java @@ -0,0 +1,52 @@ +/************************************************************************ + * + * UnicodeCharacter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-07-24) + * + */ + +package writer2latex.latex.i18n; + +// Helper class: A struct to hold the LaTeX representations of a unicode character +class UnicodeCharacter implements Cloneable { + final static int NORMAL = 0; // this is a normal character + final static int COMBINING = 1; // this character should be ignored + final static int IGNORE = 2; // this is a combining character + final static int UNKNOWN = 3; // this character is unknown + + int nType; // The type of character + String sMath; // LaTeX representation in math mode + String sText; // LaTeX representation in text mode + int nFontencs; // Valid font encoding(s) for the text mode representation + boolean bDashes; // This character is represented by dashes (-,--,---) + + protected Object clone() { + UnicodeCharacter uc = new UnicodeCharacter(); + uc.nType = this.nType; + uc.sMath = this.sMath; + uc.sText = this.sText; + uc.nFontencs = this.nFontencs; + uc.bDashes = this.bDashes; + return uc; + } +} + diff --git a/source/java/writer2latex/latex/i18n/UnicodeRow.java b/source/java/writer2latex/latex/i18n/UnicodeRow.java new file mode 100644 index 0000000..d051c2f --- /dev/null +++ b/source/java/writer2latex/latex/i18n/UnicodeRow.java @@ -0,0 +1,43 @@ +/************************************************************************ + * + * UnicodeRow.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-07-24) + * + */ + +package writer2latex.latex.i18n; + +// Helper class: A row of 256 unicode characters +class UnicodeRow implements Cloneable { + UnicodeCharacter[] entries; + UnicodeRow(){ entries=new UnicodeCharacter[256]; } + + protected Object clone() { + UnicodeRow ur = new UnicodeRow(); + for (int i=0; i<256; i++) { + if (this.entries[i]!=null) { + ur.entries[i] = (UnicodeCharacter) this.entries[i].clone(); + } + } + return ur; + } +} diff --git a/source/java/writer2latex/latex/i18n/UnicodeStringParser.java b/source/java/writer2latex/latex/i18n/UnicodeStringParser.java new file mode 100644 index 0000000..1a7b037 --- /dev/null +++ b/source/java/writer2latex/latex/i18n/UnicodeStringParser.java @@ -0,0 +1,79 @@ +/************************************************************************ + * + * UnicodeStringParser.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-07-24) + * + */ + +package writer2latex.latex.i18n; + +// Helper class: Parse a unicode string. +// Note: Some 8-bit fonts have additional "spacer" characters that are used +// for manual placement of accents. These are ignored between the base character +// and the combining character, thus we are parsing according to the rule +// <base char> <spacer char>* <combining char>? +class UnicodeStringParser { + private UnicodeTable table; // the table to use + private String s; // the string + private int i; // the current index + private int nEnd; // the maximal index + private char c; // the current character + private char cc; // the current combining character + + protected void reset(UnicodeTable table, String s, int i, int nEnd) { + this.table=table; + this.s=s; + this.i=i; + this.nEnd=nEnd; + } + + protected boolean next() { + if (i>=nEnd) { return false; } + // Pick up base character + c = s.charAt(i++); + if (table.getCharType(c)==UnicodeCharacter.COMBINING) { + // Lonely combining character - combine with space + cc = c; + c = ' '; + return true; + } + + // Skip characters that should be ignored + while (i<s.length() && table.getCharType(s.charAt(i))==UnicodeCharacter.IGNORE) { i++; } + // Pick up combining character, if any + if (i<s.length() && table.getCharType(s.charAt(i))==UnicodeCharacter.COMBINING) { + cc = s.charAt(i++); + } + else { + cc = '\u0000'; + } + + return true; + } + + protected char getChar() { return c; } + + protected boolean hasCombiningChar() { return cc!='\u0000'; } + + protected char getCombiningChar() { return cc; } + +} diff --git a/source/java/writer2latex/latex/i18n/UnicodeTable.java b/source/java/writer2latex/latex/i18n/UnicodeTable.java new file mode 100644 index 0000000..d7d3893 --- /dev/null +++ b/source/java/writer2latex/latex/i18n/UnicodeTable.java @@ -0,0 +1,169 @@ +/************************************************************************ + * + * UnicodeTable.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-07-24) + * + */ + +package writer2latex.latex.i18n; + +// Helper class: Table of up to 65536 unicode characters +class UnicodeTable { + protected UnicodeRow[] table=new UnicodeRow[256]; + private UnicodeTable parent; + + // Constructor; creates a new table, possibly based on a parent + // Note: The parent must be fully loaded before the child is created. + public UnicodeTable(UnicodeTable parent){ + this.parent = parent; + if (parent!=null) { + // *Copy* the rows from the parent + for (int i=0; i<256; i++) { + table[i] = parent.table[i]; + } + } + } + + // Make sure the required entry exists + private void createEntry(int nRow, int nCol) { + if (table[nRow]==null) { + table[nRow]=new UnicodeRow(); + } + else if (parent!=null && table[nRow]==parent.table[nRow]) { + // Before changing a row it must be *cloned* + table[nRow] = (UnicodeRow) parent.table[nRow].clone(); + } + if (table[nRow].entries[nCol]==null) { + table[nRow].entries[nCol]=new UnicodeCharacter(); + } + } + + // Addd a single character (type only), by number + protected void addCharType(char c, int nType) { + int nRow=c/256; int nCol=c%256; + createEntry(nRow,nCol); + table[nRow].entries[nCol].nType = nType; + } + + // Addd a single character (type only), by name + protected void addCharType(char c, String sType) { + int nRow=c/256; int nCol=c%256; + createEntry(nRow,nCol); + if ("combining".equals(sType)) { + table[nRow].entries[nCol].nType = UnicodeCharacter.COMBINING; + } + else if ("ignore".equals(sType)) { + table[nRow].entries[nCol].nType = UnicodeCharacter.IGNORE; + } + else { + table[nRow].entries[nCol].nType = UnicodeCharacter.NORMAL; + } + } + + // Add a single math character to the table + protected void addMathChar(char c, String sLaTeX){ + int nRow=c/256; int nCol=c%256; + createEntry(nRow,nCol); + table[nRow].entries[nCol].sMath=sLaTeX; + } + + // Add a single text character to the table + protected void addTextChar(char c, String sLaTeX, int nFontencs, boolean bDashes){ + int nRow=c/256; int nCol=c%256; + createEntry(nRow,nCol); + table[nRow].entries[nCol].sText=sLaTeX; + table[nRow].entries[nCol].nFontencs=nFontencs; + table[nRow].entries[nCol].bDashes=bDashes; + } + + // Retrieve entry for a character (or null) + private UnicodeCharacter getEntry(char c) { + int nRow=c/256; int nCol=c%256; + if (table[nRow]==null) return null; + return table[nRow].entries[nCol]; + } + + // Get character type + public int getCharType(char c) { + UnicodeCharacter entry = getEntry(c); + if (entry==null) return UnicodeCharacter.UNKNOWN; + return entry.nType; + } + + // Check to see if this math character exists? + public boolean hasMathChar(char c) { + UnicodeCharacter entry = getEntry(c); + if (entry==null) return false; + return entry.sMath!=null; + } + + // Get math character (or null) + public String getMathChar(char c) { + UnicodeCharacter entry = getEntry(c); + if (entry==null) return null; + return entry.sMath; + } + + // Check to see if this text character exists? + public boolean hasTextChar(char c) { + UnicodeCharacter entry = getEntry(c); + if (entry==null) return false; + return entry.sText!=null; + } + + // Get text character (or null) + public String getTextChar(char c) { + UnicodeCharacter entry = getEntry(c); + if (entry==null) return null; + return entry.sText; + } + + // Get font encoding(s) for text character (or 0) + public int getFontencs(char c) { + UnicodeCharacter entry = getEntry(c); + if (entry==null) return 0; + return entry.nFontencs; + } + + // Get dashes for text character + public boolean isDashes(char c) { + UnicodeCharacter entry = getEntry(c); + if (entry==null) return false; + return entry.bDashes; + } + + // Get number of defined characters + public int getCharCount() { + int nCount = 0; + for (int nRow=0; nRow<256; nRow++) { + if (table[nRow]!=null) { + for (int nCol=0; nCol<256; nCol++) { + UnicodeCharacter entry = table[nRow].entries[nCol]; + if (entry!=null) nCount++; + } + } + } + return nCount; + } + +} + diff --git a/source/java/writer2latex/latex/i18n/UnicodeTableHandler.java b/source/java/writer2latex/latex/i18n/UnicodeTableHandler.java new file mode 100644 index 0000000..6a4a940 --- /dev/null +++ b/source/java/writer2latex/latex/i18n/UnicodeTableHandler.java @@ -0,0 +1,166 @@ +/************************************************************************ + * + * UnicodeTableHandler.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-17) + * + */ + +package writer2latex.latex.i18n; + +import java.util.Hashtable; + +import org.xml.sax.Attributes; +import org.xml.sax.helpers.DefaultHandler; + +// Helper classs: SAX handler to parse symbols.xml from jar +class UnicodeTableHandler extends DefaultHandler{ + private Hashtable tableSet; // collection of all tables + private UnicodeTable table; // the current table + private String sSymbolSets; + private boolean bGlobalReadThisSet; + private boolean bReadThisSet; + private int nGlobalFontencs = 0; // The global fontencodings for current symbol set + private int nFontencs = 0; // The currently active fontencodings + private boolean b8bit = false; + + UnicodeTableHandler(Hashtable tableSet, String sSymbolSets){ + this.sSymbolSets = sSymbolSets; + this.tableSet = tableSet; + } + + public void startElement(String nameSpace, String localName, String qName, Attributes attributes){ + if (qName.equals("symbols")) { + //root element - create root table! + table = new UnicodeTable(null); + tableSet.put("root",table); + } + else if (qName.equals("symbol-set")) { + // start a new symbol set; maybe we want to include it? + bGlobalReadThisSet = sSymbolSets.indexOf(attributes.getValue("name")) >= 0; + bReadThisSet = bGlobalReadThisSet; + // Change global and current fontencodings + nGlobalFontencs = ClassicI18n.readFontencs(attributes.getValue("fontenc")); + nFontencs = nGlobalFontencs; + } + else if (qName.equals("special-symbol-set")) { + // start a new special symbol set; this requires a new table + table = new UnicodeTable((UnicodeTable) tableSet.get("root")); + tableSet.put(attributes.getValue("name"),table); + + // Read it if it requires nothing, or something we read + bGlobalReadThisSet = attributes.getValue("requires")==null || + sSymbolSets.indexOf(attributes.getValue("requires")) >= 0; + bReadThisSet = bGlobalReadThisSet; + b8bit = "true".equals(attributes.getValue("eight-bit")); + // Change global and current fontencodings + nGlobalFontencs = ClassicI18n.readFontencs(attributes.getValue("fontenc")); + nFontencs = nGlobalFontencs; + } + else if (qName.equals("symbol-subset")) { + // Do we requires something here? + if (attributes.getValue("requires")!=null) { + bReadThisSet = sSymbolSets.indexOf(attributes.getValue("requires")) >= 0; + } + // Change current fontencodings + nFontencs = ClassicI18n.readFontencs(attributes.getValue("fontenc")); + } + else if (qName.equals("symbol")) { + if (bReadThisSet) { + char c=(char)Integer.parseInt(attributes.getValue("char"),16); + String sEqChar=attributes.getValue("eq-char"); + if (sEqChar!=null) { // copy existing definitions, if any + char eqc = (char)Integer.parseInt(sEqChar,16); + if (table.getCharType(eqc)!=UnicodeCharacter.UNKNOWN) { + table.addCharType(c,table.getCharType(eqc)); + } + if (table.hasMathChar(eqc)) { + table.addMathChar(c,table.getMathChar(eqc)); + } + if (table.hasTextChar(eqc)) { + table.addTextChar(c,table.getTextChar(eqc),table.getFontencs(eqc),table.isDashes(eqc)); + } + } + else { + String sType=attributes.getValue("char-type"); + String sMath=attributes.getValue("math"); + String sText=attributes.getValue("text"); + boolean bDashes="true".equals(attributes.getValue("dashes")); + if (sType!=null) table.addCharType(c,sType); + if (sMath!=null) table.addMathChar(c,sMath); + if (sText!=null) table.addTextChar(c,sText,nFontencs,bDashes); + } + } + } + else if (qName.equals("preserve-symbol")) { + if (bReadThisSet) { + String sMode=attributes.getValue("mode"); + char c=(char)Integer.parseInt(attributes.getValue("char"),16); + table.addCharType(c,attributes.getValue("char-type")); + if ("math".equals(sMode) || "both".equals(sMode)) { + table.addMathChar(c,Character.toString(c)); + } + if ("text".equals(sMode) || "both".equals(sMode)) { + table.addTextChar(c,Character.toString(c),nFontencs,false); + } + } + } + else if (qName.equals("preserve-symbols")) { + if (bReadThisSet) { + String sMode=attributes.getValue("mode"); + String sType=attributes.getValue("char-type"); + char c1=(char)Integer.parseInt(attributes.getValue("first-char"),16); + char c2=(char)Integer.parseInt(attributes.getValue("last-char"),16); + boolean bMath = "math".equals(sMode) || "both".equals(sMode); + boolean bText = "text".equals(sMode) || "both".equals(sMode); + for (char c=c1; c<=c2; c++) { + table.addCharType(c,sType); + if (bMath) { + table.addMathChar(c,Character.toString(c)); + } + if (bText) { + table.addTextChar(c,Character.toString(c),nFontencs,false); + } + } + } + } + } + + public void endElement(String nameSpace, String localName, String qName){ + if (qName.equals("symbol-subset")) { + // Revert to global setting of reading status + bReadThisSet = bGlobalReadThisSet; + // Revert to global fontencoding + nFontencs = nGlobalFontencs; + } + else if (qName.equals("special-symbol-set")) { + if (b8bit) { + // Row 0 = Row 240 (F0) + // Note: 8-bit fonts are supposed to be relocated to F000..F0FF + // This may fail on import from msword, hence this hack + table.table[0] = table.table[240]; + } + b8bit = false; + } + } + +} + diff --git a/source/java/writer2latex/latex/i18n/XeTeXI18n.java b/source/java/writer2latex/latex/i18n/XeTeXI18n.java new file mode 100644 index 0000000..6e9679a --- /dev/null +++ b/source/java/writer2latex/latex/i18n/XeTeXI18n.java @@ -0,0 +1,120 @@ +/************************************************************************ + * + * XeTeXI18n.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-17) + * + */ + +package writer2latex.latex.i18n; + +import writer2latex.office.*; +import writer2latex.latex.LaTeXConfig; +import writer2latex.latex.LaTeXDocumentPortion; +import writer2latex.latex.ConverterPalette; +import writer2latex.latex.util.BeforeAfter; + +/** This class takes care of i18n in XeLaTeX + */ +public class XeTeXI18n extends I18n { + + // **** Constructors **** + + /** Construct a new XeTeXI18n as ConverterHelper + * @param ofr the OfficeReader to get language information from + * @param config the configuration which determines the symbols to use + * @param palette the ConverterPalette (unused) + */ + public XeTeXI18n(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + /** Add declarations to the preamble to load the required packages + * @param pack usepackage declarations + * @param decl other declarations + */ + public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) { + pack.append("\\usepackage{fontspec}").nl() + .append("\\usepackage{xunicode}").nl() + .append("\\usepackage{xltxtra}").nl() + .append("\\usepackage{amsmath,amssymb,amsfonts}").nl(); + } + + /** Apply a language language + * @param style the OOo style to read attributesfrom + * @param bDecl true if declaration form is required + * @param bInherit true if inherited properties should be used + * @param ba the <code>BeforeAfter</code> to add LaTeX code to. + */ + public void applyLanguage(StyleWithProperties style, boolean bDecl, boolean bInherit, BeforeAfter ba) { + // TODO (polyglossia) + } + + /** Push a font to the font stack + * @param sName the name of the font + */ + public void pushSpecialTable(String sName) { + // TODO + } + + /** Pop a font from the font stack + */ + public void popSpecialTable() { + // TODO + } + + /** Convert a string of characters into LaTeX + * @param s the source string + * @param bMathMode true if the string should be rendered in math mode + * @param sLang the iso language of the string + * @return the LaTeX string + */ + public String convert(String s, boolean bMathMode, String sLang){ + StringBuffer buf = new StringBuffer(); + int nLen = s.length(); + char c; + for (int i=0; i<nLen; i++) { + c = s.charAt(i); + switch (c) { + case '"' : buf.append("\\textquotedbl{}"); break; + case '#' : buf.append("\\#"); break; + case '$' : buf.append("\\$"); break; + case '%' : buf.append("\\%"); break; + case '&' : buf.append("\\&"); break; + case '\'' : buf.append("\\textbackslash{}"); break; + case '<' : buf.append("\\textless{}"); break; + case '>' : buf.append("\\textgreater{}"); break; + case '\\' : buf.append("\\textbackslash{}"); break; + case '\u005e' : buf.append("\\^{}"); break; + case '_' : buf.append("\\_"); break; + case '\u0060' : buf.append("\\textasciigrave{}"); break; + case '{' : buf.append("\\{"); break; + case '|' : buf.append("\\textbar{}"); break; + case '}' : buf.append("\\}"); break; + case '~' : buf.append("\\~{}"); break; + default: buf.append(c); + } + } + return buf.toString(); + } + + +} diff --git a/source/java/writer2latex/latex/i18n/symbols.xml b/source/java/writer2latex/latex/i18n/symbols.xml new file mode 100644 index 0000000..2ddbf96 --- /dev/null +++ b/source/java/writer2latex/latex/i18n/symbols.xml @@ -0,0 +1,3824 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- This is a datafile used by Writer2LaTeX + Version 1.0 (2008-12-03) + + The definitions for greek characters are contributed by interzone, info@interzone.gr + and extended by Johannis Likos. Additional bugfixes by Alexej Kryukov + The definitions 8-bit IPA fonts are contributed by Gavin McCullagh + + Some definitions, eg. cyrillic and tipa are taken from Dominique Unruhs ucs.sty + Some definitions are taken from "The comprehensive LaTeX symbol list", + maintained by Scott Pakin + The mappings from Wingdings to unicode are taken from Alan Wood + (http://www.alanwood.net/demos/wingdings.html) +--> +<symbols> + +<!-- +**************************************** +PART I: Common symbols, ascii only +**************************************** +--> + +<symbol-set name="ascii" fontenc="any"> +<!-- control (temporary) --> +<symbol char="A" text="" math=""/> + +<!-- Unicode block: Basic Latin --> +<symbol char="0020" text=" " math=" "/> +<symbol char="0021" text="!" math="!"/> +<symbol char="0022" text="{\textquotedbl}"/> +<symbol char="0023" text="\#" math="\#"/> +<symbol char="0024" text="\$" math="\$"/> +<symbol char="0025" text="\%" math="\%"/> +<symbol char="0026" text="\&" math="\&"/> +<symbol char="0027" text="{\textquotesingle}" math="'"/> +<symbol char="0028" text="(" math="("/> +<symbol char="0029" text=")" math=")"/> +<symbol char="002A" text="*" math="*"/> +<symbol char="002B" text="+" math="+"/> +<symbol char="002C" text="," math=","/> +<symbol char="002D" text="-" math="-" dashes="true" /> +<symbol char="002E" text="." math="."/> +<symbol char="002F" text="/" math="/"/> +<symbol char="0030" text="0" math="0"/> +<symbol char="0031" text="1" math="1"/> +<symbol char="0032" text="2" math="2"/> +<symbol char="0033" text="3" math="3"/> +<symbol char="0034" text="4" math="4"/> +<symbol char="0035" text="5" math="5"/> +<symbol char="0036" text="6" math="6"/> +<symbol char="0037" text="7" math="7"/> +<symbol char="0038" text="8" math="8"/> +<symbol char="0039" text="9" math="9"/> +<symbol char="003A" text=":" math=":"/> +<symbol char="003B" text=";" math=";"/> +<symbol char="003C" text="{\textless}" math="<"/> +<symbol char="003D" text="=" math="="/> +<symbol char="003E" text="{\textgreater}" math=">"/> +<symbol char="003F" text="?" math="?"/> +<symbol char="0040" text="@" math="@"/> +<symbol-subset fontenc="T1 T2A"> +<symbol char="0041" text="A" math="A"/> +<symbol char="0042" text="B" math="B"/> +<symbol char="0043" text="C" math="C"/> +<symbol char="0044" text="D" math="D"/> +<symbol char="0045" text="E" math="E"/> +<symbol char="0046" text="F" math="F"/> +<symbol char="0047" text="G" math="G"/> +<symbol char="0048" text="H" math="H"/> +<symbol char="0049" text="I" math="I"/> +<symbol char="004A" text="J" math="J"/> +<symbol char="004B" text="K" math="K"/> +<symbol char="004C" text="L" math="L"/> +<symbol char="004D" text="M" math="M"/> +<symbol char="004E" text="N" math="N"/> +<symbol char="004F" text="O" math="O"/> +<symbol char="0050" text="P" math="P"/> +<symbol char="0051" text="Q" math="Q"/> +<symbol char="0052" text="R" math="R"/> +<symbol char="0053" text="S" math="S"/> +<symbol char="0054" text="T" math="T"/> +<symbol char="0055" text="U" math="U"/> +<symbol char="0056" text="V" math="V"/> +<symbol char="0057" text="W" math="W"/> +<symbol char="0058" text="X" math="X"/> +<symbol char="0059" text="Y" math="Y"/> +<symbol char="005A" text="Z" math="Z"/> +</symbol-subset> +<symbol char="005B" text="[" math="["/> +<symbol char="005C" text="{\textbackslash}" math="\backslash "/> +<symbol char="005D" text="]" math="]"/> +<symbol char="005E" text="\^{}" math="\hat{}"/> +<symbol char="005F" text="\_" math="\_"/> +<symbol char="0060" text="{\textasciigrave}" math="\grave{}"/> +<symbol-subset fontenc="T1 T2A"> +<symbol char="0061" text="a" math="a"/> +<symbol char="0062" text="b" math="b"/> +<symbol char="0063" text="c" math="c"/> +<symbol char="0064" text="d" math="d"/> +<symbol char="0065" text="e" math="e"/> +<symbol char="0066" text="f" math="f"/> +<symbol char="0067" text="g" math="g"/> +<symbol char="0068" text="h" math="h"/> +<symbol char="0069" text="i" math="i"/> +<symbol char="006A" text="j" math="j"/> +<symbol char="006B" text="k" math="k"/> +<symbol char="006C" text="l" math="l"/> +<symbol char="006D" text="m" math="m"/> +<symbol char="006E" text="n" math="n"/> +<symbol char="006F" text="o" math="o"/> +<symbol char="0070" text="p" math="p"/> +<symbol char="0071" text="q" math="q"/> +<symbol char="0072" text="r" math="r"/> +<symbol char="0073" text="s" math="s"/> +<symbol char="0074" text="t" math="t"/> +<symbol char="0075" text="u" math="u"/> +<symbol char="0076" text="v" math="v"/> +<symbol char="0077" text="w" math="w"/> +<symbol char="0078" text="x" math="x"/> +<symbol char="0079" text="y" math="y"/> +<symbol char="007A" text="z" math="z"/> +</symbol-subset> +<symbol char="007B" text="\{" math="\{"/> +<symbol char="007C" text="{\textbar}" math="|"/> +<symbol char="007D" text="\}" math="\}"/> +<symbol char="007E" text="\~{}" math="\tilde{}"/> +<symbol char="007F" text="" math=""/> + +<!-- Unicode Block: Latin-1 Supplement --> +<symbol char="00A0" text="~"/> +<symbol-subset fontenc="T1"> +<symbol char="00A1" text="!`" /> +</symbol-subset> +<symbol char="00A2" text="{\textcent}"/> +<symbol char="00A3" text="{\pounds}" math="\pounds "/> +<symbol char="00A4" text="{\textcurrency}"/> +<symbol char="00A5" text="{\textyen}"/> +<symbol char="00A6" text="{\textbrokenbar}"/> +<symbol char="00A7" text="{\S}" math="{\S}"/> +<symbol char="00A8" text="{\textasciidieresis}" math="\ddot{}"/> +<symbol char="00A9" text="{\textcopyright}"/> +<symbol char="00AA" text="{\textordfeminine}"/> +<symbol char="00AB" text="{\guillemotleft}"/> +<symbol char="00AC" text="{\textlnot}" math="\lnot "/> +<symbol char="00AD" text="\-"/> +<symbol char="00AE" text="{\textregistered}"/> +<symbol char="00AF" text="{\textasciimacron}" math="\bar{}"/> +<symbol char="00B0" text="{\textdegree}" math="{}^{\circ}"/> +<symbol char="00B1" text="{\textpm}" math="\pm "/> +<symbol char="00B2" text="{\texttwosuperior}" math="^2"/> +<symbol char="00B3" text="{\textthreesuperior}" math="^3"/> +<symbol char="00B4" text="{\textasciiacute}" math="\acute{}"/> +<symbol char="00B5" text="{\textmu}" math="\mu "/> +<symbol char="00B6" text="{\P}" math="\P "/> +<symbol char="00B7" text="{\textperiodcentered}" math="\cdot "/> +<symbol char="00B8" text="\c{}"/> +<symbol char="00B9" text="{\textonesuperior}" math="^1"/> +<symbol char="00BA" text="{\textordmasculine}"/> +<symbol char="00BB" text="{\guillemotright}"/> +<symbol char="00BC" text="{\textonequarter}" math="\frac14"/> +<symbol char="00BD" text="{\textonehalf}" math="\frac12"/> +<symbol char="00BE" text="{\textthreequarters}" math="\frac34"/> +<symbol-subset fontenc="T1"> +<symbol char="00BF" text="?`"/> +<symbol char="00C0" text="\`A" /> +<symbol char="00C1" text="\'A" /> +<symbol char="00C2" text="\^A" /> +<symbol char="00C3" text="\~A" /> +<symbol char="00C4" text='\"A' /> +<symbol char="00C5" text="{\AA}" /> +<symbol char="00C6" text="{\AE}" /> +<symbol char="00C7" text="\c{C}" /> +<symbol char="00C8" text="\`E" /> +<symbol char="00C9" text="\'E" /> +<symbol char="00CA" text="\^E" /> +<symbol char="00CB" text='\"E' /> +<symbol char="00CC" text="\`I" /> +<symbol char="00CD" text="\'I" /> +<symbol char="00CE" text="\^I" /> +<symbol char="00CF" text='\"I' /> +<symbol char="00D0" text="{\DH}" /> +<symbol char="00D1" text="\~N" /> +<symbol char="00D2" text="\`O" /> +<symbol char="00D3" text="\'O" /> +<symbol char="00D4" text="\^O" /> +<symbol char="00D5" text="\~O" /> +<symbol char="00D6" text='\"O' /> +</symbol-subset> +<symbol char="00D7" text="{\texttimes}" math="\times " /> +<symbol-subset fontenc="T1"> +<symbol char="00D8" text="{\O}" /> +<symbol char="00D9" text="\`U" /> +<symbol char="00DA" text="\'U" /> +<symbol char="00DB" text="\^U" /> +<symbol char="00DC" text='\"U' /> +<symbol char="00DD" text="\'Y" /> +<symbol char="00DE" text="{\TH}" /> +<symbol char="00DF" text="{\ss}" /> +<symbol char="00E0" text="\`a" /> +<symbol char="00E1" text="\'a" /> +<symbol char="00E2" text="\^a" /> +<symbol char="00E3" text="\~a" /> +<symbol char="00E4" text='\"a' /> +<symbol char="00E5" text="{\aa}" /> +<symbol char="00E6" text="{\ae}" /> +<symbol char="00E7" text="\c{c}" /> +<symbol char="00E8" text="\`e" /> +<symbol char="00E9" text="\'e" /> +<symbol char="00EA" text="\^e" /> +<symbol char="00EB" text='\"e' /> +<symbol char="00EC" text="\`i" /> +<symbol char="00ED" text="\'i" /> +<symbol char="00EE" text="\^i" /> +<symbol char="00EF" text='\"i' /> +<symbol char="00F0" text="{\dh}" /> +<symbol char="00F1" text="\~n" /> +<symbol char="00F2" text="\`o" /> +<symbol char="00F3" text="\'o" /> +<symbol char="00F4" text="\^o" /> +<symbol char="00F5" text="\~o" /> +<symbol char="00F6" text='\"o' /> +</symbol-subset> +<symbol char="00F7" text="{\textdiv}" math="\div " /> +<symbol-subset fontenc="T1"> +<symbol char="00F8" text="{\o}" /> +<symbol char="00F9" text="\`u" /> +<symbol char="00FA" text="\'u" /> +<symbol char="00FB" text="\^u" /> +<symbol char="00FC" text='\"u' /> +<symbol char="00FD" text="\'y" /> +<symbol char="00FE" text="{\th}" /> +<symbol char="00FF" text='\"y' /> + +<!-- Unicode block: Latin Extended-A --> +<symbol char="0100" text="\=A" /> +<symbol char="0101" text="\=a" /> +<symbol char="0102" text="\u{A}" /> +<symbol char="0103" text="\u{a}" /> +<symbol char="0104" text="\k{A}" /> +<symbol char="0105" text="\k{a}" /> +<symbol char="0106" text="\'C" /> +<symbol char="0107" text="\'c" /> +<symbol char="0108" text="\^C" /> +<symbol char="0109" text="\^c" /> +<symbol char="010A" text="\.C" /> +<symbol char="010B" text="\.c" /> +<symbol char="010C" text="\v{C}" /> +<symbol char="010D" text="\v{c}" /> +<symbol char="010E" text="\v{D}" /> +<symbol char="010F" text="\v{d}" /> +<symbol char="0110" text="{\DJ}" /> +<symbol char="0111" text="{\dj}" /> +<symbol char="0112" text="\=E" /> +<symbol char="0113" text="\=e" /> +<symbol char="0114" text="\u{E}" /> +<symbol char="0115" text="\u{e}" /> +<symbol char="0116" text="\.E" /> +<symbol char="0117" text="\.e" /> +<symbol char="0118" text="\k{E}" /> +<symbol char="0119" text="\k{e}" /> +<symbol char="011A" text="\v{E}" /> +<symbol char="011B" text="\v{e}" /> +<symbol char="011C" text="\^G" /> +<symbol char="011D" text="\^g" /> +<symbol char="011E" text="\u{G}" /> +<symbol char="011F" text="\u{g}" /> +<symbol char="0120" text="\.G" /> +<symbol char="0121" text="\.g" /> +<symbol char="0122" text="\c{G}" /> +<!-- missing: LATIN SMALL LETTER G WITH CEDILLA (latvian) + would \c{g} be ok, or should the "dirty" version from ucs.sty be preferred?? --> +<symbol char="0124" text="\^H" /> +<symbol char="0125" text="\^h" /> +<!-- missing: 126 LATIN CAPITAL LETTER H WITH STROKE (\textHbar in T4 encoding) + 127 LATIN SMALL LETTER H WITH STROKE (\texthbar in T4 encoding, \textcrh in tipa) --> +<symbol char="0128" text="\~I" /> +<symbol char="0129" text="\~\i" /> +<symbol char="012A" text="\=I" /> +<symbol char="012B" text="\=\i" /> +<symbol char="012C" text="\u{I}" /> +<symbol char="012D" text="\u{\i}" /> +<symbol char="012E" text="\k{I}" /> +<symbol char="012F" text="\k{i}" /> +<symbol char="0130" text="\.I" /> +<symbol char="0131" text="{\i}" /> +<symbol char="0132" text="{\IJ}" /> +<symbol char="0133" text="{\ij}" /> +<symbol char="0134" text="\^J" /> +<symbol char="0135" text="\^\j" /> +<symbol char="0136" text="\c{K}" /> +<symbol char="0137" text="\c{k}" /> +<!-- missing: 138 LATIN SMALL LETTER KRA (greenlandic) --> +<symbol char="0139" text="\'L" /> +<symbol char="013A" text="\'l" /> +<symbol char="013B" text="\c{L}" /> +<symbol char="013C" text="\c{l}" /> +<symbol char="013D" text="\v{L}" /> +<symbol char="013E" text="\v{l}" /> +<!-- missing: 13F LATIN CAPITAL LETTER L WITH MIDDLE DOT + 140 LATIN SMALL LETTER L WITH MIDDLE DOT + the version from ucs.sty should be ok --> +<symbol char="0141" text="{\L}" /> +<symbol char="0142" text="{\l}" /> +<symbol char="0143" text="\'N" /> +<symbol char="0144" text="\'n" /> +<symbol char="0145" text="\c{N}" /> +<symbol char="0146" text="\c{n}" /> +<symbol char="0147" text="\v{N}" /> +<symbol char="0148" text="\v{n}" /> +<!-- missing: 149 LATIN SMALL LETTER N PRECEDED BY APOSTROPHE (afrikaans) --> +<symbol char="014A" text="{\NG}" /> +<symbol char="014B" text="{\ng}" /> +<symbol char="014C" text="\=O" /> +<symbol char="014D" text="\=o" /> +<symbol char="014E" text="\u{O}" /> +<symbol char="014F" text="\u{o}" /> +<symbol char="0150" text="\H{O}" /> +<symbol char="0151" text="\H{o}" /> +<symbol char="0152" text="{\OE}" /> +<symbol char="0153" text="{\oe}" /> +<symbol char="0154" text="\'R" /> +<symbol char="0155" text="\'r" /> +<symbol char="0156" text="\c{R}" /> +<symbol char="0157" text="\c{r}" /> +<symbol char="0158" text="\v{R}" /> +<symbol char="0159" text="\v{r}" /> +<symbol char="015A" text="\'S" /> +<symbol char="015B" text="\'s" /> +<symbol char="015C" text="\^S" /> +<symbol char="015D" text="\^s" /> +<symbol char="015E" text="\c{S}" /> +<symbol char="015F" text="\c{s}" /> +<symbol char="0160" text="\v{S}" /> +<symbol char="0161" text="\v{s}" /> +<symbol char="0162" text="\c{T}" /> +<symbol char="0163" text="\c{t}" /> +<symbol char="0164" text="\v{T}" /> +<symbol char="0165" text="\v{t}" /> +<!-- missing: 166 LATIN CAPITAL LETTER T WITH STROKE (\textTbar in T4 encoding) + 167 LATIN SMALL LETTER T WITH STROKE (\texttbar in T4 encoding) --> +<symbol char="0168" text="\~U" /> +<symbol char="0169" text="\~u" /> +<symbol char="016A" text="\=U" /> +<symbol char="016B" text="\=u" /> +<symbol char="016C" text="\u{U}" /> +<symbol char="016D" text="\u{u}" /> +<symbol char="016E" text="\r{U}" /> +<symbol char="016F" text="\r{u}" /> +<symbol char="0170" text="\H{U}" /> +<symbol char="0171" text="\H{u}" /> +<symbol char="0172" text="\k{U}" /> +<symbol char="0173" text="\k{u}" /> +<symbol char="0174" text="\^W" /> +<symbol char="0175" text="\^w" /> +<symbol char="0176" text="\^Y" /> +<symbol char="0177" text="\^y" /> +<symbol char="0178" text='\"Y' /> +<symbol char="0179" text="\'Z" /> +<symbol char="017A" text="\'z" /> +<symbol char="017B" text="\.Z" /> +<symbol char="017C" text="\.z" /> +<symbol char="017D" text="\v{Z}" /> +<symbol char="017E" text="\v{z}" /> +<!-- missing: 017F LATIN SMALL LETTER LONG S + should be OK to replace with "s" --> +</symbol-subset> + +<!-- Unicode block: Combining Diacritical Marks --> +<symbol-subset fontenc="T1"> +<symbol char="0300" char-type="combining" text="\`" /> +<symbol char="0301" char-type="combining" text="\'" /> +<symbol char="0302" char-type="combining" text="\^" /> +<symbol char="0303" char-type="combining" text="\~" /> +<symbol char="0304" char-type="combining" text="\=" /> +<symbol char="0306" char-type="combining" text="\u" /> +<symbol char="0307" char-type="combining" text="\." /> +<symbol char="0308" char-type="combining" text='\"' /> +<symbol char="030B" char-type="combining" text="\H" /> +<symbol char="030C" char-type="combining" text="\v" /> +</symbol-subset> + +<!-- Unicode block: Greek and Coptic (math mode) --> +<symbol char="0391" math="A" /> +<symbol char="0392" math="B" /> +<symbol char="0393" math="\Gamma " /> +<symbol char="0394" math="\Delta " /> +<symbol char="0395" math="E" /> +<symbol char="0396" math="Z" /> +<symbol char="0397" math="H" /> +<symbol char="0398" math="\Theta " /> +<symbol char="0399" math="I" /> +<symbol char="039A" math="K" /> +<symbol char="039B" math="\Lambda " /> +<symbol char="039C" math="M" /> +<symbol char="039D" math="N" /> +<symbol char="039E" math="\Xi " /> +<symbol char="039F" math="O" /> +<symbol char="03A0" math="\Pi " /> +<symbol char="03A1" math="P" /> +<symbol char="03A3" math="\Sigma " /> +<symbol char="03A4" math="T" /> +<symbol char="03A5" math="Y" /> +<symbol char="03A6" math="\Phi " /> +<symbol char="03A7" math="X" /> +<symbol char="03A8" math="\Psi " /> +<symbol char="03A9" math="\Omega " /> +<symbol char="03B1" math="\alpha " /> +<symbol char="03B2" math="\beta " /> +<symbol char="03B3" math="\gamma " /> +<symbol char="03B4" math="\delta " /> +<symbol char="03B5" math="\varepsilon " /> +<symbol char="03B6" math="\zeta " /> +<symbol char="03B7" math="\eta " /> +<symbol char="03B8" math="\theta " /> +<symbol char="03B9" math="\iota " /> +<symbol char="03BA" math="\kappa " /> +<symbol char="03BB" math="\lambda " /> +<symbol char="03BC" math="\mu " /> +<symbol char="03BD" math="\nu " /> +<symbol char="03BE" math="\xi " /> +<symbol char="03BF" math="o" /> +<symbol char="03C0" math="\pi " /> +<symbol char="03C1" math="\rho " /> +<symbol char="03C2" math="\varsigma " /> +<symbol char="03C3" math="\sigma " /> +<symbol char="03C4" math="\tau " /> +<symbol char="03C5" math="\upsilon " /> +<symbol char="03C6" math="\varphi " /> +<symbol char="03C7" math="\chi " /> +<symbol char="03C8" math="\psi " /> +<symbol char="03C9" math="\omega " /> +<symbol char="03D1" math="\vartheta " /> +<symbol char="03D5" math="\varphi " /> +<symbol char="03D6" math="\varpi " /> +<symbol char="03DB" math="\varsigma " /> +<symbol char="03F1" math="\varrho " /> +<symbol char="03F5" math="\epsilon " /> + +<!-- Unicode block: Greek and coptic (text mode) --> +<symbol-subset fontenc="LGR"> +<!-- "Based on ISO-8859-7 --> +<symbol char="0374" text="{\anwtonos}" /> <!-- \char'376 --> +<symbol char="0375" text="{\katwtonos}" /> <!-- \char'377 --> +<symbol char="037A" text="|" /> <!-- iota subscriptum, \char'174 --> +<symbol char="037E" text="?" /> <!-- greek question mark (;) --> +<symbol char="0384" text="'" /> <!-- tonos, \char'047 --> +<symbol char="0385" text="'"" /> <!-- dieresis and tonos, \char'043 --> +<symbol char="0386" text="'A" /> +<symbol char="0387" text=";" /> <!-- ano teleia, \char'046 --> +<symbol char="0388" text="'E" /> +<symbol char="0389" text="'H" /> +<symbol char="038A" text="'I" /> +<symbol char="038C" text="'O" /> +<symbol char="038E" text="'U" /> +<symbol char="038F" text="'W" /> +<symbol char="0390" text=''"i' /> +<symbol char="0391" text="A" /> +<symbol char="0392" text="B" /> +<symbol char="0393" text="G" /> +<symbol char="0394" text="D" /> +<symbol char="0395" text="E" /> +<symbol char="0396" text="Z" /> +<symbol char="0397" text="H" /> +<symbol char="0398" text="J" /> +<symbol char="0399" text="I" /> +<symbol char="039A" text="K" /> +<symbol char="039B" text="L" /> +<symbol char="039C" text="M" /> +<symbol char="039D" text="N" /> +<symbol char="039E" text="X" /> +<symbol char="039F" text="O" /> +<symbol char="03A0" text="P" /> +<symbol char="03A1" text="R" /> +<symbol char="03A3" text="S" /> +<symbol char="03A4" text="T" /> +<symbol char="03A5" text="U" /> +<symbol char="03A6" text="F" /> +<symbol char="03A7" text="Q" /> +<symbol char="03A8" text="Y" /> +<symbol char="03A9" text="W" /> +<symbol char="03AA" text='"I' /> +<symbol char="03AB" text='"U' /> +<symbol char="03AC" text="'a" /> +<symbol char="03AD" text="'e" /> +<symbol char="03AE" text="'h" /> +<symbol char="03AF" text="'i" /> +<symbol char="03B0" text=''"u' /> +<symbol char="03B1" text="a" /> +<symbol char="03B2" text="b" /> +<symbol char="03B3" text="g" /> +<symbol char="03B4" text="d" /> +<symbol char="03B5" text="e" /> +<symbol char="03B6" text="z" /> +<symbol char="03B7" text="h" /> +<symbol char="03B8" text="j" /> +<symbol char="03B9" text="i" /> +<symbol char="03BA" text="k" /> +<symbol char="03BB" text="l" /> +<symbol char="03BC" text="m" /> +<symbol char="03BD" text="n" /> +<symbol char="03BE" text="x" /> +<symbol char="03BF" text="o" /> +<symbol char="03C0" text="p" /> +<symbol char="03C1" text="r" /> +<symbol char="03C2" text="c" /> <!-- GREEK SMALL LETTER FINAL SIGMA --> +<symbol char="03C3" text="s" /> +<symbol char="03C4" text="t" /> +<symbol char="03C5" text="u" /> +<symbol char="03C6" text="f" /> +<symbol char="03C7" text="q" /> +<symbol char="03C8" text="y" /> +<symbol char="03C9" text="w" /> +<symbol char="03CA" text='"i' /> +<symbol char="03CB" text='"u' /> +<symbol char="03CC" text="'o" /> +<symbol char="03CD" text="'u" /> +<symbol char="03CE" text="'w" /> +<!-- variant letterforms and archaic letters --> +<!-- todo: replace $\var...$ with ordinary text letter --> +<symbol char="03D0" text="b" /> <!-- curled beta, uses ordinary beta --> +<symbol char="03D1" text="j" /> <!-- GREEK THETA SYMBOL --> +<symbol char="03D2" text="U" /> <!-- GREEK UPSILON WITH HOOK SYMBOL --> +<symbol char="03D3" text="'U" /> <!-- GREEK UPSILON WITH ACUTE AND HOOK SYMBOL --> +<symbol char="03D4" text='"U' /> <!-- GREEK UPSILON WITH DIAERESIS AND HOOK SYMBOL --> +<symbol char="03D5" text="f" /> <!-- $\varphi$, uses ordinary phi, GREEK PHI SYMBOL --> +<symbol char="03D6" text="$\varpi$" /> <!-- GREEK PI SYMBOL --> +<symbol char="03DA" text="{\Stigma}" /> <!-- \char'007, GREEK LETTER STIGMA (math mode \Stigma) --> +<symbol char="03DB" text="{\stigma}" /> <!-- \char'006, GREEK SMALL LETTER STIGMA (math mode \stigma) --> +<symbol char="03DC" text="{\Digamma}" /> <!-- \char'303, GREEK LETTER DIGAMMA --> +<symbol char="03DD" text="{\ddigamma}" /> <!-- \char'223, GREEK SMAL LETTER DIGAMMA --> +<symbol char="03DE" text="{\qoppa}" /> +<symbol char="03DF" text="{\qoppa}" /> <!-- GREEK SMALL LETTER KOPPA --> +<symbol char="03E0" text="{\sampi}" /> +<symbol char="03E1" text="{\sampi}" /> <!-- GREEK SMALL LETTER SAMPI --> +<symbol char="03F0" text="k" /> <!-- GREEK KAPPA SYMBOL --> +<symbol char="03F1" text="$\varrho$" /> <!-- GREEK RHO SYMBOL --> +<symbol char="03F4" text="J" /> <!-- GREEK THETA SYMBOL --> +<symbol char="03F5" text="$\in$" /> <!-- GREEK LUNATE EPSILON SYMBOL --> +<symbol char="03F6" text="$\ni$" /> <!-- GREEK REVERSED LUNATE EPSILON SYMBOL --> +</symbol-subset> + +<!-- Unicode block: Cyrillic --> +<symbol-subset fontenc="T2A"> +<symbol char="0400" text="{\`\CYRE}" /> +<symbol char="0401" text="{\CYRYO}" /> +<symbol char="0402" text="{\CYRDJE}" /> +<symbol char="0403" text="{\'\CYRG}" /> <!-- should be \CYRGJE --> +<symbol char="0404" text="{\CYRIE}" /> +<symbol char="0405" text="{\CYRDZE}" /> +<symbol char="0406" text="{\CYRII}" /> +<symbol char="0407" text="{\CYRYI}" /> +<symbol char="0408" text="{\CYRJE}" /> +<symbol char="0409" text="{\CYRLJE}" /> +<symbol char="040A" text="{\CYRNJE}" /> +<symbol char="040B" text="{\CYRTSHE}" /> +<symbol char="040C" text="{\'\CYRK}" /> <!-- should be \CYRKJE --> +<symbol char="040D" text="{\`\CYRI}" /> +<symbol char="040E" text="{\CYRUSHRT}" /> +<symbol char="040F" text="{\CYRDZHE}" /> +<symbol char="0410" text="{\CYRA}" /> +<symbol char="0411" text="{\CYRB}" /> +<symbol char="0412" text="{\CYRV}" /> +<symbol char="0413" text="{\CYRG}" /> +<symbol char="0414" text="{\CYRD}" /> +<symbol char="0415" text="{\CYRE}" /> +<symbol char="0416" text="{\CYRZH}" /> +<symbol char="0417" text="{\CYRZ}" /> +<symbol char="0418" text="{\CYRI}" /> +<symbol char="0419" text="{\CYRISHRT}" /> +<symbol char="041A" text="{\CYRK}" /> +<symbol char="041B" text="{\CYRL}" /> +<symbol char="041C" text="{\CYRM}" /> +<symbol char="041D" text="{\CYRN}" /> +<symbol char="041E" text="{\CYRO}" /> +<symbol char="041F" text="{\CYRP}" /> +<symbol char="0420" text="{\CYRR}" /> � +<symbol char="0421" text="{\CYRS}" /> +<symbol char="0422" text="{\CYRT}" /> +<symbol char="0423" text="{\CYRU}" /> +<symbol char="0424" text="{\CYRF}" /> +<symbol char="0425" text="{\CYRH}" /> +<symbol char="0426" text="{\CYRC}" /> +<symbol char="0427" text="{\CYRCH}" /> +<symbol char="0428" text="{\CYRSH}" /> +<symbol char="0429" text="{\CYRSHCH}" /> +<symbol char="042A" text="{\CYRHRDSN}" /> +<symbol char="042B" text="{\CYRERY}" /> +<symbol char="042C" text="{\CYRSFTSN}" /> +<symbol char="042D" text="{\CYREREV}" /> +<symbol char="042E" text="{\CYRYU}" /> +<symbol char="042F" text="{\CYRYA}" /> +<symbol char="0430" text="{\cyra}" /> +<symbol char="0431" text="{\cyrb}" /> +<symbol char="0432" text="{\cyrv}" /> +<symbol char="0433" text="{\cyrg}" /> +<symbol char="0434" text="{\cyrd}" /> +<symbol char="0435" text="{\cyre}" /> +<symbol char="0436" text="{\cyrzh}" /> +<symbol char="0437" text="{\cyrz}" /> +<symbol char="0438" text="{\cyri}" /> +<symbol char="0439" text="{\cyrishrt}" /> +<symbol char="043A" text="{\cyrk}" /> +<symbol char="043B" text="{\cyrl}" /> +<symbol char="043C" text="{\cyrm}" /> +<symbol char="043D" text="{\cyrn}" /> +<symbol char="043E" text="{\cyro}" /> +<symbol char="043F" text="{\cyrp}" /> +<symbol char="0440" text="{\cyrr}" /> +<symbol char="0441" text="{\cyrs}" /> +<symbol char="0442" text="{\cyrt}" /> +<symbol char="0443" text="{\cyru}" /> +<symbol char="0444" text="{\cyrf}" /> +<symbol char="0445" text="{\cyrh}" /> +<symbol char="0446" text="{\cyrc}" /> +<symbol char="0447" text="{\cyrch}" /> +<symbol char="0448" text="{\cyrsh}" /> +<symbol char="0449" text="{\cyrshch}" /> +<symbol char="044A" text="{\cyrhrdsn}" /> +<symbol char="044B" text="{\cyrery}" /> +<symbol char="044C" text="{\cyrsftsn}" /> +<symbol char="044D" text="{\cyrerev}" /> +<symbol char="044E" text="{\cyryu}" /> +<symbol char="044F" text="{\cyrya}" /> +<symbol char="0450" text="{\`\cyre}" /> +<symbol char="0451" text="{\cyryo}" /> +<symbol char="0452" text="{\cyrdje}" /> +<symbol char="0453" text="{\'\cyrg}" /> <!-- should be \cyrgje --> +<symbol char="0454" text="{\cyrie}" /> +<symbol char="0455" text="{\cyrdze}" /> +<symbol char="0456" text="{\cyrii}" /> +<symbol char="0457" text="{\cyryi}" /> +<symbol char="0458" text="{\cyrje}" /> +<symbol char="0459" text="{\cyrlje}" /> +<symbol char="045A" text="{\cyrnje}" /> +<symbol char="045B" text="{\cyrtshe}" /> +<symbol char="045C" text="{\'\cyrk}" /> <!-- should be \cyrkje --> +<symbol char="045D" text="{\`\cyri}" /> +<symbol char="045E" text="{\cyrushrt}" /> +<symbol char="045F" text="{\cyrdzhe}" /> +<symbol char="0490" text="{\CYRGUP}" /> +<symbol char="0491" text="{\cyrgup}" /> +<symbol char="0492" text="{\CYRGHCRS}" /> +<symbol char="0493" text="{\cyrghcrs}" /> +<symbol char="0496" text="{\CYRZHDSC}" /> +<symbol char="0497" text="{\cyrzhdsc}" /> +<symbol char="0498" text="{\CYRZDSC}" /> +<symbol char="0499" text="{\cyrzdsc}" /> +<symbol char="049A" text="{\CYRKDSC}" /> +<symbol char="049B" text="{\cyrkdsc}" /> +<symbol char="049C" text="{\CYRKVCRS}" /> +<symbol char="049D" text="{\cyrkvcrs}" /> +<symbol char="04A0" text="{\CYRKBEAK}" /> � +<symbol char="04A1" text="{\cyrkbeak}" /> +<symbol char="04A2" text="{\CYRNDSC}" /> +<symbol char="04A3" text="{\cyrndsc}" /> +<symbol char="04A4" text="{\CYRNG}" /> +<symbol char="04A5" text="{\cyrng}" /> +<symbol char="04AA" text="{\CYRSDSC}" /> +<symbol char="04AB" text="{\cyrsdsc}" /> +<symbol char="04AE" text="{\CYRY}" /> +<symbol char="04AF" text="{\cyry}" /> +<symbol char="04B0" text="{\CYRYHCRS}" /> +<symbol char="04B1" text="{\cyryhcrs}" /> +<symbol char="04B2" text="{\CYRHDSC}" /> +<symbol char="04B3" text="{\cyrhdsc}" /> +<symbol char="04B6" text="{\CYRCHRDSC}" /> +<symbol char="04B7" text="{\cyrchrdsc}" /> +<symbol char="04B8" text="{\CYRCHVCRS}" /> +<symbol char="04B9" text="{\cyrchvcrs}" /> +<symbol char="04BA" text="{\CYRSHHA}" /> +<symbol char="04BB" text="{\cyrshha}" /> +<symbol char="04C0" text="{\CYRpalochka}" /> +<symbol char="04C1" text="{\U\CYRZH}" /> +<symbol char="04C2" text="{\U\cyrzh}" /> +<symbol char="04D0" text="{\U\CYRA}" /> +<symbol char="04D1" text="{\U\cyra}" /> +<symbol char="04D2" text='{\"\CYRA}' /> +<symbol char="04D3" text='{\"\cyra}' /> +<symbol char="04D4" text="{\CYRAE}" /> +<symbol char="04D5" text="{\cyrae}" /> +<symbol char="04D6" text="{\U\CYRE}" /> +<symbol char="04D7" text="{\U\cyre}" /> +<symbol char="04D8" text="{\CYRSCHWA}" /> +<symbol char="04D9" text="{\cyrschwa}" /> +<symbol char="04DA" text='{\"\CYRSCHWA}' /> +<symbol char="04DB" text='{\"\cyrschwa}' /> +<symbol char="04DC" text='{\"\CYRZH}' /> +<symbol char="04DD" text='{\"\cyrzh}' /> +<symbol char="04DE" text='{\"\CYRZ}' /> +<symbol char="04DF" text='{\"\cyrz}' /> +<symbol char="04E2" text="{\=\CYRI}" /> +<symbol char="04E3" text="{\=\cyri}" /> +<symbol char="04E4" text='{\"\CYRI}' /> +<symbol char="04E5" text='{\"\cyri}' /> +<symbol char="04E6" text='{\"\CYRO}' /> +<symbol char="04E7" text='{\"\cyro}' /> +<symbol char="04E8" text="{\CYROTLD}" /> +<symbol char="04E9" text="{\cyrotld}" /> +<symbol char="04EA" text='\"\CYROTLD}' /> +<symbol char="04EB" text='\"\cyrotld}' /> +<symbol char="04EC" text='\"\CYREREV}' /> +<symbol char="04ED" text='\"\cyrerev}' /> +<symbol char="04EE" text="{\=\CYRU}" /> +<symbol char="04EF" text="{\=\cyru}" /> +<symbol char="04F0" text='{\"\CYRU}' /> +<symbol char="04F1" text='{\"\cyru}' /> +<symbol char="04F2" text="{\H\CYRU}" /> +<symbol char="04F3" text="{\H\cyru}" /> +<symbol char="04F4" text='{\"\CYRCH}' /> +<symbol char="04F5" text='{\"\cyrch}' /> +<symbol char="04F8" text='{\"\CYRERY}' /> +<symbol char="04F9" text='{\"\cyrery}' /> +</symbol-subset> + +<!-- Unicode block: Greek extended (polytonic greek) --> +<symbol-subset fontenc="LGR"> +<symbol char="1F00" text=">a" /> +<symbol char="1F01" text="<a" /> +<symbol char="1F02" text=">`a" /> +<symbol char="1F03" text="<`a" /> +<symbol char="1F04" text=">'a" /> +<symbol char="1F05" text="<'a" /> +<symbol char="1F06" text=">~a" /> +<symbol char="1F07" text="<~a" /> +<symbol char="1F08" text=">A" /> +<symbol char="1F09" text="<A" /> +<symbol char="1F0A" text=">`A" /> +<symbol char="1F0B" text="<`A" /> +<symbol char="1F0C" text=">'A" /> +<symbol char="1F0D" text="<'A" /> +<symbol char="1F0E" text=">~A" /> +<symbol char="1F0F" text="<~A" /> +<symbol char="1F10" text=">e" /> +<symbol char="1F11" text="<e" /> +<symbol char="1F12" text=">`e" /> +<symbol char="1F13" text="<`e" /> +<symbol char="1F14" text=">'e" /> +<symbol char="1F15" text="<'e" /> +<symbol char="1F18" text=">E" /> +<symbol char="1F19" text="<E" /> +<symbol char="1F1A" text=">`E" /> +<symbol char="1F1B" text="<`E" /> +<symbol char="1F1C" text=">'E" /> +<symbol char="1F1D" text="<'E" /> +<symbol char="1F20" text=">h" /> +<symbol char="1F21" text="<h" /> +<symbol char="1F22" text=">`h" /> +<symbol char="1F23" text="<`h" /> +<symbol char="1F24" text=">'h" /> +<symbol char="1F25" text="<'h" /> +<symbol char="1F26" text=">~h" /> +<symbol char="1F27" text="<~h" /> +<symbol char="1F28" text=">H" /> +<symbol char="1F29" text="<H" /> +<symbol char="1F2A" text=">`H" /> +<symbol char="1F2B" text="<`H" /> +<symbol char="1F2C" text=">'H" /> +<symbol char="1F2D" text="<'H" /> +<symbol char="1F2E" text=">~H" /> +<symbol char="1F2F" text="<~H" /> +<symbol char="1F30" text=">i" /> +<symbol char="1F31" text="<i" /> +<symbol char="1F32" text=">`i" /> +<symbol char="1F33" text="<`i" /> +<symbol char="1F34" text=">'i" /> +<symbol char="1F35" text="<'i" /> +<symbol char="1F36" text=">~i" /> +<symbol char="1F37" text="<~i" /> +<symbol char="1F38" text=">I" /> +<symbol char="1F39" text="<I" /> +<symbol char="1F3A" text=">`I" /> +<symbol char="1F3B" text="<`I" /> +<symbol char="1F3C" text=">'I" /> +<symbol char="1F3D" text="<'I" /> +<symbol char="1F3E" text=">~I" /> +<symbol char="1F3F" text="<~I" /> +<symbol char="1F40" text=">o" /> +<symbol char="1F41" text="<o" /> +<symbol char="1F42" text=">`o" /> +<symbol char="1F43" text="<`o" /> +<symbol char="1F44" text=">'o" /> +<symbol char="1F45" text="<'o" /> +<symbol char="1F48" text=">O" /> +<symbol char="1F49" text="<O" /> +<symbol char="1F4A" text=">`O" /> +<symbol char="1F4B" text="<`O" /> +<symbol char="1F4C" text=">'O" /> +<symbol char="1F4D" text="<'O" /> +<symbol char="1F50" text=">u" /> +<symbol char="1F51" text="<u" /> +<symbol char="1F52" text=">`u" /> +<symbol char="1F53" text="<`u" /> +<symbol char="1F54" text=">'u" /> +<symbol char="1F55" text="<'u" /> +<symbol char="1F56" text=">~u" /> +<symbol char="1F57" text="<~u" /> +<symbol char="1F59" text="<U" /> +<symbol char="1F5B" text="<`U" /> +<symbol char="1F5D" text="<'U" /> +<symbol char="1F5F" text="<~U" /> +<symbol char="1F60" text=">w" /> +<symbol char="1F61" text="<w" /> +<symbol char="1F62" text=">`w" /> +<symbol char="1F63" text="<`w" /> +<symbol char="1F64" text=">'w" /> +<symbol char="1F65" text="<'w" /> +<symbol char="1F66" text=">~w" /> +<symbol char="1F67" text="<~w" /> +<symbol char="1F68" text=">W" /> +<symbol char="1F69" text="<W" /> +<symbol char="1F6A" text=">`W" /> +<symbol char="1F6B" text="<`W" /> +<symbol char="1F6C" text=">'W" /> +<symbol char="1F6D" text="<'W" /> +<symbol char="1F6E" text=">~W" /> +<symbol char="1F6F" text="<~W" /> +<symbol char="1F70" text="`a" /> +<symbol char="1F71" text="'a" /> +<symbol char="1F72" text="`e" /> +<symbol char="1F73" text="'e" /> +<symbol char="1F74" text="`h" /> +<symbol char="1F75" text="'h" /> +<symbol char="1F76" text="`i" /> +<symbol char="1F77" text="'i" /> +<symbol char="1F78" text="`o" /> +<symbol char="1F79" text="'o" /> +<symbol char="1F7A" text="`u" /> +<symbol char="1F7B" text="'u" /> +<symbol char="1F7C" text="`w" /> +<symbol char="1F7D" text="'w" /> +<symbol char="1F80" text=">a|" /> +<symbol char="1F81" text="<a|" /> +<symbol char="1F82" text=">`a|" /> +<symbol char="1F83" text="<`a|" /> +<symbol char="1F84" text=">'a|" /> +<symbol char="1F85" text="<'a|" /> +<symbol char="1F86" text=">~a|" /> +<symbol char="1F87" text="<~a|" /> +<symbol char="1F88" text=">A|" /> +<symbol char="1F89" text="<A|" /> +<symbol char="1F8A" text=">`A|" /> +<symbol char="1F8B" text="<`A|" /> +<symbol char="1F8C" text=">'A|" /> +<symbol char="1F8D" text="<'A|" /> +<symbol char="1F8E" text=">~A|" /> +<symbol char="1F8F" text="<~A|" /> +<symbol char="1F90" text=">h|" /> +<symbol char="1F91" text="<h|" /> +<symbol char="1F92" text=">`h|" /> +<symbol char="1F93" text="<`h|" /> +<symbol char="1F94" text=">'h|" /> +<symbol char="1F95" text="<'h|" /> +<symbol char="1F96" text=">~h|" /> +<symbol char="1F97" text="<~h|" /> +<symbol char="1F98" text=">H|" /> +<symbol char="1F99" text="<H|" /> +<symbol char="1F9A" text=">`H|" /> +<symbol char="1F9B" text="<`H|" /> +<symbol char="1F9C" text=">'H|" /> +<symbol char="1F9D" text="<'H|" /> +<symbol char="1F9E" text=">~H|" /> +<symbol char="1F9F" text="<~H|" /> +<symbol char="1FA0" text=">w|" /> +<symbol char="1FA1" text="<w|" /> +<symbol char="1FA2" text=">`w|" /> +<symbol char="1FA3" text="<`w|" /> +<symbol char="1FA4" text=">'w|" /> +<symbol char="1FA5" text="<'w|" /> +<symbol char="1FA6" text=">~w|" /> +<symbol char="1FA7" text="<~w|" /> +<symbol char="1FA8" text=">W|" /> +<symbol char="1FA9" text="<W|" /> +<symbol char="1FAA" text=">`W|" /> +<symbol char="1FAB" text="<`W|" /> +<symbol char="1FAC" text=">'W|" /> +<symbol char="1FAD" text="<'W|" /> +<symbol char="1FAE" text=">~W|" /> +<symbol char="1FAF" text="<~W|" /> +<symbol char="1FB0" text="\u{a}" /> +<symbol char="1FB1" text="\={a}" /> +<symbol char="1FB2" text="`a|" /> +<symbol char="1FB3" text="a|" /> +<symbol char="1FB4" text="'a|" /> +<symbol char="1FB6" text="~a" /> +<symbol char="1FB7" text="~a|" /> +<symbol char="1FB8" text="\u{A}" /> +<symbol char="1FB9" text="\={A}" /> +<symbol char="1FBA" text="`A" /> +<symbol char="1FBB" text="'A" /> +<symbol char="1FBC" text="A|" /> +<symbol char="1FBD" text=">" /> +<symbol char="1FBE" text="|" /> +<symbol char="1FBF" text=">" /> +<symbol char="1FC0" text="~" /> +<symbol char="1FC1" text='~"' /> +<symbol char="1FC2" text="`h|" /> +<symbol char="1FC3" text="h|" /> +<symbol char="1FC4" text="'h|" /> +<symbol char="1FC6" text="~h" /> +<symbol char="1FC7" text="~h|" /> +<symbol char="1FC8" text="`E" /> +<symbol char="1FC9" text="'E" /> +<symbol char="1FCA" text="`H" /> +<symbol char="1FCB" text="'H" /> +<symbol char="1FCC" text="H|" /> +<symbol char="1FCD" text=">`" /> +<symbol char="1FCE" text=">'" /> +<symbol char="1FCF" text=">~" /> +<symbol char="1FD0" text="\u{i}" /> +<symbol char="1FD1" text="\={i}" /> +<symbol char="1FD2" text='`"i' /> +<symbol char="1FD3" text=''"i' /> +<symbol char="1FD6" text="~i" /> +<symbol char="1FD7" text='~"i' /> +<symbol char="1FD8" text="\u{I}" /> +<symbol char="1FD9" text="\={I}" /> +<symbol char="1FDA" text="`I" /> +<symbol char="1FDB" text="'I" /> +<symbol char="1FDD" text="<`" /> +<symbol char="1FDE" text="<'" /> +<symbol char="1FDF" text="<~" /> +<symbol char="1FE0" text="\u{u}" /> +<symbol char="1FE1" text="\={u}" /> +<symbol char="1FE2" text='`"u' /> +<symbol char="1FE3" text=''"u' /> +<symbol char="1FE4" text="<r" /> +<symbol char="1FE5" text="<r" /> +<symbol char="1FE6" text="~u" /> +<symbol char="1FE7" text='~"u' /> +<symbol char="1FE8" text="\u{U}" /> +<symbol char="1FE9" text="\={U}" /> +<symbol char="1FEA" text="`U" /> +<symbol char="1FEB" text="'U" /> +<symbol char="1FEC" text="<R" /> +<symbol char="1FED" text='`"' /> +<symbol char="1FEE" text=''"' /> +<symbol char="1FEF" text="`" /> +<symbol char="1FF2" text="`w|" /> +<symbol char="1FF3" text="w|" /> +<symbol char="1FF4" text="'w|" /> +<symbol char="1FF6" text="~w" /> +<symbol char="1FF7" text="~w|" /> +<symbol char="1FF8" text="`O" /> +<symbol char="1FF9" text="'O" /> +<symbol char="1FFA" text="`W" /> +<symbol char="1FFB" text="'W" /> +<symbol char="1FFC" text="W|" /> +<symbol char="1FFD" text="'" /> +<symbol char="1FFE" text="<" /> +</symbol-subset> <!-- end of polytonic greek --> + +<!-- Unicode block: General Punctuation --> +<symbol char="2000" text="{\enspace}" /> +<symbol char="2001" text="{\quad}" /> +<symbol char="2002" text="{\enspace}" /> +<symbol char="2003" text="{\quad}" /> +<symbol char="2004" text="\hspace{0.333em}" /> +<symbol char="2005" text="\hspace{0.25em}" /> +<symbol char="2006" text="\hspace{0.167em}" /> +<symbol char="2009" text="\hspace{0.2em}" /> +<symbol char="200A" text="{\thinspace}" /> +<symbol char="200C" text="\textcompwordmark" /> +<symbol char="200D" text="" /> +<symbol char="2010" text="-" dashes="true" /> +<symbol char="2011" text="\nobreakdash-" /> <!-- requires ams --> +<symbol char="2012" text="--" dashes="true" /> +<symbol char="2013" text="--" dashes="true" /> +<symbol char="2014" text="---" dashes="true" /> +<symbol char="2015" text="---" dashes="true" /> +<symbol char="2016" text="{\textbardbl}" math="\|" /> +<symbol char="2018" text="{\textquoteleft}" /> <!-- ` is unsafe --> +<symbol char="2019" text="{\textquoteright}" /> <!-- ' is unsafe --> +<symbol char="201A" text="{\quotesinglbase}" /> +<symbol char="201C" text="{\textquotedblleft}" /> <!-- `` is unsafe --> +<symbol char="201D" text="{\textquotedblright}" /> <!-- '' is unsafe --> +<symbol char="201E" text="{\quotedblbase}" /> +<symbol char="2020" text="{\dag}" math="{\dag}" /> +<symbol char="2021" text="{\ddag}" math="{\ddag}" /> +<symbol char="2022" text="{\textbullet}" math="{\bullet}" /> +<symbol char="2024" text="." /> +<symbol char="2025" text=".." /> +<symbol char="2026" text="{\dots}" math="{\dots}" /> +<symbol char="2027" text="\-" /> <!-- wrong?? --> +<symbol char="2030" text="{\textperthousand}" /> +<symbol char="2031" text="{\textpertenthousand}" /> +<symbol char="2039" text="{\guilsinglleft}" /> +<symbol char="203A" text="{\guilsinglright}" /> +<symbol char="203B" text="{\textreferencemark}" /> +<symbol char="203C" text="!!" /> +<symbol char="203D" text="{\textinterrobang}" /> +<symbol char="2044" text="/" math="/" /> +<symbol char="2048" text="?!" /> +<symbol char="2049" text="!?" /> + +<!-- Unicode block: Currency symbols --> +<symbol char="20A1" text="{\textcolonmonetary}" /> +<symbol char="20A4" text="{\textlira}" /> +<symbol char="20A6" text="{\textnaira}" /> +<symbol char="20A9" text="{\textwon}" /> +<symbol char="20AB" text="{\textdong}" /> +<symbol char="20AC" text="{\texteuro}" /> +<!-- unicode block: Letterlike symbols --> +<symbol char="2100" text="a/c" /> +<symbol char="2101" text="a/s" /> +<symbol char="2102" math="\mathbb{C}" /> +<symbol char="2103" text="{\textcelsius}" /> +<symbol char="2105" text="c/o" /> +<symbol char="2106" text="c/u" /> +<symbol char="2107" math="\mathcal{E}" /> +<symbol-subset fontenc="T1"> +<symbol char="2109" text="\textdegree F" /> +</symbol-subset> +<symbol char="210C" math="\mathfrak{H}" /> +<symbol char="210D" math="\mathbb{H}" /> +<symbol char="210E" math="h" /> +<symbol char="210F" math="\hbar " /> +<symbol char="2111" math="\Im " /> +<symbol char="2113" math="\ell " /> +<symbol char="2115" math="\mathbb{N}" /> +<symbol char="2116" text="{\textnumero}" /> +<symbol char="2118" math="\wp " /> +<symbol char="2119" math="\mathbb{P}" /> +<symbol char="211A" math="\mathbb{Q}" /> +<symbol char="211C" math=" \Re " /> +<symbol char="211D" math="\mathbb{R}" /> +<symbol char="211E" text="{\textrecipe}" /> +<symbol char="2120" text="{\textservicemark}" /> +<symbol-subset fontenc="T1"> +<symbol char="2121" text="\textsc{tel}" /> +</symbol-subset> +<symbol char="2122" text="{\texttrademark}" /> +<symbol char="2124" math="\mathbb{Z}" /> +<symbol char="2126" text="{\textohm}" math="{\Omega}"/> +<symbol char="2127" text="{\textmho}" math="\mho"/> +<symbol char="2128" math="\mathfrak{Z}" /> +<symbol-subset fontenc="T1"> +<symbol char="212A" text="K" /> <!--Kelvin--> +<symbol char="212B" text="{\AA}" /> <!--�ngstr�m --> +</symbol-subset> +<symbol char="212D" math="\mathfrak{G}" /> +<symbol char="212E" text="{\textestimated}" /> +<symbol char="2132" math="\Finv " /> +<symbol char="2135" math="\aleph " /> +<symbol char="2136" math="\beth " /> +<symbol char="2137" math="\gimel " /> +<symbol char="2138" math="\daleth " /> + +<!-- Unicode block: Number forms --> +<symbol char="2153" math="\frac13"/> +<symbol char="2154" math="\frac23"/> +<symbol char="2155" math="\frac15"/> +<symbol char="2156" math="\frac25"/> +<symbol char="2157" math="\frac35"/> +<symbol char="2158" math="\frac45"/> +<symbol char="2159" math="\frac16"/> +<symbol char="215A" math="\frac56"/> +<symbol char="215B" math="\frac18"/> +<symbol char="215C" math="\frac38"/> +<symbol char="215D" math="\frac58"/> +<symbol char="215E" math="\frac78"/> +<symbol char="215F" math="\frac1{ }"/> +<symbol-subset fontenc="T1"> +<symbol char="2160" text="I"/> +<symbol char="2161" text="II"/> +<symbol char="2162" text="III"/> +<symbol char="2163" text="IV"/> +<symbol char="2164" text="V"/> +<symbol char="2165" text="VI"/> +<symbol char="2166" text="VII"/> +<symbol char="2167" text="VIII"/> +<symbol char="2168" text="IX"/> +<symbol char="2169" text="X"/> +<symbol char="216A" text="XI"/> +<symbol char="216B" text="XII"/> +<symbol char="216C" text="L"/> +<symbol char="216D" text="C"/> +<symbol char="216E" text="D"/> +<symbol char="216F" text="M"/> +<symbol char="2170" text="i"/> +<symbol char="2171" text="ii"/> +<symbol char="2172" text="iii"/> +<symbol char="2173" text="iv"/> +<symbol char="2174" text="v"/> +<symbol char="2175" text="vi"/> +<symbol char="2176" text="vii"/> +<symbol char="2177" text="viii"/> +<symbol char="2178" text="ix"/> +<symbol char="2179" text="x"/> +<symbol char="217A" text="xi"/> +<symbol char="217B" text="xii"/> +<symbol char="217C" text="l"/> +<symbol char="217D" text="c"/> +<symbol char="217E" text="d"/> +<symbol char="217F" text="m"/> +</symbol-subset> + +<!-- Unicode block: Arrows --> +<symbol char="2190" math="\leftarrow "/> +<symbol char="2191" math="\uparrow "/> +<symbol char="2192" math="\rightarrow "/> +<symbol char="2193" math="\downarrow "/> +<symbol char="2194" math="\leftrightarrow "/> +<symbol char="2195" math="\updownarrow "/> +<symbol char="2196" math="\nwarrow "/> +<symbol char="2197" math="\nearrow "/> +<symbol char="2198" math="\searrow "/> +<symbol char="2199" math="\searrow "/> +<symbol char="219A" math="\nleftarrow "/> +<symbol char="219B" math="\nrightarrow "/> +<symbol char="219D" math="\leadsto "/> +<symbol char="219E" math="\twoheadleftarrow "/> +<symbol char="21A0" math="\twoheadrightarrow "/> +<symbol char="21A2" math="\leftarrowtail "/> +<symbol char="21A3" math="\rightarrowtail "/> +<symbol char="21A6" math="\mapsto "/> +<symbol char="21A9" math="\hookleftarrow "/> +<symbol char="21AA" math="\hookrightarrow "/> +<symbol char="21AB" math="\looparrowleft "/> +<symbol char="21AC" math="\looparrowright "/> +<symbol char="21AD" math="\leftrightsquigarrow "/> +<symbol char="21AE" math="\nleftrightarrow "/> +<symbol char="21B0" math="\Lsh "/> +<symbol char="21B1" math="\Rsh "/> +<symbol char="21B6" math="\curvearrowleft "/> +<symbol char="21B7" math="\curvearrowright "/> +<symbol char="21BA" math="\circlearrowleft "/> +<symbol char="21BB" math="\circlearrowright "/> +<symbol char="21BC" math="\leftharpoonup "/> +<symbol char="21BD" math="\leftharpoondown "/> +<symbol char="21BE" math="\upharpoonright "/> +<symbol char="21BF" math="\upharpoonleft "/> +<symbol char="21C0" math="\rightharpoonup "/> +<symbol char="21C1" math="\rightharpoondown "/> +<symbol char="21C2" math="\downharpoonright "/> +<symbol char="21C3" math="\downharpoonleft "/> +<symbol char="21C4" math="\rightleftarrows "/> +<symbol char="21C6" math="\leftrightarrows "/> +<symbol char="21C7" math="\leftleftarrows "/> +<symbol char="21C8" math="\upuparrows "/> +<symbol char="21C9" math="\rightrightarrows "/> +<symbol char="21CA" math="\downdownarrows "/> +<symbol char="21CB" math="\leftrightharpoons "/> +<symbol char="21CC" math="\rightleftharpoons "/> +<symbol char="21CD" math="\nLeftarrow "/> +<symbol char="21CE" math="\nLeftrightarrow "/> +<symbol char="21CF" math="\nRightarrow "/> +<symbol char="21D0" math="\Leftarrow "/> +<symbol char="21D1" math="\Uparrow "/> +<symbol char="21D2" math="\Rightarrow "/> +<symbol char="21D3" math="\Downarrow "/> +<symbol char="21D4" math="\Leftrightarrow "/> +<symbol char="21D5" math="\Updownarrow "/> +<symbol char="21DA" math="\Lleftarrow "/> +<symbol char="21DD" math="\rightsquigarrow "/> +<symbol char="21E0" math="\dashleftarrow "/> +<symbol char="21E2" math="\dashrightarrow "/> + +<!-- Math: HER!!! --> + +<!-- Unicode block: Mathematical Operators --> +<symbol char="2200" math="{\forall}"/> +<symbol char="2201" math="{\complement}"/> +<symbol char="2202" math="{\partial}"/> +<symbol char="2203" math="{\exists}"/> +<symbol char="2204" math="{\nexists}"/> +<symbol char="2205" math="{\emptyset}"/> +<symbol char="2206" math="{\Delta}"/> +<symbol char="2207" math="{\nabla}"/> +<symbol char="2208" math="{\in}"/> +<symbol char="2209" math="{\notin}"/> +<symbol char="220A" math="{\in}"/> +<symbol char="220B" math="{\ni}"/> +<symbol char="220C" math="{\not\ni}"/> +<symbol char="220D" math="{\ni}"/> +<symbol char="220E" math="{\blacksquare}"/> +<symbol char="220F" math="{\prod}"/> +<symbol char="2210" math="{\amalg}"/> +<symbol char="2211" math="{\sum}"/> +<symbol char="2212" math="-"/> +<symbol char="2213" math="{\mp}"/> +<symbol char="2214" math="{\dotplus}"/> +<symbol char="2215" math="/"/> +<symbol char="2216" math="{\setminus}"/> +<symbol char="2217" math="{\ast}" text="{\textasteriskcentered}"/> +<symbol char="2218" math="{\circ}" text="{\textopenbullet}"/> +<symbol char="2219" math="{\bullet}" text="{\textbullet}"/> +<symbol char="221A" math="{\surd}"/> +<symbol char="221B" math="\sqrt[3]{}"/> +<symbol char="221C" math="\sqrt[4]{}"/> +<symbol char="221D" math="{\propto}"/> +<symbol char="221E" math="{\infty}"/> +<symbol char="2220" math="{\angle}"/> +<symbol char="2221" math="{\measuredangle}"/> +<symbol char="2222" math="{\sphericalangle}"/> +<symbol char="2223" math="{\mid}" text="{\textbar}"/> +<symbol char="2224" math="{\nmid}"/> +<symbol char="2225" math="{\parallel}" text="{\textbardbl}"/> +<symbol char="2226" math="{\nparallel}"/> +<symbol char="2227" math="{\wedge}"/> +<symbol char="2228" math="{\vee}"/> +<symbol char="2229" math="{\cap}"/> +<symbol char="222A" math="{\cup}"/> +<symbol char="222B" math="{\int}"/> +<symbol char="222C" math="{\iint}"/> +<symbol char="222D" math="{\iiint}"/> +<symbol char="222E" math="{\oint}"/> +<symbol char="222F" math="{\oiint}"/> +<symbol char="2230" math="{\oiiint}"/> +<symbol char="2234" math="{\therefore}"/> +<symbol char="2235" math="{\because}"/> +<symbol char="2236" math=":"/> +<symbol char="2237" math="::"/> +<symbol char="2238" math="\stackrel{.}{-}"/> +<symbol char="2239" math="-:"/> +<symbol char="223C" math="{\sim}"/> +<symbol char="223D" math="{\backsim}"/> +<symbol char="2240" math="{\wr}"/> +<symbol char="2241" math="{\nsim}"/> +<symbol char="2243" math="{\simeq}"/> +<symbol char="2244" math="{\not\simeq}"/> +<symbol char="2245" math="{\cong}"/> +<symbol char="2247" math="{\ncong}"/> +<symbol char="2248" math="{\approx}"/> +<symbol char="2249" math="{\not\approx}"/> +<symbol char="224A" math="{\approxeq}"/> +<symbol char="224D" math="{\asymp}"/> +<symbol char="224E" math="{\Bumpeq}"/> +<symbol char="224F" math="{\bumpeq}"/> +<symbol char="2250" math="{\doteq}"/> +<symbol char="2251" math="{\doteqdot}"/> +<symbol char="2252" math="{\fallingdotseq}"/> +<symbol char="2253" math="{\risingdotseq}"/> +<symbol char="2254" math=":="/> +<symbol char="2255" math="=:"/> +<symbol char="2256" math="{\eqcirc}"/> +<symbol char="2257" math="{\circeq}"/> +<symbol char="2258" math="\stackrel{\frown}{=}"/> +<symbol char="2259" math="\stackrel{\wedge}{=}"/> +<symbol char="225A" math="\stackrel{\vee}{=}"/> +<symbol char="225B" math="\stackrel{\star}{=}"/> +<symbol char="225C" math="{\triangleq}"/> +<symbol char="225D" math="{\defeq}"/> +<symbol char="225E" math="\stackrel{\mathrm{m}}{=}"/> +<symbol char="225F" math="\stackrel{?}{=}"/> +<symbol char="2260" math="{\neq}"/> +<symbol char="2261" math="{\equiv}"/> +<symbol char="2262" math="{\not\equiv}"/> +<symbol char="2264" math="{\leq}"/> +<symbol char="2265" math="{\geq}"/> +<symbol char="2266" math="{\leqq}"/> +<symbol char="2267" math="{\geqq}"/> +<symbol char="2268" math="{\lneqq}"/> +<symbol char="2269" math="{\gneqq}"/> +<symbol char="226A" math="{\ll}"/> +<symbol char="226B" math="{\gg}"/> +<symbol char="226C" math="{\between}"/> +<symbol char="226D" math="{\not\asymp}"/> +<symbol char="226E" math="{\nless}"/> +<symbol char="226F" math="{\ngtr}"/> +<symbol char="2270" math="{\nleq}"/> +<symbol char="2271" math="{\ngeq}"/> +<symbol char="2272" math="{\lesssim}"/> +<symbol char="2273" math="{\gtrsim}"/> +<symbol char="2274" math="{\not\lesssim}"/> +<symbol char="2275" math="{\not\gtrsim}"/> +<symbol char="2276" math="{\lessgtr}"/> +<symbol char="2277" math="{\gtrless}"/> +<symbol char="2278" math="{\not\lessgtr}"/> +<symbol char="2279" math="{\not\gtrless}"/> +<symbol char="227A" math="{\prec}"/> +<symbol char="227B" math="{\succ}"/> +<symbol char="227C" math="{\preccurlyeq}"/> +<symbol char="227D" math="{\succcurlyeq}"/> +<symbol char="227E" math="{\precsim}"/> +<symbol char="227F" math="{\succsim}"/> +<symbol char="2280" math="{\nsucc}"/> +<symbol char="2281" math="{\nprec}"/> +<symbol char="2282" math="{\subset}"/> +<symbol char="2283" math="{\supset}"/> +<symbol char="2284" math="{\not\subset}"/> +<symbol char="2285" math="{\not\supset}"/> +<symbol char="2286" math="{\subseteq}"/> +<symbol char="2287" math="{\supseteq}"/> +<symbol char="2288" math="{\nsubseteq}"/> +<symbol char="2289" math="{\nsupseteq}"/> +<symbol char="228A" math="{\subsetneq}"/> +<symbol char="228B" math="{\supsetneq}"/> +<symbol char="228E" math="{\uplus}"/> +<symbol char="228F" math="{\sqsubset}"/> +<symbol char="2290" math="{\sqsupset}"/> +<symbol char="2291" math="{\sqsubseteq}"/> +<symbol char="2292" math="{\sqsupseteq}"/> +<symbol char="2293" math="{\sqcap}"/> +<symbol char="2294" math="{\sqcup}"/> +<symbol char="2295" math="{\oplus}"/> +<symbol char="2296" math="{\ominus}"/> +<symbol char="2297" math="{\otimes}"/> +<symbol char="2298" math="{\oslash}"/> +<symbol char="2299" math="{\odot}"/> +<symbol char="229A" math="{\circledcirc}"/> +<symbol char="229B" math="{\circledast}"/> +<symbol char="229D" math="{\circleddash}"/> +<symbol char="229E" math="{\boxplus}"/> +<symbol char="229F" math="{\boxminus}"/> +<symbol char="22A0" math="{\boxtimes}"/> +<symbol char="22A1" math="{\boxdot}"/> +<symbol char="22A2" math="{\vdash}"/> +<symbol char="22A3" math="{\dashv}"/> +<symbol char="22A4" math="{\bot}"/> +<symbol char="22A5" math="{\perp}"/> +<symbol char="22A6" math="{\vdash}"/> +<symbol char="22A7" math="{\vDash}"/> +<symbol char="22A8" math="{\models}"/> +<symbol char="22A9" math="{\Vdash}"/> +<symbol char="22AA" math="{\Vvdash}"/> +<symbol char="22AC" math="{\nvdash}"/> +<symbol char="22AD" math="{\nvDash}"/> +<symbol char="22AE" math="{\not\Vdash}"/> +<symbol char="22AF" math="{\nVdash}"/> +<symbol char="22B2" math="{\lhd}"/> +<symbol char="22B3" math="{\rhd}"/> +<symbol char="22B4" math="{\unlhd}"/> +<symbol char="22B5" math="{\unrhd}"/> +<symbol char="22B6" math="{\multimapdotbothA}"/> +<symbol char="22B7" math="{\multimapdotbothB}"/> +<symbol char="22B8" math="{\multimap}"/> +<symbol char="22BA" math="{\intercal}"/> +<symbol char="22BB" math="{\veebar}"/> +<symbol char="22BC" math="{\barwedge}"/> +<symbol char="22C0" math="{\bigwedge}"/> +<symbol char="22C1" math="{\bigvee}"/> +<symbol char="22C2" math="{\bigcap}"/> +<symbol char="22C3" math="{\bigcup}"/> +<symbol char="22C4" math="{\diamond}"/> +<symbol char="22C5" math="{\cdot}"/> +<symbol char="22C6" math="{\star}"/> +<symbol char="22C7" math="{\divideontimes}"/> +<symbol char="22C8" math="{\bowtie}"/> +<symbol char="22C9" math="{\ltimes}"/> +<symbol char="22CA" math="{\rtimes}"/> +<symbol char="22CB" math="{\leftthreetimes}"/> +<symbol char="22CC" math="{\rightthreetimes}"/> +<symbol char="22CD" math="{\backsimeq}"/> +<symbol char="22CE" math="{\curlyvee}"/> +<symbol char="22CF" math="{\curlywedge}"/> +<symbol char="22D0" math="{\Subset}"/> +<symbol char="22D1" math="{\Supset}"/> +<symbol char="22D2" math="{\Cap}"/> +<symbol char="22D3" math="{\Cup}"/> +<symbol char="22D4" math="{\pitchfork}"/> +<symbol char="22D6" math="{\lessdot}"/> +<symbol char="22D7" math="{\gtrdot}"/> +<symbol char="22D8" math="{\lll}"/> +<symbol char="22D9" math="{\ggg}"/> +<symbol char="22DA" math="{\lesseqgtr}"/> +<symbol char="22DB" math="{\gtreqless}"/> +<symbol char="22DC" math="{\eqslantless}"/> +<symbol char="22DD" math="{\eqslantgtr}"/> +<symbol char="22DE" math="{\curlyeqprec}"/> +<symbol char="22DF" math="{\curlyeqsucc}"/> +<symbol char="22E0" math="{\not\curlyeqprec}"/> +<symbol char="22E1" math="{\not\curlyeqsucc}"/> +<symbol char="22E2" math="{\not\sqsubseteq}"/> +<symbol char="22E3" math="{\not\sqsupseteq}"/> +<symbol char="22E6" math="{\lnsim}"/> +<symbol char="22E7" math="{\gnsim}"/> +<symbol char="22E8" math="{\precnsim}"/> +<symbol char="22E9" math="{\succnsim}"/> +<symbol char="22EA" math="{\ntriangleleft}"/> +<symbol char="22EB" math="{\ntriangleright}"/> +<symbol char="22EC" math="{\ntrianglelefteq}"/> +<symbol char="22ED" math="{\ntrianglerighteq}"/> +<symbol char="22EE" math="{\vdots}"/> +<symbol char="22EF" math="{\cdots}"/> +<symbol char="22F0" math="{\ddotsup}"/> +<symbol char="22F1" math="{\ddots}"/> + +<!-- Unicode block: Geometric Shapes + some symbols are approximations; ifsym gives better results +--> +<symbol char="25A0" math="{\blacksquare}" /> +<symbol char="25A1" math="{\square}" /> +<symbol char="25AA" math="{\blacksquare}" /> +<symbol char="25AB" math="{\square}" /> +<symbol char="25B2" math="{\blacktriangle}" /> +<symbol char="25B3" math="{\bigtriangleup}" /> +<symbol char="25B4" math="{\blacktriangle}" /> +<symbol char="25B5" math="{\triangle}" /> +<symbol char="25B6" math="{\blacktriangleright}" /> +<symbol char="25B7" math="{\triangleright}" /> +<symbol char="25B8" math="{\blacktriangleright}" /> +<symbol char="25B9" math="{\triangleright}" /> +<symbol char="25BA" math="{\blacktriangleright}" /> +<symbol char="25BB" math="{\triangleright}" /> +<symbol char="25BC" math="{\blacktriangledown}" /> +<symbol char="25BD" math="{\bigtriangledown}" /> +<symbol char="25BE" math="{\blacktriangledown}" /> +<symbol char="25BF" math="{\triangledown}" /> +<symbol char="25C0" math="{\blacktriangleleft}" /> +<symbol char="25C1" math="{\triangleleft}" /> +<symbol char="25C2" math="{\blacktriangleleft}" /> +<symbol char="25C3" math="{\triangleleft}" /> +<symbol char="25C4" math="{\blacktriangleleft}" /> +<symbol char="25C5" math="{\triangleleft}" /> +<symbol char="25C6" math="{\blacklozenge}" /> +<symbol char="25C7" math="{\lozenge}" /> +<symbol char="25CA" math="{\lozenge}" /> +<symbol char="25CB" math="{\circ}" /> +<symbol char="25CF" math="{\bullet}" /> +<symbol char="25E6" math="{\circ}" /> +<symbol char="25EF" math="{\bigcirc}" /> +<symbol char="25FB" math="{\square}" /> +<symbol char="25FC" math="{\blacksquare}" /> +<symbol char="25FD" math="{\square}" /> +<symbol char="25FE" math="{\blacksquare}" /> + +<!-- Unicode block: Miscellaneous symbols --> +<symbol char="2660" math="{\spadesuit}" /> +<symbol char="2661" math="{\heartsuit}" /> +<symbol char="2662" math="{\diamondsuit}" /> +<symbol char="2663" math="{\clubsuit}" /> +<symbol char="2664" math="{\spadesuit}" /> +<symbol char="2665" math="{\heartsuit}" /> +<symbol char="2666" math="{\diamondsuit}" /> +<symbol char="2667" math="{\clubsuit}" /> +<symbol char="266A" text="{\textmusicalnote}" /> +<symbol char="266D" math="{\flat}" /> +<symbol char="266E" math="{\natural}" /> +<symbol char="266F" math="{\sharp}" /> +</symbol-set> <!-- end of ascii --> + +<!-- +******************************************************** +PART II: Support for various input encodings +Note: These tables specifies which characters should +be preserved by Writer2LaTeX. Java's OutputStreamWriter +takes care of the mapping from unicode to 8 bit encoding +******************************************************** +--> + +<!-- Latin 1 input encoding (ISO-8859-1) --> +<symbol-set name="latin1" fontenc="T1"> +<!-- Unicode block: Latin-1 supplement --> +<preserve-symbol char="00A1" mode="text" /> +<symbol-subset fontenc="any"> +<preserve-symbols first-char="00A2" last-char="00AB" mode="text" /> +<preserve-symbol char="00A3" mode="math" /> +<preserve-symbol char="00A7" mode="math" /> +<preserve-symbol char="00AC" mode="math" /> +<preserve-symbols first-char="00AD" last-char="00B0" mode="text" /> +<preserve-symbols first-char="00B1" last-char="00B3" mode="math" /> +<preserve-symbol char="00B4" mode="text" /> +<preserve-symbol char="00B5" mode="math" /> +<preserve-symbol char="00B6" mode="both" /> +<preserve-symbol char="00B8" mode="text" /> +<preserve-symbol char="00B9" mode="math" /> +<preserve-symbols first-char="00BA" last-char="00BE" mode="text" /> +</symbol-subset> +<preserve-symbols first-char="00BF" last-char="00D6" mode="text" /> +<preserve-symbol char="00D7" mode="math" /> +<preserve-symbols first-char="00D8" last-char="00F6" mode="text" /> +<preserve-symbol char="00F7" mode="math" /> +<preserve-symbols first-char="00F8" last-char="00FF" mode="text" /> +</symbol-set> <!-- end of latin1 --> + +<!-- latin2 input encoding (ISO-8859-2) --> +<symbol-set name="latin2" fontenc="T1"> +<!-- ordered by position in latin2 rather than by unicode block --> + <preserve-symbol char="0104" mode="text" /> <!-- 00A1: LATIN CAPITAL LETTER A WITH OGONEK --> + <preserve-symbol char="02D8" mode="text" /> <!-- 00A2: BREVE --> + <preserve-symbol char="0141" mode="text" /> <!-- 00A3: LATIN CAPITAL LETTER L WITH STROKE --> + <preserve-symbol char="00A4" mode="text" /> <!-- 00A4: CURRENCY SIGN --> + <preserve-symbol char="013D" mode="text" /> <!-- 00A5: LATIN CAPITAL LETTER L WITH CARON --> + <preserve-symbol char="015A" mode="text" /> <!-- 00A6: LATIN CAPITAL LETTER S WITH ACUTE --> + <preserve-symbol char="00A7" mode="text" /> <!-- 00A7: SECTION SIGN --> + <preserve-symbol char="00A8" mode="text" /> <!-- 00A8: DIAERESIS --> + <preserve-symbol char="0160" mode="text" /> <!-- 00A9: LATIN CAPITAL LETTER S WITH CARON --> + <preserve-symbol char="015E" mode="text" /> <!-- 00AA: LATIN CAPITAL LETTER S WITH CEDILLA --> + <preserve-symbol char="0164" mode="text" /> <!-- 00AB: LATIN CAPITAL LETTER T WITH CARON --> + <preserve-symbol char="0179" mode="text" /> <!-- 00AC: LATIN CAPITAL LETTER Z WITH ACUTE --> + <preserve-symbol char="00AD" mode="text" /> <!-- 00AD: SOFT HYPHEN --> + <preserve-symbol char="017D" mode="text" /> <!-- 00AE: LATIN CAPITAL LETTER Z WITH CARON --> + <preserve-symbol char="017B" mode="text" /> <!-- 00AF: LATIN CAPITAL LETTER Z WITH DOT ABOVE --> + <preserve-symbol char="00B0" mode="text" /> <!-- 00B0: DEGREE SIGN --> + <preserve-symbol char="0105" mode="text" /> <!-- 00B1: LATIN SMALL LETTER A WITH OGONEK --> + <preserve-symbol char="02DB" mode="text" /> <!-- 00B2: OGONEK --> + <preserve-symbol char="0142" mode="text" /> <!-- 00B3: LATIN SMALL LETTER L WITH STROKE --> + <preserve-symbol char="00B4" mode="text" /> <!-- 00B4: ACUTE ACCENT --> + <preserve-symbol char="013E" mode="text" /> <!-- 00B5: LATIN SMALL LETTER L WITH CARON --> + <preserve-symbol char="015B" mode="text" /> <!-- 00B6: LATIN SMALL LETTER S WITH ACUTE --> + <preserve-symbol char="02C7" mode="text" /> <!-- 00B7: CARON --> + <preserve-symbol char="00B8" mode="text" /> <!-- 00B8: CEDILLA --> + <preserve-symbol char="0161" mode="text" /> <!-- 00B9: LATIN SMALL LETTER S WITH CARON --> + <preserve-symbol char="015F" mode="text" /> <!-- 00BA: LATIN SMALL LETTER S WITH CEDILLA --> + <preserve-symbol char="0165" mode="text" /> <!-- 00BB: LATIN SMALL LETTER T WITH CARON --> + <preserve-symbol char="017A" mode="text" /> <!-- 00BC: LATIN SMALL LETTER Z WITH ACUTE --> + <preserve-symbol char="02DD" mode="text" /> <!-- 00BD: DOUBLE ACUTE ACCENT --> + <preserve-symbol char="017E" mode="text" /> <!-- 00BE: LATIN SMALL LETTER Z WITH CARON --> + <preserve-symbol char="017C" mode="text" /> <!-- 00BF: LATIN SMALL LETTER Z WITH DOT ABOVE --> + <preserve-symbol char="0154" mode="text" /> <!-- 00C0: LATIN CAPITAL LETTER R WITH ACUTE --> + <preserve-symbol char="00C1" mode="text" /> <!-- 00C1: LATIN CAPITAL LETTER A WITH ACUTE --> + <preserve-symbol char="00C2" mode="text" /> <!-- 00C2: LATIN CAPITAL LETTER A WITH CIRCUMFLEX --> + <preserve-symbol char="0102" mode="text" /> <!-- 00C3: LATIN CAPITAL LETTER A WITH BREVE --> + <preserve-symbol char="00C4" mode="text" /> <!-- 00C4: LATIN CAPITAL LETTER A WITH DIAERESIS --> + <preserve-symbol char="0139" mode="text" /> <!-- 00C5: LATIN CAPITAL LETTER L WITH ACUTE --> + <preserve-symbol char="0106" mode="text" /> <!-- 00C6: LATIN CAPITAL LETTER C WITH ACUTE --> + <preserve-symbol char="00C7" mode="text" /> <!-- 00C7: LATIN CAPITAL LETTER C WITH CEDILLA --> + <preserve-symbol char="010C" mode="text" /> <!-- 00C8: LATIN CAPITAL LETTER C WITH CARON --> + <preserve-symbol char="00C9" mode="text" /> <!-- 00C9: LATIN CAPITAL LETTER E WITH ACUTE --> + <preserve-symbol char="0118" mode="text" /> <!-- 00CA: LATIN CAPITAL LETTER E WITH OGONEK --> + <preserve-symbol char="00CB" mode="text" /> <!-- 00CB: LATIN CAPITAL LETTER E WITH DIAERESIS --> + <preserve-symbol char="011A" mode="text" /> <!-- 00CC: LATIN CAPITAL LETTER E WITH CARON --> + <preserve-symbol char="00CD" mode="text" /> <!-- 00CD: LATIN CAPITAL LETTER I WITH ACUTE --> + <preserve-symbol char="00CE" mode="text" /> <!-- 00CE: LATIN CAPITAL LETTER I WITH CIRCUMFLEX --> + <preserve-symbol char="010E" mode="text" /> <!-- 00CF: LATIN CAPITAL LETTER D WITH CARON --> + <preserve-symbol char="0110" mode="text" /> <!-- 00D0: LATIN CAPITAL LETTER D WITH STROKE --> + <preserve-symbol char="0143" mode="text" /> <!-- 00D1: LATIN CAPITAL LETTER N WITH ACUTE --> + <preserve-symbol char="0147" mode="text" /> <!-- 00D2: LATIN CAPITAL LETTER N WITH CARON --> + <preserve-symbol char="00D3" mode="text" /> <!-- 00D3: LATIN CAPITAL LETTER O WITH ACUTE --> + <preserve-symbol char="00D4" mode="text" /> <!-- 00D4: LATIN CAPITAL LETTER O WITH CIRCUMFLEX --> + <preserve-symbol char="0150" mode="text" /> <!-- 00D5: LATIN CAPITAL LETTER O WITH DOUBLE ACUTE --> + <preserve-symbol char="00D6" mode="text" /> <!-- 00D6: LATIN CAPITAL LETTER O WITH DIAERESIS --> + <preserve-symbol char="00D7" mode="math" /> <!-- 00D7: MULTIPLICATION SIGN --> + <preserve-symbol char="0158" mode="text" /> <!-- 00D8: LATIN CAPITAL LETTER R WITH CARON --> + <preserve-symbol char="016E" mode="text" /> <!-- 00D9: LATIN CAPITAL LETTER U WITH RING ABOVE --> + <preserve-symbol char="00DA" mode="text" /> <!-- 00DA: LATIN CAPITAL LETTER U WITH ACUTE --> + <preserve-symbol char="0170" mode="text" /> <!-- 00DB: LATIN CAPITAL LETTER U WITH DOUBLE ACUTE --> + <preserve-symbol char="00DC" mode="text" /> <!-- 00DC: LATIN CAPITAL LETTER U WITH DIAERESIS --> + <preserve-symbol char="00DD" mode="text" /> <!-- 00DD: LATIN CAPITAL LETTER Y WITH ACUTE --> + <preserve-symbol char="0162" mode="text" /> <!-- 00DE: LATIN CAPITAL LETTER T WITH CEDILLA --> + <preserve-symbol char="00DF" mode="text" /> <!-- 00DF: LATIN SMALL LETTER SHARP S --> + <preserve-symbol char="0155" mode="text" /> <!-- 00E0: LATIN SMALL LETTER R WITH ACUTE --> + <preserve-symbol char="00E1" mode="text" /> <!-- 00E1: LATIN SMALL LETTER A WITH ACUTE --> + <preserve-symbol char="00E2" mode="text" /> <!-- 00E2: LATIN SMALL LETTER A WITH CIRCUMFLEX --> + <preserve-symbol char="0103" mode="text" /> <!-- 00E3: LATIN SMALL LETTER A WITH BREVE --> + <preserve-symbol char="00E4" mode="text" /> <!-- 00E4: LATIN SMALL LETTER A WITH DIAERESIS --> + <preserve-symbol char="013A" mode="text" /> <!-- 00E5: LATIN SMALL LETTER L WITH ACUTE --> + <preserve-symbol char="0107" mode="text" /> <!-- 00E6: LATIN SMALL LETTER C WITH ACUTE --> + <preserve-symbol char="00E7" mode="text" /> <!-- 00E7: LATIN SMALL LETTER C WITH CEDILLA --> + <preserve-symbol char="010D" mode="text" /> <!-- 00E8: LATIN SMALL LETTER C WITH CARON --> + <preserve-symbol char="00E9" mode="text" /> <!-- 00E9: LATIN SMALL LETTER E WITH ACUTE --> + <preserve-symbol char="0119" mode="text" /> <!-- 00EA: LATIN SMALL LETTER E WITH OGONEK --> + <preserve-symbol char="00EB" mode="text" /> <!-- 00EB: LATIN SMALL LETTER E WITH DIAERESIS --> + <preserve-symbol char="011B" mode="text" /> <!-- 00EC: LATIN SMALL LETTER E WITH CARON --> + <preserve-symbol char="00ED" mode="text" /> <!-- 00ED: LATIN SMALL LETTER I WITH ACUTE --> + <preserve-symbol char="00EE" mode="text" /> <!-- 00EE: LATIN SMALL LETTER I WITH CIRCUMFLEX --> + <preserve-symbol char="010F" mode="text" /> <!-- 00EF: LATIN SMALL LETTER D WITH CARON --> + <preserve-symbol char="0111" mode="text" /> <!-- 00F0: LATIN SMALL LETTER D WITH STROKE --> + <preserve-symbol char="0144" mode="text" /> <!-- 00F1: LATIN SMALL LETTER N WITH ACUTE --> + <preserve-symbol char="0148" mode="text" /> <!-- 00F2: LATIN SMALL LETTER N WITH CARON --> + <preserve-symbol char="00F3" mode="text" /> <!-- 00F3: LATIN SMALL LETTER O WITH ACUTE --> + <preserve-symbol char="00F4" mode="text" /> <!-- 00F4: LATIN SMALL LETTER O WITH CIRCUMFLEX --> + <preserve-symbol char="0151" mode="text" /> <!-- 00F5: LATIN SMALL LETTER O WITH DOUBLE ACUTE --> + <preserve-symbol char="00F6" mode="text" /> <!-- 00F6: LATIN SMALL LETTER O WITH DIAERESIS --> + <preserve-symbol char="00F7" mode="math" /> <!-- 00F7: DIVISION SIGN --> + <preserve-symbol char="0159" mode="text" /> <!-- 00F8: LATIN SMALL LETTER R WITH CARON --> + <preserve-symbol char="016F" mode="text" /> <!-- 00F9: LATIN SMALL LETTER U WITH RING ABOVE --> + <preserve-symbol char="00FA" mode="text" /> <!-- 00FA: LATIN SMALL LETTER U WITH ACUTE --> + <preserve-symbol char="0171" mode="text" /> <!-- 00FB: LATIN SMALL LETTER U WITH DOUBLE ACUTE --> + <preserve-symbol char="00FC" mode="text" /> <!-- 00FC: LATIN SMALL LETTER U WITH DIAERESIS --> + <preserve-symbol char="00FD" mode="text" /> <!-- 00FD: LATIN SMALL LETTER Y WITH ACUTE --> + <preserve-symbol char="0163" mode="text" /> <!-- 00FE: LATIN SMALL LETTER T WITH CEDILLA --> + <preserve-symbol char="02D9" mode="text" /> <!-- 00FF: DOT ABOVE --> +</symbol-set> <!-- end of latin2 --> + +<!-- cp1250 encoding (Microsoft Windows Eastern European) --> +<symbol-set name="cp1250" fontenc="T1"> + <!-- ordered by position in cp1250 rather than by unicode block --> + <!--<preserve-symbol char="20AC" mode="text" />--><!-- 80 EURO SIGN --> + <preserve-symbol char="201A" mode="text" /> <!-- 82 SINGLE LOW-9 QUOTATION MARK --> + <preserve-symbol char="201E" mode="text" /> <!-- 84 DOUBLE LOW-9 QUOTATION MARK --> + <preserve-symbol char="2026" mode="text" /> <!-- 85 HORIZONTAL ELLIPSIS --> + <preserve-symbol char="2020" mode="text" /> <!-- 86 DAGGER --> + <preserve-symbol char="2021" mode="text" /> <!-- 87 DOUBLE DAGGER --> + <preserve-symbol char="2030" mode="text" /> <!-- 89 PER MILLE SIGN --> + <preserve-symbol char="0160" mode="text" /> <!-- 8A LATIN CAPITAL LETTER S WITH CARON --> + <preserve-symbol char="2039" mode="text" /> <!-- 8B SINGLE LEFT-POINTING ANGLE QUOTATION MARK --> + <preserve-symbol char="015A" mode="text" /> <!-- 8C LATIN CAPITAL LETTER S WITH ACUTE --> + <preserve-symbol char="0164" mode="text" /> <!-- 8D LATIN CAPITAL LETTER T WITH CARON --> + <preserve-symbol char="017D" mode="text" /> <!-- 8E LATIN CAPITAL LETTER Z WITH CARON --> + <preserve-symbol char="0179" mode="text" /> <!-- 8F LATIN CAPITAL LETTER Z WITH ACUTE --> + <preserve-symbol char="2018" mode="text" /> <!-- 91 LEFT SINGLE QUOTATION MARK --> + <preserve-symbol char="2019" mode="text" /> <!-- 92 RIGHT SINGLE QUOTATION MARK --> + <preserve-symbol char="201C" mode="text" /> <!-- 93 LEFT DOUBLE QUOTATION MARK --> + <preserve-symbol char="201D" mode="text" /> <!-- 94 RIGHT DOUBLE QUOTATION MARK --> + <preserve-symbol char="2022" mode="text" /> <!-- 95 BULLET --> + <preserve-symbol char="2013" mode="text" /> <!-- 96 EN DASH --> + <preserve-symbol char="2014" mode="text" /> <!-- 97 EM DASH --> + <preserve-symbol char="2122" mode="text" /> <!-- 99 TRADE MARK SIGN --> + <preserve-symbol char="0161" mode="text" /> <!-- 9A LATIN SMALL LETTER S WITH CARON --> + <preserve-symbol char="203A" mode="text" /> <!-- 9B SINGLE RIGHT-POINTING ANGLE QUOTATION MARK --> + <preserve-symbol char="015B" mode="text" /> <!-- 9C LATIN SMALL LETTER S WITH ACUTE --> + <preserve-symbol char="0165" mode="text" /> <!-- 9D LATIN SMALL LETTER T WITH CARON --> + <preserve-symbol char="017E" mode="text" /> <!-- 9E LATIN SMALL LETTER Z WITH CARON --> + <preserve-symbol char="017A" mode="text" /> <!-- 9F LATIN SMALL LETTER Z WITH ACUTE --> + <preserve-symbol char="02C7" mode="text" /> <!-- A1 CARON --> + <preserve-symbol char="02D8" mode="text" /> <!-- A2 BREVE --> + <preserve-symbol char="0141" mode="text" /> <!-- A3 LATIN CAPITAL LETTER L WITH STROKE --> + <preserve-symbol char="00A4" mode="text" /> <!-- A4 CURRENCY SIGN --> + <preserve-symbol char="0104" mode="text" /> <!-- A5 LATIN CAPITAL LETTER A WITH OGONEK --> + <preserve-symbol char="00A6" mode="text" /> <!-- A6 BROKEN BAR --> + <preserve-symbol char="00A7" mode="text" /> <!-- A7 SECTION SIGN --> + <preserve-symbol char="00A8" mode="text" /> <!-- A8 DIAERESIS --> + <preserve-symbol char="00A9" mode="text" /> <!-- A9 COPYRIGHT SIGN --> + <preserve-symbol char="015E" mode="text" /> <!-- AA LATIN CAPITAL LETTER S WITH CEDILLA --> + <preserve-symbol char="00AB" mode="text" /> <!-- AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <preserve-symbol char="00AD" mode="text" /> <!-- AD SOFT HYPHEN --> + <preserve-symbol char="00AE" mode="text" /> <!-- AE REGISTERED SIGN --> + <preserve-symbol char="017B" mode="text" /> <!-- AF LATIN CAPITAL LETTER Z WITH DOT ABOVE --> + <preserve-symbol char="00B0" mode="text" /> <!-- B0 DEGREE SIGN --> + <preserve-symbol char="00B1" mode="math" /> <!-- B1 PLUS-MINUS SIGN --> + <preserve-symbol char="02DB" mode="text" /> <!-- B2 OGONEK --> + <preserve-symbol char="0142" mode="text" /> <!-- B3 LATIN SMALL LETTER L WITH STROKE --> + <preserve-symbol char="00B4" mode="text" /> <!-- B4 ACUTE ACCENT --> + <preserve-symbol char="00B5" mode="math" /> <!-- B5 MICRO SIGN --> + <preserve-symbol char="00B6" mode="text" /> <!-- B6 PILCROW SIGN --> + <preserve-symbol char="00B7" mode="text" /> <!-- B7 MIDDLE DOT --> + <preserve-symbol char="00B8" mode="text" /> <!-- B8 CEDILLA --> + <preserve-symbol char="0105" mode="text" /> <!-- B9 LATIN SMALL LETTER A WITH OGONEK --> + <preserve-symbol char="015F" mode="text" /> <!-- BA LATIN SMALL LETTER S WITH CEDILLA --> + <preserve-symbol char="00BB" mode="text" /> <!-- BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <preserve-symbol char="013D" mode="text" /> <!-- BC LATIN CAPITAL LETTER L WITH CARON --> + <preserve-symbol char="02DD" mode="text" /> <!-- BD DOUBLE ACUTE ACCENT --> + <preserve-symbol char="013E" mode="text" /> <!-- BE LATIN SMALL LETTER L WITH CARON --> + <preserve-symbol char="017C" mode="text" /> <!-- BF LATIN SMALL LETTER Z WITH DOT ABOVE --> + <preserve-symbol char="0154" mode="text" /> <!-- C0 LATIN CAPITAL LETTER R WITH ACUTE --> + <preserve-symbol char="00C1" mode="text" /> <!-- C1 LATIN CAPITAL LETTER A WITH ACUTE --> + <preserve-symbol char="00C2" mode="text" /> <!-- C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX --> + <preserve-symbol char="0102" mode="text" /> <!-- C3 LATIN CAPITAL LETTER A WITH BREVE --> + <preserve-symbol char="00C4" mode="text" /> <!-- C4 LATIN CAPITAL LETTER A WITH DIAERESIS --> + <preserve-symbol char="0139" mode="text" /> <!-- C5 LATIN CAPITAL LETTER L WITH ACUTE --> + <preserve-symbol char="0106" mode="text" /> <!-- C6 LATIN CAPITAL LETTER C WITH ACUTE --> + <preserve-symbol char="00C7" mode="text" /> <!-- C7 LATIN CAPITAL LETTER C WITH CEDILLA --> + <preserve-symbol char="010C" mode="text" /> <!-- C8 LATIN CAPITAL LETTER C WITH CARON --> + <preserve-symbol char="00C9" mode="text" /> <!-- C9 LATIN CAPITAL LETTER E WITH ACUTE --> + <preserve-symbol char="0118" mode="text" /> <!-- CA LATIN CAPITAL LETTER E WITH OGONEK --> + <preserve-symbol char="00CB" mode="text" /> <!-- CB LATIN CAPITAL LETTER E WITH DIAERESIS --> + <preserve-symbol char="011A" mode="text" /> <!-- CC LATIN CAPITAL LETTER E WITH CARON --> + <preserve-symbol char="00CD" mode="text" /> <!-- CD LATIN CAPITAL LETTER I WITH ACUTE --> + <preserve-symbol char="00CE" mode="text" /> <!-- CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX --> + <preserve-symbol char="010E" mode="text" /> <!-- CF LATIN CAPITAL LETTER D WITH CARON --> + <preserve-symbol char="0110" mode="text" /> <!-- D0 LATIN CAPITAL LETTER D WITH STROKE --> + <preserve-symbol char="0143" mode="text" /> <!-- D1 LATIN CAPITAL LETTER N WITH ACUTE --> + <preserve-symbol char="0147" mode="text" /> <!-- D2 LATIN CAPITAL LETTER N WITH CARON --> + <preserve-symbol char="00D3" mode="text" /> <!-- D3 LATIN CAPITAL LETTER O WITH ACUTE --> + <preserve-symbol char="00D4" mode="text" /> <!-- D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX --> + <preserve-symbol char="0150" mode="text" /> <!-- D5 LATIN CAPITAL LETTER O WITH DOUBLE ACUTE --> + <preserve-symbol char="00D6" mode="text" /> <!-- D6 LATIN CAPITAL LETTER O WITH DIAERESIS --> + <preserve-symbol char="00D7" mode="math" /> <!-- D7 MULTIPLICATION SIGN --> + <preserve-symbol char="0158" mode="text" /> <!-- D8 LATIN CAPITAL LETTER R WITH CARON --> + <preserve-symbol char="016E" mode="text" /> <!-- D9 LATIN CAPITAL LETTER U WITH RING ABOVE --> + <preserve-symbol char="00DA" mode="text" /> <!-- DA LATIN CAPITAL LETTER U WITH ACUTE --> + <preserve-symbol char="0170" mode="text" /> <!-- DB LATIN CAPITAL LETTER U WITH DOUBLE ACUTE --> + <preserve-symbol char="00DC" mode="text" /> <!-- DC LATIN CAPITAL LETTER U WITH DIAERESIS --> + <preserve-symbol char="00DD" mode="text" /> <!-- DD LATIN CAPITAL LETTER Y WITH ACUTE --> + <preserve-symbol char="0162" mode="text" /> <!-- DE LATIN CAPITAL LETTER T WITH CEDILLA --> + <preserve-symbol char="00DF" mode="text" /> <!-- DF LATIN SMALL LETTER SHARP S --> + <preserve-symbol char="0155" mode="text" /> <!-- E0 LATIN SMALL LETTER R WITH ACUTE --> + <preserve-symbol char="00E1" mode="text" /> <!-- E1 LATIN SMALL LETTER A WITH ACUTE --> + <preserve-symbol char="00E2" mode="text" /> <!-- E2 LATIN SMALL LETTER A WITH CIRCUMFLEX --> + <preserve-symbol char="0103" mode="text" /> <!-- E3 LATIN SMALL LETTER A WITH BREVE --> + <preserve-symbol char="00E4" mode="text" /> <!-- E4 LATIN SMALL LETTER A WITH DIAERESIS --> + <preserve-symbol char="013A" mode="text" /> <!-- E5 LATIN SMALL LETTER L WITH ACUTE --> + <preserve-symbol char="0107" mode="text" /> <!-- E6 LATIN SMALL LETTER C WITH ACUTE --> + <preserve-symbol char="00E7" mode="text" /> <!-- E7 LATIN SMALL LETTER C WITH CEDILLA --> + <preserve-symbol char="010D" mode="text" /> <!-- E8 LATIN SMALL LETTER C WITH CARON --> + <preserve-symbol char="00E9" mode="text" /> <!-- E9 LATIN SMALL LETTER E WITH ACUTE --> + <preserve-symbol char="0119" mode="text" /> <!-- EA LATIN SMALL LETTER E WITH OGONEK --> + <preserve-symbol char="00EB" mode="text" /> <!-- EB LATIN SMALL LETTER E WITH DIAERESIS --> + <preserve-symbol char="011B" mode="text" /> <!-- EC LATIN SMALL LETTER E WITH CARON --> + <preserve-symbol char="00ED" mode="text" /> <!-- ED LATIN SMALL LETTER I WITH ACUTE --> + <preserve-symbol char="00EE" mode="text" /> <!-- EE LATIN SMALL LETTER I WITH CIRCUMFLEX --> + <preserve-symbol char="010F" mode="text" /> <!-- EF LATIN SMALL LETTER D WITH CARON --> + <preserve-symbol char="0111" mode="text" /> <!-- F0 LATIN SMALL LETTER D WITH STROKE --> + <preserve-symbol char="0144" mode="text" /> <!-- F1 LATIN SMALL LETTER N WITH ACUTE --> + <preserve-symbol char="0148" mode="text" /> <!-- F2 LATIN SMALL LETTER N WITH CARON --> + <preserve-symbol char="00F3" mode="text" /> <!-- F3 LATIN SMALL LETTER O WITH ACUTE --> + <preserve-symbol char="00F4" mode="text" /> <!-- F4 LATIN SMALL LETTER O WITH CIRCUMFLEX --> + <preserve-symbol char="0151" mode="text" /> <!-- F5 LATIN SMALL LETTER O WITH DOUBLE ACUTE --> + <preserve-symbol char="00F6" mode="text" /> <!-- F6 LATIN SMALL LETTER O WITH DIAERESIS --> + <preserve-symbol char="00F7" mode="math" /> <!-- F7 DIVISION SIGN --> + <preserve-symbol char="0159" mode="text" /> <!-- F8 LATIN SMALL LETTER R WITH CARON --> + <preserve-symbol char="016F" mode="text" /> <!-- F9 LATIN SMALL LETTER U WITH RING ABOVE --> + <preserve-symbol char="00FA" mode="text" /> <!-- FA LATIN SMALL LETTER U WITH ACUTE --> + <preserve-symbol char="0171" mode="text" /> <!-- FB LATIN SMALL LETTER U WITH DOUBLE ACUTE --> + <preserve-symbol char="00FC" mode="text" /> <!-- FC LATIN SMALL LETTER U WITH DIAERESIS --> + <preserve-symbol char="00FD" mode="text" /> <!-- FD LATIN SMALL LETTER Y WITH ACUTE --> + <preserve-symbol char="0163" mode="text" /> <!-- FE LATIN SMALL LETTER T WITH CEDILLA --> + <preserve-symbol char="02D9" mode="text" /> <!-- FF DOT ABOVE --> +</symbol-set> + +<!-- cp1251 encoding (Microsoft Windows Cyrillic) --> +<symbol-set name="cp1251" fontenc="T2A"> +<!-- ordered by position in cp1251 rather than by unicode block --> +<!-- Based on table from ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/ --> +<preserve-symbol char="0402" mode="text" /> <!-- 80 CYRILLIC CAPITAL LETTER DJE --> +<preserve-symbol char="0403" mode="text" /> <!-- 81 CYRILLIC CAPITAL LETTER GJE --> +<symbol-subset fontenc="any"> + <preserve-symbol char="201A" mode="text" /> <!-- 82 SINGLE LOW-9 QUOTATION MARK --> +</symbol-subset> +<preserve-symbol char="0453" mode="text" /> <!-- 83 CYRILLIC SMALL LETTER GJE --> +<symbol-subset fontenc="any"> + <preserve-symbol char="201E" mode="text" /> <!-- 84 DOUBLE LOW-9 QUOTATION MARK --> + <preserve-symbol char="2026" mode="text" /> <!-- 85 HORIZONTAL ELLIPSIS --> + <preserve-symbol char="2020" mode="text" /> <!-- 86 DAGGER --> + <preserve-symbol char="2021" mode="text" /> <!-- 87 DOUBLE DAGGER --> + <preserve-symbol char="20AC" mode="text" /> <!-- 88 EURO SIGN --> + <preserve-symbol char="2030" mode="text" /> <!-- 89 PER MILLE SIGN --> +</symbol-subset> +<preserve-symbol char="0409" mode="text" /> <!-- 8A CYRILLIC CAPITAL LETTER LJE --> +<symbol-subset fontenc="any"> + <preserve-symbol char="2039" mode="text" /> <!-- 8B SINGLE LEFT-POINTING ANGLE QUOTATION MARK --> +</symbol-subset> +<preserve-symbol char="040A" mode="text" /> <!-- 8C CYRILLIC CAPITAL LETTER NJE --> +<preserve-symbol char="040C" mode="text" /> <!-- 8D CYRILLIC CAPITAL LETTER KJE --> +<preserve-symbol char="040B" mode="text" /> <!-- 8E CYRILLIC CAPITAL LETTER TSHE --> +<preserve-symbol char="040F" mode="text" /> <!-- 8F CYRILLIC CAPITAL LETTER DZHE --> +<preserve-symbol char="0452" mode="text" /> <!-- 90 CYRILLIC SMALL LETTER DJE --> +<symbol-subset fontenc="any"> + <preserve-symbol char="2018" mode="text" /> <!-- 91 LEFT SINGLE QUOTATION MARK --> + <preserve-symbol char="2019" mode="text" /> <!-- 92 RIGHT SINGLE QUOTATION MARK --> + <preserve-symbol char="201C" mode="text" /> <!-- 93 LEFT DOUBLE QUOTATION MARK --> + <preserve-symbol char="201D" mode="text" /> <!-- 94 RIGHT DOUBLE QUOTATION MARK --> + <preserve-symbol char="2022" mode="text" /> <!-- 95 BULLET --> + <preserve-symbol char="2013" mode="text" /> <!-- 96 EN DASH --> + <preserve-symbol char="2014" mode="text" /> <!-- 97 EM DASH --> + <preserve-symbol char="2122" mode="text" /> <!-- 99 TRADE MARK SIGN --> +</symbol-subset> +<preserve-symbol char="0459" mode="text" /> <!-- 9A CYRILLIC SMALL LETTER LJE --> +<preserve-symbol char="203A" mode="text" /> <!-- 9B SINGLE RIGHT-POINTING ANGLE QUOTATION MARK --> +<preserve-symbol char="045A" mode="text" /> <!-- 9C CYRILLIC SMALL LETTER NJE --> +<preserve-symbol char="045C" mode="text" /> <!-- 9D CYRILLIC SMALL LETTER KJE --> +<preserve-symbol char="045B" mode="text" /> <!-- 9E CYRILLIC SMALL LETTER TSHE --> +<preserve-symbol char="045F" mode="text" /> <!-- 9F CYRILLIC SMALL LETTER DZHE --> +<preserve-symbol char="040E" mode="text" /> <!-- A1 CYRILLIC CAPITAL LETTER SHORT U --> +<preserve-symbol char="045E" mode="text" /> <!-- A2 CYRILLIC SMALL LETTER SHORT U --> +<preserve-symbol char="0408" mode="text" /> <!-- A3 CYRILLIC CAPITAL LETTER JE --> +<symbol-subset fontenc="any"> + <preserve-symbol char="00A4" mode="text" /> <!-- A4 CURRENCY SIGN --> +</symbol-subset> +<preserve-symbol char="0490" mode="text" /> <!-- A5 CYRILLIC CAPITAL LETTER GHE WITH UPTURN --> +<symbol-subset fontenc="any"> + <preserve-symbol char="00A6" mode="text" /> <!-- A6 BROKEN BAR --> + <preserve-symbol char="00A7" mode="text" /> <!-- A7 SECTION SIGN --> +</symbol-subset> +<preserve-symbol char="0401" mode="text" /> <!-- A8 CYRILLIC CAPITAL LETTER IO --> +<symbol-subset fontenc="any"> + <preserve-symbol char="00A9" mode="text" /> <!-- A9 COPYRIGHT SIGN --> +</symbol-subset> +<preserve-symbol char="0404" mode="text" /> <!-- AA CYRILLIC CAPITAL LETTER UKRAINIAN IE --> +<symbol-subset fontenc="any"> + <preserve-symbol char="00AB" mode="text" /> <!-- AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK --> + <preserve-symbol char="00AC" mode="math" /> <!-- AC NOT SIGN --> + <preserve-symbol char="00AD" mode="text" /> <!-- AD SOFT HYPHEN --> + <preserve-symbol char="00AE" mode="text" /> <!-- AE REGISTERED SIGN --> +</symbol-subset> +<preserve-symbol char="0407" mode="text" /> <!-- AF CYRILLIC CAPITAL LETTER YI --> +<symbol-subset fontenc="any"> + <preserve-symbol char="00B0" mode="text" /> <!-- B0 DEGREE SIGN --> + <preserve-symbol char="00B1" mode="math" /> <!-- B1 PLUS-MINUS SIGN --> +</symbol-subset> +<preserve-symbol char="0406" mode="text" /> <!-- B2 CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I --> +<preserve-symbol char="0456" mode="text" /> <!-- B3 CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I --> +<preserve-symbol char="0491" mode="text" /> <!-- B4 CYRILLIC SMALL LETTER GHE WITH UPTURN --> +<symbol-subset fontenc="any"> + <preserve-symbol char="00B5" mode="math" /> <!-- B5 MICRO SIGN --> + <preserve-symbol char="00B6" mode="text" /> <!-- B6 PILCROW SIGN --> + <preserve-symbol char="00B7" mode="text" /> <!-- B7 MIDDLE DOT --> +</symbol-subset> +<preserve-symbol char="0451" mode="text" /> <!-- B8 CYRILLIC SMALL LETTER IO --> +<symbol-subset fontenc="any"> + <preserve-symbol char="2116" mode="text" /> <!-- B9 NUMERO SIGN --> +</symbol-subset> +<preserve-symbol char="0454" mode="text" /> <!-- BA CYRILLIC SMALL LETTER UKRAINIAN IE --> +<preserve-symbol char="00BB" mode="text" /> <!-- BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK --> +<preserve-symbol char="0458" mode="text" /> <!-- BC CYRILLIC SMALL LETTER JE --> +<preserve-symbol char="0405" mode="text" /> <!-- BD CYRILLIC CAPITAL LETTER DZE --> +<preserve-symbol char="0455" mode="text" /> <!-- BE CYRILLIC SMALL LETTER DZE --> +<preserve-symbol char="0457" mode="text" /> <!-- BF CYRILLIC SMALL LETTER YI --> +<preserve-symbol char="0410" mode="text" /> <!-- C0 CYRILLIC CAPITAL LETTER A --> +<preserve-symbol char="0411" mode="text" /> <!-- C1 CYRILLIC CAPITAL LETTER BE --> +<preserve-symbol char="0412" mode="text" /> <!-- C2 CYRILLIC CAPITAL LETTER VE --> +<preserve-symbol char="0413" mode="text" /> <!-- C3 CYRILLIC CAPITAL LETTER GHE --> +<preserve-symbol char="0414" mode="text" /> <!-- C4 CYRILLIC CAPITAL LETTER DE --> +<preserve-symbol char="0415" mode="text" /> <!-- C5 CYRILLIC CAPITAL LETTER IE --> +<preserve-symbol char="0416" mode="text" /> <!-- C6 CYRILLIC CAPITAL LETTER ZHE --> +<preserve-symbol char="0417" mode="text" /> <!-- C7 CYRILLIC CAPITAL LETTER ZE --> +<preserve-symbol char="0418" mode="text" /> <!-- C8 CYRILLIC CAPITAL LETTER I --> +<preserve-symbol char="0419" mode="text" /> <!-- C9 CYRILLIC CAPITAL LETTER SHORT I --> +<preserve-symbol char="041A" mode="text" /> <!-- CA CYRILLIC CAPITAL LETTER KA --> +<preserve-symbol char="041B" mode="text" /> <!-- CB CYRILLIC CAPITAL LETTER EL --> +<preserve-symbol char="041C" mode="text" /> <!-- CC CYRILLIC CAPITAL LETTER EM --> +<preserve-symbol char="041D" mode="text" /> <!-- CD CYRILLIC CAPITAL LETTER EN --> +<preserve-symbol char="041E" mode="text" /> <!-- CE CYRILLIC CAPITAL LETTER O --> +<preserve-symbol char="041F" mode="text" /> <!-- CF CYRILLIC CAPITAL LETTER PE --> +<preserve-symbol char="0420" mode="text" /> <!-- D0 CYRILLIC CAPITAL LETTER ER --> +<preserve-symbol char="0421" mode="text" /> <!-- D1 CYRILLIC CAPITAL LETTER ES --> +<preserve-symbol char="0422" mode="text" /> <!-- D2 CYRILLIC CAPITAL LETTER TE --> +<preserve-symbol char="0423" mode="text" /> <!-- D3 CYRILLIC CAPITAL LETTER U --> +<preserve-symbol char="0424" mode="text" /> <!-- D4 CYRILLIC CAPITAL LETTER EF --> +<preserve-symbol char="0425" mode="text" /> <!-- D5 CYRILLIC CAPITAL LETTER HA --> +<preserve-symbol char="0426" mode="text" /> <!-- D6 CYRILLIC CAPITAL LETTER TSE --> +<preserve-symbol char="0427" mode="text" /> <!-- D7 CYRILLIC CAPITAL LETTER CHE --> +<preserve-symbol char="0428" mode="text" /> <!-- D8 CYRILLIC CAPITAL LETTER SHA --> +<preserve-symbol char="0429" mode="text" /> <!-- D9 CYRILLIC CAPITAL LETTER SHCHA --> +<preserve-symbol char="042A" mode="text" /> <!-- DA CYRILLIC CAPITAL LETTER HARD SIGN --> +<preserve-symbol char="042B" mode="text" /> <!-- DB CYRILLIC CAPITAL LETTER YERU --> +<preserve-symbol char="042C" mode="text" /> <!-- DC CYRILLIC CAPITAL LETTER SOFT SIGN --> +<preserve-symbol char="042D" mode="text" /> <!-- DD CYRILLIC CAPITAL LETTER E --> +<preserve-symbol char="042E" mode="text" /> <!-- DE CYRILLIC CAPITAL LETTER YU --> +<preserve-symbol char="042F" mode="text" /> <!-- DF CYRILLIC CAPITAL LETTER YA --> +<preserve-symbol char="0430" mode="text" /> <!-- E0 CYRILLIC SMALL LETTER A --> +<preserve-symbol char="0431" mode="text" /> <!-- E1 CYRILLIC SMALL LETTER BE --> +<preserve-symbol char="0432" mode="text" /> <!-- E2 CYRILLIC SMALL LETTER VE --> +<preserve-symbol char="0433" mode="text" /> <!-- E3 CYRILLIC SMALL LETTER GHE --> +<preserve-symbol char="0434" mode="text" /> <!-- E4 CYRILLIC SMALL LETTER DE --> +<preserve-symbol char="0435" mode="text" /> <!-- E5 CYRILLIC SMALL LETTER IE --> +<preserve-symbol char="0436" mode="text" /> <!-- E6 CYRILLIC SMALL LETTER ZHE --> +<preserve-symbol char="0437" mode="text" /> <!-- E7 CYRILLIC SMALL LETTER ZE --> +<preserve-symbol char="0438" mode="text" /> <!-- E8 CYRILLIC SMALL LETTER I --> +<preserve-symbol char="0439" mode="text" /> <!-- E9 CYRILLIC SMALL LETTER SHORT I --> +<preserve-symbol char="043A" mode="text" /> <!-- EA CYRILLIC SMALL LETTER KA --> +<preserve-symbol char="043B" mode="text" /> <!-- EB CYRILLIC SMALL LETTER EL --> +<preserve-symbol char="043C" mode="text" /> <!-- EC CYRILLIC SMALL LETTER EM --> +<preserve-symbol char="043D" mode="text" /> <!-- ED CYRILLIC SMALL LETTER EN --> +<preserve-symbol char="043E" mode="text" /> <!-- EE CYRILLIC SMALL LETTER O --> +<preserve-symbol char="043F" mode="text" /> <!-- EF CYRILLIC SMALL LETTER PE --> +<preserve-symbol char="0440" mode="text" /> <!-- F0 CYRILLIC SMALL LETTER ER --> +<preserve-symbol char="0441" mode="text" /> <!-- F1 CYRILLIC SMALL LETTER ES --> +<preserve-symbol char="0442" mode="text" /> <!-- F2 CYRILLIC SMALL LETTER TE --> +<preserve-symbol char="0443" mode="text" /> <!-- F3 CYRILLIC SMALL LETTER U --> +<preserve-symbol char="0444" mode="text" /> <!-- F4 CYRILLIC SMALL LETTER EF --> +<preserve-symbol char="0445" mode="text" /> <!-- F5 CYRILLIC SMALL LETTER HA --> +<preserve-symbol char="0446" mode="text" /> <!-- F6 CYRILLIC SMALL LETTER TSE --> +<preserve-symbol char="0447" mode="text" /> <!-- F7 CYRILLIC SMALL LETTER CHE --> +<preserve-symbol char="0448" mode="text" /> <!-- F8 CYRILLIC SMALL LETTER SHA --> +<preserve-symbol char="0449" mode="text" /> <!-- F9 CYRILLIC SMALL LETTER SHCHA --> +<preserve-symbol char="044A" mode="text" /> <!-- FA CYRILLIC SMALL LETTER HARD SIGN --> +<preserve-symbol char="044B" mode="text" /> <!-- FB CYRILLIC SMALL LETTER YERU --> +<preserve-symbol char="044C" mode="text" /> <!-- FC CYRILLIC SMALL LETTER SOFT SIGN --> +<preserve-symbol char="044D" mode="text" /> <!-- FD CYRILLIC SMALL LETTER E --> +<preserve-symbol char="044E" mode="text" /> <!-- FE CYRILLIC SMALL LETTER YU --> +<preserve-symbol char="044F" mode="text" /> <!-- FF CYRILLIC SMALL LETTER YA --> +</symbol-set> + +<!-- koi8-r encoding (latin/russian) --> +<symbol-set name="koi8-r" fontenc="T2A"> +<!-- Unicode block: Latin-1 supplement --> +<symbol-subset fontenc="any"> +<preserve-symbol char="00A9" mode="text" /> <!-- copyright --> +<preserve-symbol char="00B0" mode="text" /> <!-- degree --> +<preserve-symbol char="00B2" mode="math" /> <!-- two superior --> +<preserve-symbol char="00B7" mode="text" /> <!-- period centered --> +<preserve-symbol char="00F7" mode="math" /> <!-- div --> +</symbol-subset> +<!-- Unicode block: Cyrillic --> +<symbol char="0400" text="\`Е" /> +<preserve-symbol char="0401" mode="text" /> +<symbol char="0403" text="\'Г" /> +<symbol char="040C" text="\'К" /> +<symbol char="040D" text="\`И" /> +<preserve-symbols first-char="0410" last-char="044F" mode="text" /> +<symbol char="0450" text="\`е" /> +<preserve-symbol char="0451" mode="text" /> +<symbol char="0453" text="\'г" /> +<symbol char="045C" text="\'к" /> +<symbol char="045D" text="\`и" /> +<!-- Unicode block: Mathematical Operators --> +<preserve-symbol char="2219" mode="math" /> <!-- bullet --> +<preserve-symbol char="221A" mode="math" /> <!-- surd --> +<preserve-symbol char="223C" mode="math" /> <!-- sim --> +<preserve-symbol char="2264" mode="math" /> <!-- leq --> +<preserve-symbol char="2265" mode="math" /> <!-- geq --> +</symbol-set> + +<!-- Iso-8859-7 encoding (latin/greek) --> +<symbol-set name="iso-8859-7" fontenc="LGR"> +<!-- Missing Characters. Will check and see... +0374, 0375, 037a, 0384, 0385, 0387 +03D0 - 03F6 +--> +<preserve-symbol char="037E" mode="text" /> +<preserve-symbol char="0386" mode="text" /> +<preserve-symbols first-char="0388" last-char="038A" mode="text" /> +<preserve-symbol char="038C" mode="text" /> +<preserve-symbols first-char="038E" last-char="03A1" mode="text" /> +<preserve-symbols first-char="03A3" last-char="03CE" mode="text" /> +</symbol-set> <!-- end of iso-8859-7 --> + +<!-- utf8 input encoding --> +<symbol-set name="utf8" fontenc="any"> +<!-- preserve symbols defined in utf8enc.dfu --> +<preserve-symbols first-char="00A1" last-char="00AC" mode="text" /> +<preserve-symbols first-char="00AE" last-char="00FF" mode="text" /> +<preserve-symbols first-char="0102" last-char="0107" mode="text" /> +<preserve-symbols first-char="010C" last-char="0111" mode="text" /> +<preserve-symbols first-char="0118" last-char="011B" mode="text" /> +<preserve-symbols first-char="011E" last-char="011F" mode="text" /> +<preserve-symbols first-char="0130" last-char="0133" mode="text" /> +<preserve-symbols first-char="0139" last-char="013A" mode="text" /> +<preserve-symbols first-char="013D" last-char="013E" mode="text" /> +<preserve-symbols first-char="0141" last-char="0144" mode="text" /> +<preserve-symbols first-char="0147" last-char="0148" mode="text" /> +<preserve-symbols first-char="014A" last-char="014B" mode="text" /> +<preserve-symbols first-char="0150" last-char="0155" mode="text" /> +<preserve-symbols first-char="0158" last-char="015B" mode="text" /> +<preserve-symbols first-char="015E" last-char="0165" mode="text" /> +<preserve-symbols first-char="016E" last-char="0171" mode="text" /> +<preserve-symbols first-char="0178" last-char="017E" mode="text" /> +<preserve-symbol char="0192" mode="text" /> +<preserve-symbols first-char="02C6" last-char="02C7" mode="text" /> +<preserve-symbol char="02D8" mode="text" /> +<preserve-symbols first-char="02DC" last-char="02DD" mode="text" /> +<symbol-subset fontenc="T2A"> +<preserve-symbols first-char="0401" last-char="0402" mode="text" /> +<preserve-symbols first-char="0404" last-char="040B" mode="text" /> +<preserve-symbols first-char="040E" last-char="0452" mode="text" /> +<preserve-symbols first-char="0454" last-char="045B" mode="text" /> +<preserve-symbols first-char="045E" last-char="045F" mode="text" /> +<preserve-symbols first-char="0462" last-char="0463" mode="text" /> +<preserve-symbols first-char="046A" last-char="046B" mode="text" /> +<preserve-symbols first-char="0472" last-char="0475" mode="text" /> +<preserve-symbols first-char="048C" last-char="04C0" mode="text" /> +<preserve-symbols first-char="04C3" last-char="04C8" mode="text" /> +<preserve-symbols first-char="04CB" last-char="04CE" mode="text" /> +<preserve-symbols first-char="04D4" last-char="04D5" mode="text" /> +<preserve-symbols first-char="04D8" last-char="04D9" mode="text" /> +<preserve-symbols first-char="04E0" last-char="04E1" mode="text" /> +<preserve-symbols first-char="04E8" last-char="04E9" mode="text" /> +</symbol-subset> +<preserve-symbol char="0E3F" mode="text" /> +<preserve-symbol char="200C" mode="text" /> +<preserve-symbols first-char="2013" last-char="2014" mode="text" /> +<preserve-symbol char="2016" mode="text" /> +<preserve-symbols first-char="2018" last-char="201A" mode="text" /> +<preserve-symbols first-char="201C" last-char="201E" mode="text" /> +<preserve-symbols first-char="2020" last-char="2022" mode="text" /> +<preserve-symbol char="2026" mode="text" /> +<preserve-symbols first-char="2030" last-char="2031" mode="text" /> +<preserve-symbols first-char="2039" last-char="203B" mode="text" /> +<preserve-symbol char="203D" mode="text" /> +<preserve-symbol char="2044" mode="text" /> +<preserve-symbol char="204E" mode="text" /> +<preserve-symbol char="2052" mode="text" /> +<preserve-symbol char="20A1" mode="text" /> +<preserve-symbol char="20A4" mode="text" /> +<preserve-symbol char="20A6" mode="text" /> +<preserve-symbol char="20A9" mode="text" /> +<preserve-symbols first-char="20AB" last-char="20AC" mode="text" /> +<preserve-symbol char="20B1" mode="text" /> +<preserve-symbol char="2103" mode="text" /> +<preserve-symbols first-char="2116" last-char="2117" mode="text" /> +<preserve-symbol char="211E" mode="text" /> +<preserve-symbol char="2120" mode="text" /> +<preserve-symbol char="2122" mode="text" /> +<preserve-symbols first-char="2126" last-char="2127" mode="text" /> +<preserve-symbol char="212E" mode="text" /> +<preserve-symbols first-char="2190" last-char="2193" mode="text" /> +<preserve-symbols first-char="2329" last-char="232A" mode="text" /> +<preserve-symbols first-char="2422" last-char="2423" mode="text" /> +<preserve-symbol char="25E6" mode="text" /> +<preserve-symbol char="25EF" mode="text" /> +<preserve-symbol char="266A" mode="text" /> +</symbol-set> +<!-- end of utf8 input encoding --> + +<!-- +************************************************** +PART III: Additional symbols +These tables contain symbols from various packages +************************************************** +--> + +<!-- support for Henrik Theiling's eurosym.sty (euro symbol with design + following the official European Commision's definitions) --> +<symbol-set name="eurosym" fontenc="any"> + <symbol char="20AC" text="{\euro}" /> <!-- same as \officialeuro --> +</symbol-set> + +<!-- support for Ingo Kl�ckl's ifsym.sty (various symbols in metafont format) --> +<symbol-set name="ifsym" fontenc="any"> +<!-- Unicode block: Geometric Shapes --> +<symbol char="25A0" text="{\FilledBigSquare}" /> +<symbol char="25A1" text="{\BigSquare}" /> +<symbol char="25A3" text="\rlap{\FilledSmallSquare}{\BigSquare}" /> +<symbol char="25AA" text="{\FilledSmallSquare}" /> +<symbol char="25AB" text="{\SmallSquare}" /> +<symbol char="25AC" text="\textwide{\FilledBigSquare}" /> +<symbol char="25AD" text="\textwide{\BigSquare}" /> +<symbol char="25AE" text="\textnarrow{\FilledBigSquare}" /> +<symbol char="25AF" text="\textnarrow{\BigSquare}" /> +<symbol char="25B2" text="{\FilledBigTriangleUp}" /> +<symbol char="25B3" text="{\BigTriangleUp}" /> +<symbol char="25B4" text="{\FilledSmallTriangleUp}" /> +<symbol char="25B5" text="{\SmallTriangleUp}" /> +<symbol char="25B6" text="{\FilledBigTriangleRight}" /> +<symbol char="25B7" text="{\BigTriangleRight}" /> +<symbol char="25B8" text="{\FilledSmallTriangleRight}" /> +<symbol char="25B9" text="{\SmallTriangleRight}" /> +<symbol char="25BA" text="\textwide{\FilledBigTriangleRight}" /> +<symbol char="25BB" text="\textwide{\BigTriangleRight}" /> +<symbol char="25BC" text="{\FilledBigTriangleDown}" /> +<symbol char="25BD" text="{\BigTriangleDown}" /> +<symbol char="25BE" text="{\FilledSmallTriangleDown}" /> +<symbol char="25BF" text="{\SmallTriangleDown}" /> +<symbol char="25C0" text="{\FilledBigTriangleLeft}" /> +<symbol char="25C1" text="{\BigTriangleLeft}" /> +<symbol char="25C2" text="{\FilledSmallTriangleLeft}" /> +<symbol char="25C3" text="{\SmallTriangleLeft}" /> +<symbol char="25C4" text="\textwide{\FilledBigTriangleLeft}" /> +<symbol char="25C5" text="\textwide{\BigTriangleLeft}" /> +<symbol char="25C6" text="{\FilledBigDiamondshape}" /> +<symbol char="25C7" text="{\BigDiamondshape}" /> +<symbol char="25C8" text="\rlap{\FilledSmallDiamondshape}{\BigDiamondshape}" /> +<symbol char="25C9" text="\rlap{\FilledSmallCircle}{\BigCircle}" /> +<symbol char="25CA" text="\textnarrow{\BigDiamondshape}" /> +<symbol char="25CB" text="{\BigCircle}" /> +<symbol char="25CE" text="\rlap{\SmallCircle}{\BigCircle}" /> +<symbol char="25CF" text="{\FilledBigCircle}" /> +<symbol char="25E6" text="{\SmallCircle}" /> +<symbol char="25EB" text="\rlap{\BigVBar}{\BigSquare}" /> +<symbol char="25EF" text="{\BigCircle}" /> +<!-- Unicode block: Miscellaneous Symbols --> +<symbol char="2601" text="{\FilledCloud}" /> +<symbol char="260E" text="{\Telephone}" /> +<symbol char="2622" text="{\Radiation}" /> +</symbol-set><!-- end of ifsym --> + +<!-- support for wasysym (various symbols in metafont format) --> +<symbol-set name="wasysym" fontenc="any"> +<!-- Unicode block: Miscellaneous Technical --> +<symbol char="2300" text="{\diameter}" /> +<!-- Unicode block: Geometric Shapes --> +<symbol char="25D0" text="{\LEFTcircle}" /> +<symbol char="25D1" text="{\RIGHTcircle}" /> +<symbol char="25D6" text="{\LEFTCIRCLE}" /> +<symbol char="25D7" text="{\RIGHTCIRCLE}" /> +<!-- Unicode block: Miscellaneous Symbols --> +<symbol char="2607" text="{\lightning}" /> +<symbol char="2609" text="{\astrosun}" /> +<symbol char="260A" text="{\ascnode}" /> +<symbol char="260B" text="{\descnode}" /> +<symbol char="260C" text="{\conjunction}" /> +<symbol char="260D" text="{\opposition}" /> +<symbol char="2610" text="{\Square}" /> +<symbol char="2611" text="{\CheckedBox}" /> +<symbol char="2612" text="{\XBox}" /> +<symbol char="2639" text="{\frownie}" /> +<symbol char="263A" text="{\smiley}" /> +<symbol char="263B" text="{\blacksmiley}" /> +<symbol char="263C" text="{\sun}" /> +<symbol char="263D" text="{\rightmoon}" /> +<symbol char="263E" text="{\leftmoon}" /> +<symbol char="263F" text="{\mercury}" /> +<symbol char="2640" text="{\venus}" /> +<symbol char="2641" text="{\earth}" /> +<symbol char="2642" text="{\mars}" /> +<symbol char="2643" text="{\jupiter}" /> +<symbol char="2644" text="{\saturn}" /> +<symbol char="2645" text="{\uranus}" /> +<symbol char="2646" text="{\neptune}" /> +<symbol char="2647" text="{\pluto}" /> +<symbol char="2648" text="{\aries}" /> +<symbol char="2649" text="{\taurus}" /> +<symbol char="264A" text="{\gemini}" /> +<symbol char="264B" text="{\cancer}" /> +<symbol char="264C" text="{\leo}" /> +<symbol char="264D" text="{\virgo}" /> +<symbol char="264E" text="{\libra}" /> +<symbol char="264F" text="{\scorpio}" /> +<symbol char="2650" text="{\sagittarius}" /> +<symbol char="2651" text="{\capricornus}" /> +<symbol char="2652" text="{\aquarius}" /> +<symbol char="2653" text="{\pisces}" /> +<symbol char="2669" text="{\quarternote}" /> +<symbol char="266B" text="{\twonotes}" /> +<!-- note: wasysym has some APL symbols as well --> +</symbol-set><!--end of wasysym--> + +<!-- support for bbding (metafont clone of parts of Zapf dingbats) + Note that three symbols are renamed because of name clashes --> +<symbol-set name="bbding" fontenc="any"> +<!-- Unicode block: Geometric Shapes --> +<symbol char="25A0" text="{\SquareSolid}" /> +<symbol char="25A1" text="{\bbSquare}" /> <!-- renamed! --> +<symbol char="25B2" text="{\bbTriangleUp}" /> <!-- renamed! --> +<symbol char="25BC" text="{\bbTriangleDown}" /> <!-- renamed! --> +<symbol char="25C6" text="{\DiamonSolid}" /> +<symbol char="25CF" text="{\CircleSolid}" /> +<symbol char="25D6" text="{\HalfCircleLeft}" /> +<symbol char="25D7" text="{\HalfCircleRight}" /> +<!-- Unicode block: Miscellaneous Symbols --> +<symbol char="2605" text="{\FiveStar}" /> +<symbol char="2606" text="{\FiveStarOpen}" /> +<symbol char="260E" text="{\Phone}" /> +<symbol char="261A" text="{\HandCuffLeft}" /> +<symbol char="261B" text="{\HandCuffRight}" /> +<symbol char="261C" text="{\HandLeft}" /> +<symbol char="261E" text="{\HandRight}" /> +<!-- Unicode block: Dingbats --> +<symbol char="2701" text="{\ScissorRightBrokenBottom}" /> +<symbol char="2702" text="{\ScissorRight}" /> +<symbol char="2703" text="{\ScissorRightBrokenTop}" /> +<symbol char="2704" text="{\ScissorHollowRight}" /> +<symbol char="2706" text="{\PhoneHandset}" /> +<symbol char="2707" text="{\Tape}" /> +<symbol char="2708" text="{\Plane}" /> +<symbol char="2709" text="{\Envelope}" /> +<symbol char="270C" text="{\Peace}" /> +<symbol char="270D" text="{\HandPencilLeft}" /> +<symbol char="270E" text="{\PencilRightDown}" /> +<symbol char="270F" text="{\PencilRight}" /> +<symbol char="2710" text="{\PencilRightUp}" /> +<symbol char="2711" text="{\NibRight}" /> +<symbol char="2712" text="{\NibSolidRight}" /> +<symbol char="2713" text="{\Checkmark}" /> +<symbol char="2714" text="{\CheckmarkBold}" /> +<symbol char="2715" text="{\XSolid}" /> +<symbol char="2716" text="{\XSolidBold}" /> +<symbol char="2717" text="{\XSolidBrush}" /> +<!--<symbol char="2718" text="{\??}" /> --> +<symbol char="2719" text="{\PlusOutline}" /> +<symbol char="271A" text="{\Plus}" /> +<symbol char="271B" text="{\PlusThinCenterOpen}" /> +<symbol char="271C" text="{\PlusCenterOpen}" /> +<symbol char="271D" text="{\bbCross}" /> <!-- renamed! --> +<symbol char="271E" text="{\CrossOpenShadow}" /> +<symbol char="271F" text="{\CrossOutline}" /> +<symbol char="2720" text="{\CrossMaltese}" /> +<symbol char="2721" text="{\DavidStar}" /> +<symbol char="2722" text="{\FourAsterisk}" /> +<symbol char="2723" text="{\JackStar}" /> +<symbol char="2724" text="{\JackStarBold}" /> +<symbol char="2725" text="{\CrossClowerTips}" /> +<symbol char="2726" text="{\FourStar}" /> +<symbol char="2727" text="{\FourStarOpen}" /> +<symbol char="2729" text="{\FiveStarOpen}" /> +<symbol char="272A" text="{\FiveStarOpenCircled}" /> +<symbol char="272B" text="{\FiveStarCenterOpen}" /> +<symbol char="272C" text="{\FiveStarOpenDotted}" /> +<symbol char="272D" text="{\FiveStarOutline}" /> +<symbol char="272E" text="{\FiveStarOutlineHeavy}" /> +<symbol char="272F" text="{\FiveStarConvex}" /> +<symbol char="2730" text="{\FiveStarShadow}" /> +<symbol char="2731" text="{\AsteriskBold}" /> +<symbol char="2732" text="{\AsteriskCenterOpen}" /> +<!-- <symbol char="2733" text="{\??}" /> --> +<symbol char="2734" text="{\EightStarTaper}" /> +<symbol char="2735" text="{\EightStarConvex}" /> +<symbol char="2736" text="{\SixStar}" /> +<symbol char="2737" text="{\EightStar}" /> +<symbol char="2738" text="{\EightStarBold}" /> +<symbol char="2739" text="{\TwelweStar}" /> +<symbol char="273A" text="{\SixteenStarLight}" /> +<symbol char="273B" text="{\SixFlowerPetalRemoved}" /> +<symbol char="273C" text="{\SixFlowerOpenCenter}" /> +<symbol char="273D" text="{\Asterisk}" /> +<symbol char="273E" text="{\SixFlowerAlternate}" /> +<symbol char="273F" text="{\FiveFlowerPetal}" /> +<symbol char="2740" text="{\FiveFlowerOpen}" /> +<symbol char="2741" text="{\EightFlowerPetal}" /> +<symbol char="2742" text="{\SunshineOpenCircled}" /> +<symbol char="2743" text="{\SixFlowerAltPetal}" /> +<symbol char="2744" text="{\SnowflakeChevron}" /> +<symbol char="2745" text="{\Snowflake}" /> +<symbol char="2746" text="{\SnowflakeChevronBold}" /> +<symbol char="2747" text="{\Sparkle}" /> +<symbol char="2748" text="{\SparkleBold}" /> +<symbol char="2749" text="{\AsteriskRoundedEnds}" /> +<symbol char="274A" text="{\EightFlowerPetalRemoved}" /> +<symbol char="274B" text="{\EightAsterisk}" /> +<symbol char="274D" text="{\CircleShadow}" /> +<symbol char="274F" text="{\SquareShadowBottomRight}" /> +<symbol char="2750" text="{\SquareShadowTopRight}" /> +<symbol char="2751" text="{\SquareCastShadowBottomRight}" /> +<symbol char="2752" text="{\SquareCastShadowTopRight}" /> +<symbol char="2756" text="{\OrnamentDiamondSolid}" /> +<symbol char="2758" text="{\RectangleThin}" /> +<symbol char="2759" text="{\Rectangle}" /> +<symbol char="275A" text="{\RectangleBold}" /> +<symbol char="27A0" text="{\ArrowBoldRightStrobe}" /> +<symbol char="27A5" text="{\ArrowBoldDownRight}" /> +<symbol char="27A6" text="{\ArrowBoldUpRight}" /> +<symbol char="27A7" text="{\ArrowBoldRightShort}" /> +<symbol char="27B2" text="{\ArrowBoldRightCircled}" /> +</symbol-set><!--end of bbding--> + +<!-- support for zapf dingbats (pifont.sty) --> +<symbol-set name="dingbats" fontenc="any"> +<!-- Note: Most dingbats are located in 2700-27FF, but some are + placed elsewhere because they fit in another category. +--> +<!-- Unicode block: Arrows --> +<symbol char="2192" text="\ding{213}" /> +<symbol char="2194" text="\ding{214}" /> +<symbol char="2195" text="\ding{215}" /> +<!-- Unicode block: Enclosed Alphanumericals --> +<symbol char="2460" text="\ding{172}" /> +<symbol char="2461" text="\ding{173}" /> +<symbol char="2462" text="\ding{174}" /> +<symbol char="2463" text="\ding{175}" /> +<symbol char="2464" text="\ding{176}" /> +<symbol char="2465" text="\ding{177}" /> +<symbol char="2466" text="\ding{178}" /> +<symbol char="2467" text="\ding{179}" /> +<symbol char="2468" text="\ding{180}" /> +<symbol char="2469" text="\ding{181}" /> +<!-- Unicode block: Geometric Shapes --> +<symbol char="25A0" text="\ding{110}" /> +<symbol char="25B2" text="\ding{115}" /> +<symbol char="25BC" text="\ding{116}" /> +<symbol char="25C6" text="\ding{117}" /> +<symbol char="25CF" text="\ding{108}" /> +<symbol char="25D7" text="\ding{119}" /> +<!-- Unicode block: Miscellaneous Symbols --> +<symbol char="2605" text="\ding{72}" /> +<symbol char="260E" text="\ding{37}" /> +<symbol char="261B" text="\ding{42}" /> +<symbol char="261E" text="\ding{43}" /> +<!-- Unicode block: Dingbats --> +<symbol char="2701" text="\ding{33}" /> +<symbol char="2702" text="\ding{34}" /> +<symbol char="2703" text="\ding{35}" /> +<symbol char="2704" text="\ding{36}" /> +<symbol char="2706" text="\ding{38}" /> +<symbol char="2707" text="\ding{39}" /> +<symbol char="2708" text="\ding{40}" /> +<symbol char="2709" text="\ding{41}" /> +<symbol char="270C" text="\ding{44}" /> +<symbol char="270D" text="\ding{45}" /> +<symbol char="270E" text="\ding{46}" /> +<symbol char="270F" text="\ding{47}" /> +<symbol char="2710" text="\ding{48}" /> +<symbol char="2711" text="\ding{49}" /> +<symbol char="2712" text="\ding{50}" /> +<symbol char="2713" text="\ding{51}" /> +<symbol char="2714" text="\ding{52}" /> +<symbol char="2715" text="\ding{53}" /> +<symbol char="2716" text="\ding{54}" /> +<symbol char="2717" text="\ding{55}" /> +<symbol char="2718" text="\ding{56}" /> +<symbol char="2719" text="\ding{57}" /> +<symbol char="271A" text="\ding{58}" /> +<symbol char="271B" text="\ding{59}" /> +<symbol char="271C" text="\ding{60}" /> +<symbol char="271D" text="\ding{61}" /> +<symbol char="271E" text="\ding{62}" /> +<symbol char="271F" text="\ding{63}" /> +<symbol char="2720" text="\ding{64}" /> +<symbol char="2721" text="\ding{65}" /> +<symbol char="2722" text="\ding{66}" /> +<symbol char="2723" text="\ding{67}" /> +<symbol char="2724" text="\ding{68}" /> +<symbol char="2725" text="\ding{69}" /> +<symbol char="2726" text="\ding{70}" /> +<symbol char="2727" text="\ding{71}" /> +<symbol char="2729" text="\ding{73}" /> +<symbol char="272A" text="\ding{74}" /> +<symbol char="272B" text="\ding{75}" /> +<symbol char="272C" text="\ding{76}" /> +<symbol char="272D" text="\ding{77}" /> +<symbol char="272E" text="\ding{78}" /> +<symbol char="272F" text="\ding{79}" /> +<symbol char="2730" text="\ding{80}" /> +<symbol char="2731" text="\ding{81}" /> +<symbol char="2732" text="\ding{82}" /> +<symbol char="2733" text="\ding{83}" /> +<symbol char="2734" text="\ding{84}" /> +<symbol char="2735" text="\ding{85}" /> +<symbol char="2736" text="\ding{86}" /> +<symbol char="2737" text="\ding{87}" /> +<symbol char="2738" text="\ding{88}" /> +<symbol char="2739" text="\ding{89}" /> +<symbol char="273A" text="\ding{90}" /> +<symbol char="273B" text="\ding{91}" /> +<symbol char="273C" text="\ding{92}" /> +<symbol char="273D" text="\ding{93}" /> +<symbol char="273E" text="\ding{94}" /> +<symbol char="273F" text="\ding{95}" /> +<symbol char="2740" text="\ding{96}" /> +<symbol char="2741" text="\ding{97}" /> +<symbol char="2742" text="\ding{98}" /> +<symbol char="2743" text="\ding{99}" /> +<symbol char="2744" text="\ding{100}" /> +<symbol char="2745" text="\ding{101}" /> +<symbol char="2746" text="\ding{102}" /> +<symbol char="2747" text="\ding{103}" /> +<symbol char="2748" text="\ding{104}" /> +<symbol char="2749" text="\ding{105}" /> +<symbol char="274A" text="\ding{106}" /> +<symbol char="274B" text="\ding{107}" /> +<symbol char="274D" text="\ding{109}" /> +<symbol char="274F" text="\ding{111}" /> +<symbol char="2750" text="\ding{112}" /> +<symbol char="2751" text="\ding{113}" /> +<symbol char="2752" text="\ding{114}" /> +<symbol char="2756" text="\ding{118}" /> +<symbol char="2758" text="\ding{120}" /> +<symbol char="2759" text="\ding{121}" /> +<symbol char="275A" text="\ding{122}" /> +<symbol char="275B" text="\ding{123}" /> +<symbol char="275C" text="\ding{124}" /> +<symbol char="275D" text="\ding{125}" /> +<symbol char="275E" text="\ding{126}" /> +<symbol char="2761" text="\ding{161}" /> +<symbol char="2762" text="\ding{162}" /> +<symbol char="2763" text="\ding{163}" /> +<symbol char="2764" text="\ding{164}" /> +<symbol char="2765" text="\ding{165}" /> +<symbol char="2766" text="\ding{166}" /> +<symbol char="2767" text="\ding{167}" /> +<symbol char="2776" text="\ding{182}" /> +<symbol char="2777" text="\ding{183}" /> +<symbol char="2778" text="\ding{184}" /> +<symbol char="2779" text="\ding{185}" /> +<symbol char="277A" text="\ding{186}" /> +<symbol char="277B" text="\ding{187}" /> +<symbol char="277C" text="\ding{188}" /> +<symbol char="277D" text="\ding{189}" /> +<symbol char="277E" text="\ding{190}" /> +<symbol char="277F" text="\ding{191}" /> +<symbol char="2780" text="\ding{192}" /> +<symbol char="2781" text="\ding{193}" /> +<symbol char="2782" text="\ding{194}" /> +<symbol char="2783" text="\ding{195}" /> +<symbol char="2784" text="\ding{196}" /> +<symbol char="2785" text="\ding{197}" /> +<symbol char="2786" text="\ding{198}" /> +<symbol char="2787" text="\ding{199}" /> +<symbol char="2788" text="\ding{200}" /> +<symbol char="2789" text="\ding{201}" /> +<symbol char="278A" text="\ding{202}" /> +<symbol char="278B" text="\ding{203}" /> +<symbol char="278C" text="\ding{204}" /> +<symbol char="278D" text="\ding{205}" /> +<symbol char="278E" text="\ding{206}" /> +<symbol char="278F" text="\ding{207}" /> +<symbol char="2790" text="\ding{208}" /> +<symbol char="2791" text="\ding{209}" /> +<symbol char="2792" text="\ding{210}" /> +<symbol char="2793" text="\ding{211}" /> +<symbol char="2794" text="\ding{212}" /> +<symbol char="2798" text="\ding{216}" /> +<symbol char="2799" text="\ding{217}" /> +<symbol char="279A" text="\ding{218}" /> +<symbol char="279B" text="\ding{219}" /> +<symbol char="279C" text="\ding{220}" /> +<symbol char="279D" text="\ding{221}" /> +<symbol char="279E" text="\ding{222}" /> +<symbol char="279F" text="\ding{223}" /> +<symbol char="27A0" text="\ding{224}" /> +<symbol char="27A1" text="\ding{225}" /> +<symbol char="27A2" text="\ding{226}" /> +<symbol char="27A3" text="\ding{227}" /> +<symbol char="27A4" text="\ding{228}" /> +<symbol char="27A5" text="\ding{229}" /> +<symbol char="27A6" text="\ding{230}" /> +<symbol char="27A7" text="\ding{231}" /> +<symbol char="27A8" text="\ding{232}" /> +<symbol char="27A9" text="\ding{233}" /> +<symbol char="27AA" text="\ding{234}" /> +<symbol char="27AB" text="\ding{235}" /> +<symbol char="27AC" text="\ding{236}" /> +<symbol char="27AD" text="\ding{237}" /> +<symbol char="27AE" text="\ding{238}" /> +<symbol char="27AF" text="\ding{239}" /> +<symbol char="27B1" text="\ding{241}" /> +<symbol char="27B2" text="\ding{242}" /> +<symbol char="27B3" text="\ding{243}" /> +<symbol char="27B4" text="\ding{244}" /> +<symbol char="27B5" text="\ding{245}" /> +<symbol char="27B6" text="\ding{246}" /> +<symbol char="27B7" text="\ding{247}" /> +<symbol char="27B8" text="\ding{248}" /> +<symbol char="27B9" text="\ding{249}" /> +<symbol char="27BA" text="\ding{250}" /> +<symbol char="27BB" text="\ding{251}" /> +<symbol char="27BC" text="\ding{252}" /> +<symbol char="27BD" text="\ding{253}" /> +<symbol char="27BE" text="\ding{254}" /> +</symbol-set> <!-- end of dingbats --> + +<!-- support for tipa phonetic symbols (tipa.sty) --> +<symbol-set name="tipa" fontenc="any" > + <!-- Unicode block: IPA extensions --> + <symbol char="0250" text="{\textturna}" /> + <symbol char="0251" text="{\textscripta}" /> + <symbol char="0252" text="{\textturnscripta}" /> + <symbol char="0253" text="{\texthtb}" /> + <symbol char="0254" text="{\textopeno}" /> + <symbol char="0255" text="{\textctc}" /> + <symbol char="0256" text="{\textrtaild}" /> + <symbol char="0257" text="{\texthtd}" /> + <symbol char="0258" text="{\textreve}" /> + <symbol char="0259" text="{\textschwa}" /> + <symbol char="025A" text="{\textrhookschwa}" /> + <symbol char="025B" text="{\textepsilon}" /> + <symbol char="025C" text="{\textrevepsilon}" /> + <symbol char="025D" text="{\textrhookrevepsilon}" /> + <symbol char="025E" text="{\textcloserevepsilon}" /> + <symbol char="025F" text="{\textbardotlessj}" /> + <symbol char="0260" text="{\texthtg}" /> + <symbol char="0261" text="{\textscriptg}" /> + <symbol char="0262" text="{\textscg}" /> + <symbol char="0263" text="{\textgamma}" /> + <symbol char="0264" text="{\textramshorns}" /> + <symbol char="0265" text="{\textturnh}" /> + <symbol char="0266" text="{\texthth}" /> + <symbol char="0267" text="{\texththeng}" /> + <symbol char="0268" text="{\textbari}" /> + <symbol char="0269" text="{\textiota}" /> + <symbol char="026A" text="{\textsci}" /> + <symbol char="026B" text="{\textltilde}" /> + <symbol char="026C" text="{\textbeltl}" /> + <symbol char="026D" text="{\textrtaill}" /> + <symbol char="026E" text="{\textlyoghlig}" /> + <symbol char="026F" text="{\textturnm}" /> + <symbol char="0270" text="{\textturnmrleg}" /> + <symbol char="0271" text="{\textltailm}" /> + <symbol char="0272" text="{\textltailn}" /> + <symbol char="0273" text="{\textrtailn}" /> + <symbol char="0274" text="{\textscn}" /> + <symbol char="0275" text="{\textbaro}" /> + <symbol char="0276" text="{\textscoelig}" /> + <symbol char="0277" text="{\textcloseomega}" /> + <symbol char="0278" text="{\textphi}" /> + <symbol char="0279" text="{\textturnr}" /> + <symbol char="027A" text="{\textturnlonglegr}" /> + <symbol char="027B" text="{\textturnrrtail}" /> + <symbol char="027C" text="{\textlonglegr}" /> + <symbol char="027D" text="{\textrtailr}" /> + <symbol char="027E" text="{\textfishhookr}" /> + <symbol char="027F" text="{\textlhti}" /> + <symbol char="0280" text="{\textscr}" /> + <symbol char="0281" text="{\textinvscr}" /> + <symbol char="0282" text="{\textrtails}" /> + <symbol char="0283" text="{\textesh}" /> + <symbol char="0284" text="{\texthtbardotlessj}" /> + <symbol char="0285" text="{\textraisevibyi}" /> + <symbol char="0286" text="{\textctesh}" /> + <symbol char="0287" text="{\textturnt}" /> + <symbol char="0288" text="{\textrtailt}" /> + <symbol char="0289" text="{\textbaru}" /> + <symbol char="028A" text="{\textupsilon}" /> + <symbol char="028B" text="{\textscriptv}" /> + <symbol char="028C" text="{\textturnv}" /> + <symbol char="028D" text="{\textturnw}" /> + <symbol char="028E" text="{\textturny}" /> + <symbol char="028F" text="{\textscy}" /> + <symbol char="0290" text="{\textrtailz}" /> + <symbol char="0291" text="{\textctz}" /> + <symbol char="0292" text="{\textyogh}" /> + <symbol char="0293" text="{\textctyogh}" /> + <symbol char="0294" text="{\textglotstop}" /> + <symbol char="0295" text="{\textrevglotstop}" /> + <symbol char="0296" text="{\textinvglotstop}" /> + <symbol char="0297" text="{\textstretchc}" /> + <symbol char="0298" text="{\textbullseye}" /> + <symbol char="0299" text="{\textscb}" /> + <symbol char="029A" text="{\textcloseepsilon}" /> + <symbol char="029B" text="{\texthtscg}" /> + <symbol char="029C" text="{\textsch}" /> + <symbol char="029D" text="{\textctj}" /> + <symbol char="029E" text="{\textturnk}" /> + <symbol char="029F" text="{\textscl}" /> + <symbol char="02A0" text="{\texthtq}" /> + <symbol char="02A1" text="{\textbarglotstop}" /> + <symbol char="02A2" text="{\textbarrevglotstop}" /> + <symbol char="02A3" text="{\textdzlig}" /> + <symbol char="02A4" text="{\textdyoghlig}" /> + <symbol char="02A5" text="{\textdctzlig}" /> + <symbol char="02A6" text="{\texttslig}" /> + <symbol char="02A7" text="{\textteshlig}" /> + <symbol char="02A8" text="{\texttctclig}" /> + + <symbol char="02B0" text="{\textsuperscript h}" /> + <symbol char="02B1" text="{\textsuperscript\texthth}" /> + <symbol char="02B2" text="{\textsuperscript j}" /> + <symbol char="02B3" text="{\textsuperscript r}" /> + <symbol char="02B4" text="{\textsuperscript\textturnr}" /> + <symbol char="02B5" text="{\textsuperscript\textturnrrtail}" /> + <symbol char="02B6" text="{\textsuperscript\textinvscr}" /> + <symbol char="02B7" text="{\textsuperscript w}" /> + <symbol char="02B8" text="{\textsuperscript y}" /> + + <symbol char="02C6" text="{\^{}}" /> + <symbol char="02C7" text="{\v{}}" /> + <symbol char="02C8" text="{\textprimstress}" /> + <symbol char="02C9" text="{\={}}" /> + <symbol char="02CA" text="{\'{}}" /> + <symbol char="02CB" text="{\`{}}" /> + + <symbol char="02CD" text="{\b{}}" /> + + <symbol char="02D0" text="{\textlengthmark}" /> + + <symbol char="02D8" text="{\u{}}" /> + <symbol char="02D9" text="{\.{}}" /> + <symbol char="02DA" text="{\r{}}" /> + <symbol char="02DB" text="{\k{}}" /> + <symbol char="02DC" text="{\~{}}" /> + <symbol char="02DD" text="{\H{}}" /> + + <symbol char="02E5" text="{\tone5}" /> + <symbol char="02E6" text="{\tone4}" /> + <symbol char="02E7" text="{\tone3}" /> + <symbol char="02E8" text="{\tone2}" /> + <symbol char="02E9" text="{\tone1}" /> +</symbol-set> + +<!-- +****************************************************************** +PART IV: Font-specific symbols +These tables contain symbols which are specific for certain fonts; +usually in the private use area of unicode. +This includes 8-bit fonts. These are normally relocated to the range +F000-F0FF by OOo (part of private use area in unicode). +If the attribute eight-bit is set to true, w2l will copy the +definitions to the range 00-FF. +****************************************************************** +--> + +<!-- support for private use area of "OpenSymbol" (OOo symbol font) --> +<special-symbol-set name="OpenSymbol" fontenc="any"> + <symbol char="E002" eq-char="2666" /> + <symbol char="E003" eq-char="25C6" /> + <symbol char="E005" eq-char="274D" /> + <symbol char="E006" eq-char="2794" /> + <symbol char="E007" eq-char="2713" /> + <symbol char="E008" eq-char="25CF" /> + <symbol char="E009" eq-char="274D" /> + <symbol char="E00A" eq-char="25FC" /> + <symbol char="E00B" eq-char="2752" /> + <symbol char="E00D" eq-char="2756" /> + <symbol char="E013" eq-char="2742" /> + <symbol char="E01B" eq-char="270D" /> + <symbol char="E01E" eq-char="2022" /> + <symbol char="E021" eq-char="00A9" /> + <symbol char="E024" eq-char="00AE" /> + <symbol char="E025" eq-char="21E8" /> + <symbol char="E026" eq-char="21E9" /> + <symbol char="E027" eq-char="21E6" /> + <symbol char="E028" eq-char="21E7" /> + <symbol char="E02B" eq-char="279E" /> + <symbol char="E032" eq-char="2741" /> + <symbol char="E036" eq-char="0028" /> + <symbol char="E037" eq-char="0029" /> + <symbol char="E03A" eq-char="20AC" /> + <symbol char="E080" eq-char="2030" /> + <symbol char="E081" eq-char="FE38" /> <!-- underbrace --> + <symbol char="E082" eq-char="FE37" /> <!-- overbrace --> + <symbol char="E083" eq-char="002B" /> + <symbol char="E084" eq-char="003C" /> + <symbol char="E085" eq-char="003E" /> + <symbol char="E086" eq-char="2264" /> + <symbol char="E087" eq-char="2265" /> + <symbol char="E089" eq-char="2208" /> + <symbol char="E08B" eq-char="2026" /> + <symbol char="E08C" eq-char="2192" /> + <symbol char="E090" eq-char="2225" /> + <symbol char="E091" eq-char="005E" /> + <symbol char="E092" eq-char="02C7" /> + <symbol char="E093" eq-char="02D8" /> + <symbol char="E094" eq-char="00B4" /> + <symbol char="E095" eq-char="0060" /> + <symbol char="E096" eq-char="02DC" /> <!-- or 007E --> + <symbol char="E097" eq-char="00AF" /> + <symbol char="E098" eq-char="2192" /> <!-- or 20E1 --> + <symbol char="E09B" eq-char="20DB" /> + <symbol char="E09E" eq-char="0028" /> + <symbol char="E09F" eq-char="0029" /> + <symbol char="E0A0" eq-char="2221" /> + <symbol char="E0AA" eq-char="2751" /> + <symbol char="E0AC" eq-char="0393" /> + <symbol char="E0AD" eq-char="0394" /> + <symbol char="E0AE" eq-char="0398" /> + <symbol char="E0AF" eq-char="039B" /> + <symbol char="E0B0" eq-char="039E" /> + <symbol char="E0B1" eq-char="03A0" /> + <symbol char="E0B2" eq-char="03A3" /> + <symbol char="E0B3" eq-char="03A5" /> + <symbol char="E0B4" eq-char="03A6" /> + <symbol char="E0B5" eq-char="03A8" /> + <symbol char="E0B6" eq-char="03A9" /> + <symbol char="E0B7" eq-char="03B1" /> + <symbol char="E0B8" eq-char="03B2" /> + <symbol char="E0B9" eq-char="03B3" /> + <symbol char="E0BA" eq-char="03B4" /> + <symbol char="E0BB" eq-char="03F5" /> + <symbol char="E0BC" eq-char="03B6" /> + <symbol char="E0BD" eq-char="03B7" /> + <symbol char="E0BE" eq-char="03B8" /> + <symbol char="E0BF" eq-char="03B9" /> + <symbol char="E0C0" eq-char="03BA" /> + <symbol char="E0C1" eq-char="03BB" /> + <symbol char="E0C2" eq-char="03BC" /> + <symbol char="E0C3" eq-char="03BD" /> + <symbol char="E0C4" eq-char="03BE" /> + <symbol char="E0C5" eq-char="03BF" /> + <symbol char="E0C6" eq-char="03C0" /> + <symbol char="E0C7" eq-char="03C1" /> + <symbol char="E0C8" eq-char="03C3" /> + <symbol char="E0C9" eq-char="03C4" /> + <symbol char="E0CA" eq-char="03C5" /> + <symbol char="E0CB" eq-char="03D5" /> + <symbol char="E0CC" eq-char="03C7" /> + <symbol char="E0CD" eq-char="03C8" /> + <symbol char="E0CE" eq-char="03C9" /> + <symbol char="E0CF" eq-char="03B5" /> + <symbol char="E0D0" eq-char="03D1" /> + <symbol char="E0D1" eq-char="03D6" /> + <symbol char="E0D3" eq-char="03C2" /> + <symbol char="E0D4" eq-char="03C6" /> + <symbol char="E0D5" eq-char="2202" /> + <symbol char="E0D9" eq-char="22A4" /> + <symbol char="E0DB" eq-char="2190" /> + <symbol char="E0DC" eq-char="2191" /> + <symbol char="E0DD" eq-char="2193" /> +</special-symbol-set> + +<!-- support for private use area of "StarSymbol" (StarOffice symbol font) + StarSymbol is an extension of OpenSymbol --> +<special-symbol-set name="StarSymbol" parent="OpenSymbol" fontenc="any"> + <!-- todo --> +</special-symbol-set> + +<!-- support for the standard mswindows font "Symbol" --> +<special-symbol-set name="Symbol" fontenc="any" eight-bit="true" > + <symbol char="F020" text=" " /> + <symbol char="F021" text="!" /> + <symbol char="F022" math="{\forall}" /> + <symbol char="F023" text="\#" /> + <symbol char="F024" math="{\exists}" /> + <symbol char="F025" text="\%" /> + <symbol char="F026" text="\&" /> + <symbol char="F027" math="{\backepsilon}" /> + <symbol char="F028" text="(" /> + <symbol char="F029" text=")" /> + <symbol char="F02A" math="*" /> + <symbol char="F02B" math="+" /> + <symbol char="F02C" text="," /> + <symbol char="F02D" math="-" /> + <symbol char="F02E" text="." /> + <symbol char="F02F" math="/" /> + <symbol char="F030" text="0" /> + <symbol char="F031" text="1" /> + <symbol char="F032" text="2" /> + <symbol char="F033" text="3" /> + <symbol char="F034" text="4" /> + <symbol char="F035" text="5" /> + <symbol char="F036" text="6" /> + <symbol char="F037" text="7" /> + <symbol char="F038" text="8" /> + <symbol char="F039" text="9" /> + <symbol char="F03A" text=":" /> + <symbol char="F03B" text=";" /> + <symbol char="F03C" math="<" /> + <symbol char="F03D" math="=" /> + <symbol char="F03E" math=">" /> + <symbol char="F03F" text="?" /> + <symbol char="F040" math="{\cong}" /> + <symbol char="F041" math="A" /> + <symbol char="F042" math="B" /> + <symbol char="F043" math="X" /> + <symbol char="F044" math="{\Delta}" /> + <symbol char="F045" math="E" /> + <symbol char="F046" math="{\Phi}" /> + <symbol char="F047" math="{\Gamma}" /> + <symbol char="F048" math="H" /> + <symbol char="F049" math="I" /> + <symbol char="F04A" math="{\vartheta}" /> + <symbol char="F04B" math="K" /> + <symbol char="F04C" math="{\Lambda}" /> + <symbol char="F04D" math="M" /> + <symbol char="F04E" math="N" /> + <symbol char="F04F" math="O" /> + <symbol char="F050" math="{\Pi}" /> + <symbol char="F051" math="{\Theta}" /> + <symbol char="F052" math="P" /> + <symbol char="F053" math="{\Sigma}" /> + <symbol char="F054" math="T" /> + <symbol char="F055" math="Y" /> + <symbol char="F056" math="{\varsigma}" /> + <symbol char="F057" math="{\Omega}" /> + <symbol char="F058" math="{\Xi}" /> + <symbol char="F059" math="{\Psi}" /> + <symbol char="F05A" math="Z" /> + <symbol char="F05B" text="[" /> + <symbol char="F05C" math="{\therefore}" /> + <symbol char="F05D" text="]" /> + <symbol char="F05E" math="{\perp}" /> + <symbol char="F05F" text="\_" /> + <symbol char="F060" text="{\textasciimacron}" /> <!-- not sure? --> + <symbol char="F061" math="{\alpha}" /> + <symbol char="F062" math="{\beta}" /> + <symbol char="F063" math="{\chi}" /> + <symbol char="F064" math="{\delta}" /> + <symbol char="F065" math="{\varepsilon}" /> + <symbol char="F066" math="{\phi}" /> + <symbol char="F067" math="{\gamma}" /> + <symbol char="F068" math="{\eta}" /> + <symbol char="F069" math="{\iota}" /> + <symbol char="F06A" math="{\varphi}" /> + <symbol char="F06B" math="{\kappa}" /> + <symbol char="F06C" math="{\lambda}" /> + <symbol char="F06D" math="{\mu}" /> + <symbol char="F06E" math="{\nu}" /> + <symbol char="F06F" math="o" /> + <symbol char="F070" math="{\pi}" /> + <symbol char="F071" math="{\theta}" /> + <symbol char="F072" math="{\varrho}" /> + <symbol char="F073" math="{\sigma}" /> + <symbol char="F074" math="{\tau}" /> + <symbol char="F075" math="{\upsilon}" /> + <symbol char="F076" math="{\varpi}" /> + <symbol char="F077" math="{\omega}" /> + <symbol char="F078" math="{\xi}" /> + <symbol char="F079" math="{\psi}" /> + <symbol char="F07A" math="{\zeta}" /> + <symbol char="F07B" text="\{" /> + <symbol char="F07C" text="{\textbar}" /> + <symbol char="F07D" text="\}" /> + <symbol char="F07E" text="\~{}" /> + <symbol char="F0A1" math="{\Upsilon}" /> + <symbol char="F0A2" text="'" /> + <symbol char="F0A3" math="{\leq}" /> + <symbol char="F0A4" math="/" /> + <symbol char="F0A5" math="{\infty}" /> + <symbol char="F0A6" math="f" /> + <symbol char="F0A7" math="{\clubsuit}" /> + <symbol char="F0A8" math="{\diamondsuit}" /> + <symbol char="F0A9" math="{\heartsuit}" /> + <symbol char="F0AA" math="{\spadesuit}" /> + <symbol char="F0AB" math="{\leftrightarrow}" /> + <symbol char="F0AC" math="{\leftarrow}" /> + <symbol char="F0AD" math="{\uparrow}" /> + <symbol char="F0AE" math="{\rightarrow}" /> + <symbol char="F0AF" math="{\downarrow}" /> + <symbol char="F0B0" text="{\textdegree}" /> + <symbol char="F0B1" math="{\pm}" /> + <symbol char="F0B2" text="{\textquotedbl}" /> + <symbol char="F0B3" math="{\geq}" /> + <symbol char="F0B4" math="{\times}" /> + <symbol char="F0B5" math="{\propto}" /> + <symbol char="F0B6" math="{\partial}" /> + <symbol char="F0B7" text="{\textbullet}" /> + <symbol char="F0B8" math="{\div}" /> + <symbol char="F0B9" math="{\neq}" /> + <symbol char="F0BA" math="{\equiv}" /> + <symbol char="F0BB" math="{\approx}" /> + <symbol char="F0BC" math="{\ldots}" /> + <symbol char="F0BD" math="{\mid}" /> + <symbol char="F0BE" text="---" dashes="true" /> + <!--<symbol char="F0BF" math="??" />--> + <symbol char="F0C0" math="{\aleph}" /> + <symbol char="F0C1" math="{\Im}" /> + <symbol char="F0C2" math="{\Re}" /> + <symbol char="F0C3" math="{\wp}" /> + <symbol char="F0C4" math="{\otimes}" /> + <symbol char="F0C5" math="{\oplus}" /> + <symbol char="F0C6" math="{\varnothing}" /> + <symbol char="F0C7" math="{\cap}" /> + <symbol char="F0C8" math="{\cup}" /> + <symbol char="F0C9" math="{\supset}" /> + <symbol char="F0CA" math="{\supseteq}" /> + <symbol char="F0CB" math="{\not\subset}" /> + <symbol char="F0CC" math="{\subset}" /> + <symbol char="F0CD" math="{\subseteq}" /> + <symbol char="F0CE" math="{\in}" /> + <symbol char="F0CF" math="{\not\in}" /> + <symbol char="F0D0" math="{\angle}" /> + <symbol char="F0D1" math="{\nabla}" /> + <symbol char="F0D2" text="{\textregistered}" /> + <symbol char="F0D3" text="{\textcopyright}" /> + <symbol char="F0D4" text="{\texttrademark}" /> + <symbol char="F0D5" math="{\prod}" /> + <symbol char="F0D6" math="{\surd}" /> + <symbol char="F0D7" math="{\cdot}" /> + <symbol char="F0D8" math="{\lnot}" /> + <symbol char="F0D9" math="{\wedge}" /> + <symbol char="F0DA" math="{\vee}" /> + <symbol char="F0DB" math="{\Leftrightarrow}" /> + <symbol char="F0DC" math="{\Leftarrow}" /> + <symbol char="F0DD" math="{\Uparrow}" /> + <symbol char="F0DE" math="{\Rightarrow}" /> + <symbol char="F0DF" math="{\Downarrow}" /> + <symbol char="F0E0" math="{\Diamond}" /> + <symbol char="F0E1" math="{\langle}" /> + <symbol char="F0E2" text="{\textregistered}" /> + <symbol char="F0E3" text="{\textcopyright}" /> + <symbol char="F0E4" text="{\texttrademark}" /> + <symbol char="F0E5" math="{\sum}" /> + <symbol char="F0F1" math="{\rangle}" /> + <symbol char="F0F2" math="{\int}" /> +</special-symbol-set> + +<!-- support for the standard mswindows font "WingDings" --> +<special-symbol-set name="Wingdings" fontenc="any" eight-bit="true" > + <symbol char="F020" eq-char="0020" /> + <symbol char="F021" eq-char="270F" /> + <symbol char="F022" eq-char="2702" /> + <symbol char="F023" eq-char="2701" /> + <symbol char="F028" eq-char="260E" /> + <symbol char="F029" eq-char="2706" /> + <symbol char="F02A" eq-char="2709" /> + <symbol char="F036" eq-char="231B" /> + <symbol char="F037" eq-char="2328" /> + <symbol char="F03E" eq-char="2707" /> + <symbol char="F03F" eq-char="270D" /> + <symbol char="F041" eq-char="270C" /> + <symbol char="F045" eq-char="261C" /> + <symbol char="F046" eq-char="261E" /> + <symbol char="F047" eq-char="261D" /> + <symbol char="F048" eq-char="261F" /> + <symbol char="F04A" eq-char="263A" /> + <symbol char="F04C" eq-char="2639" /> + <symbol char="F04E" eq-char="2620" /> + <symbol char="F051" eq-char="2708" /> + <symbol char="F052" eq-char="263C" /> + <symbol char="F054" eq-char="2744" /> + <symbol char="F056" eq-char="271E" /> + <symbol char="F058" eq-char="2720" /> + <symbol char="F059" eq-char="2721" /> + <symbol char="F05A" eq-char="262A" /> + <symbol char="F05B" eq-char="262F" /> + <symbol char="F05C" eq-char="0950" /> + <symbol char="F05D" eq-char="2638" /> + <symbol char="F05E" eq-char="2648" /> + <symbol char="F05F" eq-char="2649" /> + <symbol char="F060" eq-char="264A" /> + <symbol char="F061" eq-char="264B" /> + <symbol char="F062" eq-char="264C" /> + <symbol char="F063" eq-char="264D" /> + <symbol char="F064" eq-char="264E" /> + <symbol char="F065" eq-char="264F" /> + <symbol char="F066" eq-char="2650" /> + <symbol char="F067" eq-char="2651" /> + <symbol char="F068" eq-char="2652" /> + <symbol char="F069" eq-char="2653" /> + <symbol char="F06A" eq-char="0026" /> + <symbol char="F06B" eq-char="0026" /> + <symbol char="F06C" eq-char="25CF" /> + <symbol char="F06D" eq-char="274D" /> + <symbol char="F06E" eq-char="25A0" /> + <symbol char="F06F" eq-char="25A1" /> + <symbol char="F071" eq-char="2751" /> + <symbol char="F072" eq-char="2752" /> + <symbol char="F074" eq-char="2666" /> + <symbol char="F075" eq-char="25C6" /> + <symbol char="F076" eq-char="2756" /> + <symbol char="F078" eq-char="2327" /> + <symbol char="F079" eq-char="2353" /> + <symbol char="F07A" eq-char="2318" /> + <symbol char="F07B" eq-char="2740" /> + <symbol char="F07C" eq-char="273F" /> + <symbol char="F07D" eq-char="275D" /> + <symbol char="F07E" eq-char="275E" /> + <symbol char="F07F" eq-char="25AF" /> + <symbol char="F080" eq-char="24EA" /> + <symbol char="F081" eq-char="2460" /> + <symbol char="F082" eq-char="2461" /> + <symbol char="F083" eq-char="2462" /> + <symbol char="F084" eq-char="2463" /> + <symbol char="F085" eq-char="2464" /> + <symbol char="F086" eq-char="2465" /> + <symbol char="F087" eq-char="2466" /> + <symbol char="F088" eq-char="2467" /> + <symbol char="F089" eq-char="2468" /> + <symbol char="F08A" eq-char="2469" /> + <symbol char="F08B" eq-char="24FF" /> + <symbol char="F08C" eq-char="2776" /> + <symbol char="F08D" eq-char="2777" /> + <symbol char="F08E" eq-char="2778" /> + <symbol char="F08F" eq-char="2779" /> + <symbol char="F090" eq-char="277A" /> + <symbol char="F091" eq-char="277B" /> + <symbol char="F092" eq-char="277C" /> + <symbol char="F093" eq-char="277D" /> + <symbol char="F094" eq-char="277E" /> + <symbol char="F095" eq-char="277F" /> + <symbol char="F09E" eq-char="00B7" /> + <symbol char="F09F" eq-char="2022" /> + <symbol char="F0A0" eq-char="25AA" /> + <symbol char="F0A1" eq-char="25CB" /> + <symbol char="F0A4" eq-char="25C9" /> + <symbol char="F0A5" eq-char="25CE" /> + <symbol char="F0A7" eq-char="25AA" /> + <symbol char="F0A8" eq-char="25FB" /> + <symbol char="F0AA" eq-char="2726" /> + <symbol char="F0AB" eq-char="2605" /> + <symbol char="F0AC" eq-char="2736" /> + <symbol char="F0AD" eq-char="2734" /> + <symbol char="F0AE" eq-char="2739" /> + <symbol char="F0AF" eq-char="2735" /> + <symbol char="F0B1" eq-char="2316" /> + <symbol char="F0B2" eq-char="2727" /> + <symbol char="F0B3" eq-char="2311" /> + <symbol char="F0B5" eq-char="272A" /> + <symbol char="F0B6" eq-char="2730" /> + <symbol-subset requires="ifsym" fontenc="any"> + <symbol char="F0B7" text="\showclock{1}{0}" /> + <symbol char="F0B8" text="\showclock{2}{0}" /> + <symbol char="F0B9" text="\showclock{3}{0}" /> + <symbol char="F0BA" text="\showclock{4}{0}" /> + <symbol char="F0BB" text="\showclock{5}{0}" /> + <symbol char="F0BC" text="\showclock{6}{0}" /> + <symbol char="F0BD" text="\showclock{7}{0}" /> + <symbol char="F0BE" text="\showclock{8}{0}" /> + <symbol char="F0BF" text="\showclock{9}{0}" /> + <symbol char="F0C0" text="\showclock{10}{0}" /> + <symbol char="F0C1" text="\showclock{11}{0}" /> + <symbol char="F0C2" text="\showclock{0}{0}" /> + </symbol-subset> + <symbol char="F0D5" eq-char="232B" /> + <symbol char="F0D6" eq-char="2326" /> + <symbol char="F0D8" eq-char="27A2" /> + <symbol char="F0DC" eq-char="27B2" /> + <symbol char="F0E8" eq-char="2794" /> + <symbol char="F0EF" eq-char="21E6" /> + <symbol char="F0F0" eq-char="21E8" /> + <symbol char="F0F1" eq-char="21E7" /> + <symbol char="F0F2" eq-char="21E9" /> + <symbol char="F0F3" eq-char="2B04" /> + <symbol char="F0F4" eq-char="21F3" /> + <symbol char="F0F5" eq-char="2B00" /> + <symbol char="F0F6" eq-char="2B01" /> + <symbol char="F0F7" eq-char="2B03" /> + <symbol char="F0F8" eq-char="2B02" /> + <symbol char="F0F9" eq-char="25AD" /> + <symbol char="F0FA" eq-char="25AB" /> + <symbol char="F0FB" eq-char="2717" /> + <symbol char="F0FC" eq-char="2713" /> + <symbol char="F0FD" eq-char="2612" /> + <symbol char="F0FE" eq-char="2611" /> +</special-symbol-set> + +<!-- support for bakoma fonts (Truetype version of Computer Modern) --> +<special-symbol-set name="cmr10" fontenc="any" eight-bit="true"> +<symbol char="F020" text=" "/> +<symbol char="F021" math="!"/> +<symbol char="F022" text="{\textquotedblright}"/> +<symbol char="F023" math="\#"/> +<symbol char="F024" math="\$"/> +<symbol char="F025" math="\%"/> +<symbol char="F026" math="\&"/> +<symbol char="F027" text="{\textquoteright}"/> +<symbol char="F028" math="("/> +<symbol char="F029" math=")"/> +<symbol char="F02A" math="*"/> +<symbol char="F02B" math="+"/> +<symbol char="F02C" math=","/> +<symbol char="F02D" text="-" dashes="true" /> +<symbol char="F02E" math="."/> +<symbol char="F02F" math="/"/> +<symbol char="F030" math="0"/> +<symbol char="F031" math="1"/> +<symbol char="F032" math="2"/> +<symbol char="F033" math="3"/> +<symbol char="F034" math="4"/> +<symbol char="F035" math="5"/> +<symbol char="F036" math="6"/> +<symbol char="F037" math="7"/> +<symbol char="F038" math="8"/> +<symbol char="F039" math="9"/> +<symbol char="F03A" math=":"/> +<symbol char="F03B" math=";"/> +<symbol char="F03C" text="!`"/> +<symbol char="F03D" math="="/> +<symbol char="F03E" text="?`"/> +<symbol char="F03F" math="?"/> +<symbol char="F040" math="@"/> +<symbol char="F041" math="A"/> +<symbol char="F042" math="B"/> +<symbol char="F043" math="C"/> +<symbol char="F044" math="D"/> +<symbol char="F045" math="E"/> +<symbol char="F046" math="F"/> +<symbol char="F047" math="G"/> +<symbol char="F048" math="H"/> +<symbol char="F049" math="I"/> +<symbol char="F04A" math="J"/> +<symbol char="F04B" math="K"/> +<symbol char="F04C" math="L"/> +<symbol char="F04D" math="M"/> +<symbol char="F04E" math="N"/> +<symbol char="F04F" math="O"/> +<symbol char="F050" math="P"/> +<symbol char="F051" math="Q"/> +<symbol char="F052" math="R"/> +<symbol char="F053" math="S"/> +<symbol char="F054" math="T"/> +<symbol char="F055" math="U"/> +<symbol char="F056" math="V"/> +<symbol char="F057" math="W"/> +<symbol char="F058" math="X"/> +<symbol char="F059" math="Y"/> +<symbol char="F05A" math="Z"/> +<symbol char="F05B" math="["/> +<symbol char="F05C" text="{\textquotedblleft}"/> +<symbol char="F05D" math="]"/> +<symbol char="F060" text="{\textquoteleft}"/> +<symbol char="F061" math="a"/> +<symbol char="F062" math="b"/> +<symbol char="F063" math="c"/> +<symbol char="F064" math="d"/> +<symbol char="F065" math="e"/> +<symbol char="F066" math="f"/> +<symbol char="F067" math="g"/> +<symbol char="F068" math="h"/> +<symbol char="F069" math="i"/> +<symbol char="F06A" math="j"/> +<symbol char="F06B" math="k"/> +<symbol char="F06C" math="l"/> +<symbol char="F06D" math="m"/> +<symbol char="F06E" math="n"/> +<symbol char="F06F" math="o"/> +<symbol char="F070" math="p"/> +<symbol char="F071" math="q"/> +<symbol char="F072" math="r"/> +<symbol char="F073" math="s"/> +<symbol char="F074" math="t"/> +<symbol char="F075" math="u"/> +<symbol char="F076" math="v"/> +<symbol char="F077" math="w"/> +<symbol char="F078" math="x"/> +<symbol char="F079" math="y"/> +<symbol char="F07A" math="z"/> +<symbol char="F07B" text="{--}" dashes="true"/> +<symbol char="F07C" text="{---}" dashes="true"/> +<symbol char="F0A1" math="\Gamma " /> +<symbol char="F0A2" math="\Delta "/> +<symbol char="F0A3" math="\Theta "/> +<symbol char="F0A4" math="\Lambda "/> +<symbol char="F0A5" math="\Xi "/> +<symbol char="F0A6" math="\Pi "/> +<symbol char="F0A7" math="\Sigma "/> +<symbol char="F0A8" math="\Upsilon "/> +<symbol char="F0A9" math="\Phi "/> +<symbol char="F0AA" math="\Psi "/> +<symbol char="F0AD" math="\Omega "/> +<symbol char="F0AE" text="ff"/> +<symbol char="F0AF" text="fi"/> +<symbol char="F0B0" text="fl"/> +<symbol char="F0B1" text="ffi"/> +<symbol char="F0B2" text="ffl"/> +<symbol char="F0B3" math="\imath "/> +<symbol char="F0B4" math="\jmath "/> +<symbol char="F0BC" text="{\ss}"/> +<symbol char="F0BD" text="{\ae}"/> +<symbol char="F0BE" text="{\oe}"/> +<symbol char="F0BF" text="{\o}"/> +<symbol char="F0C0" text="{\AE}" /> +<symbol char="F0C1" text="{\OE}" /> +<symbol char="F0C2" text="{\O}" /> +</special-symbol-set> + +<special-symbol-set name="cmmi10" fontenc="any" eight-bit="true" math-mode="true"> + <symbol char="F020" math="" /> + <symbol char="F021" math="\omega " /> + <symbol char="F022" math="\varepsilon " /> + <symbol char="F023" math="\vartheta " /> + <symbol char="F024" math="\varpi " /> + <symbol char="F025" math="\varrho " /> + <symbol char="F026" math="\varsigma " /> + <symbol char="F027" math="\varphi " /> + <symbol char="F028" math="\leftharpoonup " /> + <symbol char="F029" math="\leftharpoondown " /> + <symbol char="F02A" math="\rightharpoonup " /> + <symbol char="F02B" math="\rightharpoondown " /> + <symbol char="F02C" math="" /> <!-- ?? --> + <symbol char="F02D" math="" /> <!-- ?? --> + <symbol char="F02E" math="\triangleright " /> + <symbol char="F02F" math="\triangleleft " /> + <symbol char="F030" math="\mathnormal{0}" /> + <symbol char="F031" math="\mathnormal{1}" /> + <symbol char="F032" math="\mathnormal{2}" /> + <symbol char="F033" math="\mathnormal{3}" /> + <symbol char="F034" math="\mathnormal{4}" /> + <symbol char="F035" math="\mathnormal{5}" /> + <symbol char="F036" math="\mathnormal{6}" /> + <symbol char="F037" math="\mathnormal{7}" /> + <symbol char="F038" math="\mathnormal{8}" /> + <symbol char="F039" math="\mathnormal{9}" /> + <symbol char="F03A" math="." /> + <symbol char="F03B" math="," /> + <symbol char="F03C" math="<" /> + <symbol char="F03D" math="/" /> + <symbol char="F03E" math=">" /> + <symbol char="F03F" math="*" /> + <symbol char="F040" math="\partial " /> + <symbol char="F041" math="A" /> + <symbol char="F042" math="B" /> + <symbol char="F043" math="C" /> + <symbol char="F044" math="D" /> + <symbol char="F045" math="E" /> + <symbol char="F046" math="F" /> + <symbol char="F047" math="G" /> + <symbol char="F048" math="H" /> + <symbol char="F049" math="I" /> + <symbol char="F04A" math="J" /> + <symbol char="F04B" math="K" /> + <symbol char="F04C" math="L" /> + <symbol char="F04D" math="M" /> + <symbol char="F04E" math="N" /> + <symbol char="F04F" math="O" /> + <symbol char="F050" math="P" /> + <symbol char="F051" math="Q" /> + <symbol char="F052" math="R" /> + <symbol char="F053" math="S" /> + <symbol char="F054" math="T" /> + <symbol char="F055" math="U" /> + <symbol char="F056" math="V" /> + <symbol char="F057" math="W" /> + <symbol char="F058" math="X" /> + <symbol char="F059" math="Y" /> + <symbol char="F05A" math="Z" /> + <symbol char="F05B" math="\flat " /> + <symbol char="F05C" math="\natural " /> + <symbol char="F05D" math="\sharp " /> + <symbol char="F05E" math="\smile " /> + <symbol char="F05F" math="\frown " /> + <symbol char="F060" math="\ell" /> + <symbol char="F061" math="a" /> + <symbol char="F062" math="b" /> + <symbol char="F063" math="c" /> + <symbol char="F064" math="d" /> + <symbol char="F065" math="e" /> + <symbol char="F066" math="f" /> + <symbol char="F067" math="g" /> + <symbol char="F068" math="h" /> + <symbol char="F069" math="i" /> + <symbol char="F06A" math="j" /> + <symbol char="F06B" math="k" /> + <symbol char="F06C" math="l" /> + <symbol char="F06D" math="m" /> + <symbol char="F06E" math="n" /> + <symbol char="F06F" math="o" /> + <symbol char="F070" math="p" /> + <symbol char="F071" math="q" /> + <symbol char="F072" math="r" /> + <symbol char="F073" math="s" /> + <symbol char="F074" math="t" /> + <symbol char="F075" math="u" /> + <symbol char="F076" math="v" /> + <symbol char="F077" math="w" /> + <symbol char="F078" math="x" /> + <symbol char="F079" math="y" /> + <symbol char="F07A" math="z" /> + <symbol char="F07B" math="\imath " /> + <symbol char="F07C" math="\jmath " /> + <symbol char="F07D" math="\wp " /> + <symbol char="F07E" math="\vec{}" /> + <symbol char="F080" math="\varkappa " /> + <symbol char="F0A0" math="" /> + <symbol char="F0A1" math="\Gamma " /> + <symbol char="F0A2" math="\Delta " /> + <symbol char="F0A3" math="\Theta " /> + <symbol char="F0A4" math="\Lambda " /> + <symbol char="F0A5" math="\Xi " /> + <symbol char="F0A6" math="\Pi " /> + <symbol char="F0A7" math="\Sigma " /> + <symbol char="F0A8" math="\Upsilon " /> + <symbol char="F0A9" math="\Phi " /> + <symbol char="F0AA" math="\Psi " /> + <symbol char="F0AD" math="\Omega " /> + <symbol char="F0AE" math="\alpha " /> + <symbol char="F0AF" math="\beta " /> + <symbol char="F0B0" math="\gamma " /> + <symbol char="F0B1" math="\delta " /> + <symbol char="F0B2" math="\epsilon " /> + <symbol char="F0B3" math="\zeta " /> + <symbol char="F0B4" math="\eta " /> + <symbol char="F0B5" math="\theta " /> + <symbol char="F0B6" math="\iota " /> + <symbol char="F0B7" math="\kappa " /> + <symbol char="F0B8" math="\lambda " /> + <symbol char="F0B9" math="\mu " /> + <symbol char="F0BA" math="\nu " /> + <symbol char="F0BB" math="\xi " /> + <symbol char="F0BC" math="\pi " /> + <symbol char="F0BD" math="\rho " /> + <symbol char="F0BE" math="\sigma " /> + <symbol char="F0BF" math="\tau " /> + <symbol char="F0C0" math="\upsilon " /> + <symbol char="F0C1" math="\phi " /> + <symbol char="F0C2" math="\xi " /> + <symbol char="F0C3" math="\psi " /> +</special-symbol-set> + +<!-- support for the Euclid fonts from Design Science --> + +<!-- Euclid is an ordinary unicode font, but we mark it for math mode --> +<special-symbol-set name="Euclid" fontenc="any" math-mode="true"> +</special-symbol-set> + +<!-- Euclid Extra is a 8-bit font containing some mathematical symbols --> +<special-symbol-set name="'Euclid Extra'" fontenc="any" eight-bit="true" math-mode="true"> + <symbol char="F03A" math="\sim " /> + <symbol char="F03B" math="\simeq " /> + <symbol char="F03C" math="\triangleleft " /> + <symbol char="F03D" math="\ll " /> + <symbol char="F03E" math="\triangleright " /> + <symbol char="F03F" math="\gg" /> + <symbol char="F040" math="\triangleeq " /> + <symbol char="F041" math="\stackrel{\wedge}{=}" /> + <symbol char="F042" math="\doteq " /> + <symbol char="F043" math="\coprod " /> + <symbol char="F044" math="\mathchar'26\mkern-10mu\lambda " /> + <symbol char="F049" math="\cap " /> + <symbol char="F04A" math="\mho " /> + <!-- in progress... --> +</special-symbol-set> + +<!-- Euclid Fraktur is an ordinary unicode font, but numbers are oldstyle and + letters (a-z) are fraktur --> +<special-symbol-set name="'Euclid Fraktur'" fontenc="any" math-mode="true"> + <symbol char="0030" math="\mathfrak{0}" /> + <symbol char="0031" math="\mathfrak{1}" /> + <symbol char="0032" math="\mathfrak{2}" /> + <symbol char="0033" math="\mathfrak{3}" /> + <symbol char="0034" math="\mathfrak{4}" /> + <symbol char="0035" math="\mathfrak{5}" /> + <symbol char="0036" math="\mathfrak{6}" /> + <symbol char="0037" math="\mathfrak{7}" /> + <symbol char="0038" math="\mathfrak{8}" /> + <symbol char="0039" math="\mathfrak{9}" /> + + <symbol char="0041" math="\mathfrak{A}" /> + <symbol char="0042" math="\mathfrak{B}" /> + <symbol char="0043" math="\mathfrak{C}" /> + <symbol char="0044" math="\mathfrak{D}" /> + <symbol char="0045" math="\mathfrak{E}" /> + <symbol char="0046" math="\mathfrak{F}" /> + <symbol char="0047" math="\mathfrak{G}" /> + <symbol char="0048" math="\mathfrak{H}" /> + <symbol char="0049" math="\mathfrak{I}" /> + <symbol char="004A" math="\mathfrak{J}" /> + <symbol char="004B" math="\mathfrak{K}" /> + <symbol char="004C" math="\mathfrak{L}" /> + <symbol char="004D" math="\mathfrak{M}" /> + <symbol char="004E" math="\mathfrak{N}" /> + <symbol char="004F" math="\mathfrak{O}" /> + + <symbol char="0050" math="\mathfrak{P}" /> + <symbol char="0051" math="\mathfrak{Q}" /> + <symbol char="0052" math="\mathfrak{R}" /> + <symbol char="0053" math="\mathfrak{S}" /> + <symbol char="0054" math="\mathfrak{T}" /> + <symbol char="0055" math="\mathfrak{U}" /> + <symbol char="0056" math="\mathfrak{V}" /> + <symbol char="0057" math="\mathfrak{W}" /> + <symbol char="0058" math="\mathfrak{X}" /> + <symbol char="0059" math="\mathfrak{Y}" /> + <symbol char="005A" math="\mathfrak{Z}" /> + + <symbol char="0061" math="\mathfrak{a}" /> + <symbol char="0062" math="\mathfrak{b}" /> + <symbol char="0063" math="\mathfrak{c}" /> + <symbol char="0064" math="\mathfrak{d}" /> + <symbol char="0065" math="\mathfrak{e}" /> + <symbol char="0066" math="\mathfrak{f}" /> + <symbol char="0067" math="\mathfrak{g}" /> + <symbol char="0068" math="\mathfrak{h}" /> + <symbol char="0069" math="\mathfrak{i}" /> + <symbol char="006A" math="\mathfrak{j}" /> + <symbol char="006B" math="\mathfrak{k}" /> + <symbol char="006C" math="\mathfrak{l}" /> + <symbol char="006D" math="\mathfrak{m}" /> + <symbol char="006E" math="\mathfrak{n}" /> + <symbol char="006F" math="\mathfrak{o}" /> + + <symbol char="0070" math="\mathfrak{p}" /> + <symbol char="0071" math="\mathfrak{q}" /> + <symbol char="0072" math="\mathfrak{r}" /> + <symbol char="0073" math="\mathfrak{s}" /> + <symbol char="0074" math="\mathfrak{t}" /> + <symbol char="0075" math="\mathfrak{u}" /> + <symbol char="0076" math="\mathfrak{v}" /> + <symbol char="0077" math="\mathfrak{w}" /> + <symbol char="0078" math="\mathfrak{x}" /> + <symbol char="0079" math="\mathfrak{y}" /> + <symbol char="007A" math="\mathfrak{z}" /> + +</special-symbol-set> + +<!-- Euclid Math One is an 8-bit font containing some mathematical symbols --> +<special-symbol-set name="'Euclid Math One'" fontenc="any" eight-bit="true" math-mode="true"> + <symbol char="F020" math="" /> <!-- ignore space --> + <symbol char="F021" math="\circleddash " /> + <symbol char="F022" math="\circledcirc " /> + <symbol char="F023" math="\circledast " /> + <symbol char="F024" math="\ominus " /> + <symbol char="F025" math="\oslash " /> + <symbol char="F026" math="\circledS " /> + <symbol char="F027" math="\boxminus " /> + <symbol char="F028" math="\boxplus " /> + <symbol char="F029" math="\boxtimes " /> + <symbol char="F02A" math="\boxdot " /> + <symbol char="F02B" math="\blacksquare " /> + <symbol char="F02C" math="\square " /> + <symbol char="F02D" math="\mathnormal{.}" /> + <symbol char="F030" math="\mathnormal{0}" /> + <symbol char="F031" math="\mathnormal{1}" /> + <symbol char="F032" math="\mathnormal{2}" /> + <symbol char="F033" math="\mathnormal{3}" /> + <symbol char="F034" math="\mathnormal{4}" /> + <symbol char="F035" math="\mathnormal{5}" /> + <symbol char="F036" math="\mathnormal{6}" /> + <symbol char="F037" math="\mathnormal{7}" /> + <symbol char="F038" math="\mathnormal{8}" /> + <symbol char="F039" math="\mathnormal{9}" /> + <symbol char="F041" math="\mathcal{A}" /> + <symbol char="F042" math="\mathcal{B}" /> + <symbol char="F043" math="\mathcal{C}" /> + <symbol char="F044" math="\mathcal{D}" /> + <symbol char="F045" math="\mathcal{E}" /> + <symbol char="F046" math="\mathcal{F}" /> + <symbol char="F047" math="\mathcal{G}" /> + <symbol char="F048" math="\mathcal{H}" /> + <symbol char="F049" math="\mathcal{I}" /> + <symbol char="F04A" math="\mathcal{J}" /> + <symbol char="F04B" math="\mathcal{K}" /> + <symbol char="F04C" math="\mathcal{L}" /> + <symbol char="F04D" math="\mathcal{M}" /> + <symbol char="F04E" math="\mathcal{N}" /> + <symbol char="F04F" math="\mathcal{O}" /> + <symbol char="F050" math="\mathcal{P}" /> + <symbol char="F051" math="\mathcal{Q}" /> + <symbol char="F052" math="\mathcal{R}" /> + <symbol char="F053" math="\mathcal{S}" /> + <symbol char="F054" math="\mathcal{T}" /> + <symbol char="F055" math="\mathcal{U}" /> + <symbol char="F056" math="\mathcal{V}" /> + <symbol char="F057" math="\mathcal{W}" /> + <symbol char="F058" math="\mathcal{X}" /> + <symbol char="F059" math="\mathcal{Y}" /> + <symbol char="F05A" math="\mathcal{Z}" /> + <symbol char="F080" math="\smallsetminus " /> + <symbol char="F081" math="\setminus " /> + <symbol char="F082" math="\diagdown " /> + <symbol char="F083" math="\diagup " /> + <!-- todo: box drawing corners F084-F087 --> + <symbol char="F088" math="\shortmid " /> + <symbol char="F089" math="\shortparallel " /> + <symbol char="F08A" math="\nshortmid " /> + <symbol char="F08B" math="\nshortparallel " /> + <symbol char="F08C" math="\nmid " /> + <symbol char="F08D" math="\nparallel " /> + <symbol char="F090" math="\vdash " /> + <symbol char="F091" math="\vDash " /> + <symbol char="F092" math="\Vdash " /> + <symbol char="F093" math="\Vvdash " /> + <symbol char="F094" math="\dashv " /> + <symbol char="F095" math="\top" /> + <symbol char="F096" math="\nvdash " /> + <symbol char="F097" math="\nvDash " /> + <symbol char="F098" math="\nVdash " /> + <symbol char="F099" math="\not\Vvdash " /> <!-- does not look good --> + <symbol char="F0A0" math="" /> <!-- ignore space --> + <symbol char="F0A1" math="\circeq " /> + <symbol char="F0A2" math="\eqcirc " /> + <symbol char="F0A3" math="\risingdotseq " /> + <symbol char="F0A4" math="\Doteq " /> + <symbol char="F0A5" math="\fallingdotseq " /> + <symbol char="F0A6" math="\bumpeq " /> + <symbol char="F0A7" math="\Bumpeq " /> + <symbol char="F0A8" math="\multimap " /> + <symbol char="F0A9" math="\asymp " /> + <symbol char="F0AA" math="\wr " /> + <symbol char="F0AB" math="\eqsim " /> + <symbol char="F0AC" math="\approxeq " /> + <symbol char="F0AD" math="\nsim " /> + <symbol char="F0AE" math="\ncong " /> + <symbol char="F0B0" math="\rightarrowtail " /> + <symbol char="F0B1" math="\leftarrowtail " /> + <symbol char="F0B2" math="\twoheadrightarrow " /> + <symbol char="F0B3" math="\twoheadleftarrow " /> + <symbol char="F0B4" math="\leftharpoonup " /> + <symbol char="F0B5" math="\leftharpoondown " /> + <symbol char="F0B6" math="\rightharpoonup " /> + <symbol char="F0B7" math="\rightharpoondown " /> + <symbol char="F0B8" math="\upharpoonright " /> + <symbol char="F0B9" math="\downharpoonright " /> + <symbol char="F0BA" math="\upharpoonleft " /> + <symbol char="F0BB" math="\downharpoonleft " /> + <symbol char="F0BC" math="\nleftarrow " /> + <symbol char="F0BD" math="\nrightarrow " /> + <symbol char="F0BE" math="\nLeftarrow " /> + <symbol char="F0BF" math="\nRightarrow " /> + <symbol char="F0C0" math="\nleftrightarrow " /> + <symbol char="F0C1" math="\nLeftrightarrow " /> + <symbol char="F0C2" math="\leftleftarrows " /> + <symbol char="F0C3" math="\rightrightarrows " /> + <symbol char="F0C4" math="\upuparrows " /> + <symbol char="F0C5" math="\downdownarrows " /> + <symbol char="F0C6" math="\leftrightarrows " /> + <symbol char="F0C7" math="\leftrightharpoons " /> + <symbol char="F0C8" math="\Rrightarrow " /> + <symbol char="F0C9" math="\Lleftarrow " /> + <symbol char="F0CA" math="\Rsh " /> + <symbol char="F0CB" math="\Lsh " /> + <symbol char="F0CC" math="\looparrowright " /> + <symbol char="F0CD" math="\looparrowleft " /> + <symbol char="F0CE" math="\rightsquigarrow " /> + <symbol char="F0CF" math="\leftrightsquigarrow " /> + <symbol char="F0D0" math="\curvearrowright " /> + <symbol char="F0D1" math="\curvearrowleft " /> + <symbol char="F0D2" math="\circlearrowright " /> + <symbol char="F0D3" math="\circlearrowleft " /> + <symbol char="F0E0" math="\leftthreetimes " /> + <symbol char="F0E1" math="\rightthreetimes " /> + <symbol char="F0E2" math="\ltimes " /> + <symbol char="F0E3" math="\rtimes " /> + <symbol char="F0E4" eq-char="2720" /> <!-- maltese cross --> + <symbol char="F0E5" math="\star " /> + <symbol char="F0E6" math="\divideontimes " /> + <symbol char="F0E7" math="\flat " /> + <symbol char="F0E8" math="\natural " /> + <symbol char="F0E9" math="\sharp " /> + <symbol char="F0EA" math="\dotplus " /> + <symbol char="F0F0" math="\complement " /> + <symbol char="F0F1" math="\varrho " /> + <symbol char="F0F2" math="\epsilon " /> + <symbol char="F0F3" math="\nexists " /> + <symbol char="F0F4" math="\digamma " /> + <symbol char="F0F5" math="\Finv " /> + <symbol char="F0F6" math="\Game " /> + <symbol char="F0F7" math="\imath " /> + <symbol char="F0F8" math="\jmath " /> + <symbol char="F0F9" math="\varkappa " /> + <symbol char="F0FA" math="\top " /> + <symbol char="F0FB" math="\beth " /> + <symbol char="F0FC" math="\gimel " /> + <symbol char="F0FD" math="\daleth " /> + <symbol char="F0FE" math="\eth " /> + <symbol char="F0FF" math="\hbar " /> +</special-symbol-set> + +<!-- Euclid Math Two is an 8-bit font containing some mathematical symbols --> +<special-symbol-set name="'Euclid Math Two'" fontenc="any" eight-bit="true" math-mode="true"> + <symbol char="F020" math="" /> <!-- ignore space --> + <symbol char="F021" math="\vartriangle " /> + <symbol char="F022" math="\triangledown " /> + <symbol char="F023" math="\blacktriangle " /> + <symbol char="F024" math="\blacktriangledown" /> + <symbol char="F025" math="\blacktriangleright " /> + <symbol char="F026" math="\blacktriangleleft " /> + <symbol char="F027" math="\trianglerighteq " /> + <symbol char="F028" math="\trianglelefteq " /> + <symbol char="F029" math="\ntriangleright " /> + <symbol char="F02A" math="\ntriangleleft " /> + <symbol char="F02B" math="\ntrianglerighteq " /> + <symbol char="F02C" math="\ntrianglelefteq " /> + <symbol char="F041" math="\mathbb{A}" /> + <symbol char="F042" math="\mathbb{B}" /> + <symbol char="F043" math="\mathbb{C}" /> + <symbol char="F044" math="\mathbb{D}" /> + <symbol char="F045" math="\mathbb{E}" /> + <symbol char="F046" math="\mathbb{F}" /> + <symbol char="F047" math="\mathbb{G}" /> + <symbol char="F048" math="\mathbb{H}" /> + <symbol char="F049" math="\mathbb{I}" /> + <symbol char="F04A" math="\mathbb{J}" /> + <symbol char="F04B" math="\mathbb{K}" /> + <symbol char="F04C" math="\mathbb{L}" /> + <symbol char="F04D" math="\mathbb{M}" /> + <symbol char="F04E" math="\mathbb{N}" /> + <symbol char="F04F" math="\mathbb{O}" /> + <symbol char="F050" math="\mathbb{P}" /> + <symbol char="F051" math="\mathbb{Q}" /> + <symbol char="F052" math="\mathbb{R}" /> + <symbol char="F053" math="\mathbb{S}" /> + <symbol char="F054" math="\mathbb{T}" /> + <symbol char="F055" math="\mathbb{U}" /> + <symbol char="F056" math="\mathbb{V}" /> + <symbol char="F057" math="\mathbb{W}" /> + <symbol char="F058" math="\mathbb{X}" /> + <symbol char="F059" math="\mathbb{Y}" /> + <symbol char="F05A" math="\mathbb{Z}" /> + <!-- F06B black board bold small letter k ? --> + <symbol char="F080" math="\lessdot " /> + <symbol char="F081" math="\gtrdot " /> + <symbol char="F082" math="\eqslantless " /> + <symbol char="F083" math="\eqslantgtr " /> + <symbol char="F084" math="\leqslant " /> + <symbol char="F085" math="\geqslant " /> + <symbol char="F086" math="\leqq " /> + <symbol char="F087" math="\geqq " /> + <symbol char="F088" math="\lesssim " /> + <symbol char="F089" math="\gtrsim " /> + <symbol char="F08A" math="\lessapprox " /> + <symbol char="F08B" math="\gtrapprox " /> + <symbol char="F08C" math="\lll " /> + <symbol char="F08D" math="\ggg " /> + <symbol char="F08E" math="\nless " /> + <symbol char="F08F" math="\ngtr " /> + <symbol char="F090" math="\lneq " /> + <symbol char="F091" math="\gneq " /> + <symbol char="F092" math="\nleq " /> + <symbol char="F093" math="\ngeq " /> + <symbol char="F094" math="\not\eqslantless " /> + <symbol char="F095" math="\not\eqslantgtr " /> + <symbol char="F096" math="\nleqslant " /> + <symbol char="F097" math="\ngeqslant " /> + <symbol char="F098" math="\lneqq " /> + <symbol char="F099" math="\gneqq " /> + <symbol char="F09A" math="\lvertneqq " /> + <symbol char="F09B" math="\gvertneqq " /> + <symbol char="F09C" math="\nleqq " /> + <symbol char="F09D" math="\ngeqq " /> + <symbol char="F09E" math="\lnsim " /> + <symbol char="F09F" math="\gnsim " /> + <symbol char="F0A0" math="" /> <!-- ignore space --> + <symbol char="F0A1" math="\lnapprox " /> + <symbol char="F0A2" math="\gnapprox " /> + <symbol char="F0A3" math="\lessgtr " /> + <symbol char="F0A4" math="\gtrless " /> + <symbol char="F0A5" math="\lesseqgtr " /> + <symbol char="F0A6" math="\gtreqless " /> + <symbol char="F0A7" math="\lesseqqgtr " /> + <symbol char="F0A8" math="\gtreqqless " /> + <symbol char="F0A9" math="\veebar " /> + <symbol char="F0AA" math="\barwedge " /> + <symbol char="F0AB" math="\bar{\bar{\wedge}} " /> + <symbol char="F0B0" math="\preceq " /> + <symbol char="F0B1" math="\succeq " /> + <symbol char="F0B2" math="\curlyeqprec " /> + <symbol char="F0B3" math="\curlyeqsucc " /> + <symbol char="F0B4" math="\preccurlyeq " /> + <symbol char="F0B5" math="\succcurlyeq " /> + <symbol char="F0B6" math="\precsim " /> + <symbol char="F0B7" math="\succsim " /> + <symbol char="F0B8" math="\precapprox " /> + <symbol char="F0B9" math="\succapprox " /> + <symbol char="F0BA" math="\nprec " /> + <symbol char="F0BB" math="\nsucc " /> + <symbol char="F0BC" math="\precneqq " /> <!-- should be \precneq --> + <symbol char="F0BD" math="\succneqq " /> <!-- should be \succneq --> + <symbol char="F0BE" math="\npreceq " /> + <symbol char="F0BF" math="\nsucceq " /> + <!-- F0C0-F0C3 are not correct: should have vertical bar --> + <symbol char="F0C0" math="\not\curlyeqprec " /> + <symbol char="F0C1" math="\not\curlyeqsucc " /> + <symbol char="F0C2" math="\not\preccurlyeq " /> + <symbol char="F0C3" math="\not\succcurlyeq " /> + <symbol char="F0C4" math="\precneqq " /> + <symbol char="F0C5" math="\succneqq " /> + <symbol char="F0C6" math="\precnsim " /> + <symbol char="F0C7" math="\succnsim " /> + <symbol char="F0C8" math="\precnapprox " /> + <symbol char="F0C9" math="\succnapprox " /> + <symbol char="F0CA" math="\curlywedge " /> + <symbol char="F0CB" math="\curlyvee " /> + <symbol char="F0D0" math="\Subset " /> + <symbol char="F0D1" math="\Supset " /> + <symbol char="F0D2" math="\Cup " /> + <symbol char="F0D3" math="\Cap " /> + <symbol char="F0D4" math="\subseteqq " /> + <symbol char="F0D5" math="\supseteqq " /> + <symbol char="F0D6" math="\subsetneq " /> + <symbol char="F0D7" math="\supsetneq " /> + <symbol char="F0D8" math="\varsubsetneq " /> + <symbol char="F0D9" math="\varsupsetneq " /> + <symbol char="F0DA" math="\nsubseteq " /> + <symbol char="F0DB" math="\nsupseteq " /> + <symbol char="F0DC" math="\subsetneqq " /> + <symbol char="F0DD" math="\supsetneqq " /> + <symbol char="F0DE" math="\varsubsetneqq " /> + <symbol char="F0DF" math="\varsupsetneqq " /> + <symbol char="F0E0" math="\nsubseteqq " /> + <symbol char="F0E1" math="\nsupseteqq " /> + <symbol char="F0E2" math="\uplus " /> + <symbol char="F0E3" math="\pitchfork " /> + <symbol char="F0E4" math="\in " /> + <symbol char="F0E5" math="\ni " /> + <symbol char="F0F0" math="\sqsubset " /> + <symbol char="F0F1" math="\sqsupset " /> + <symbol char="F0F2" math="\sqcup " /> + <symbol char="F0F3" math="\sqcap " /> + <symbol char="F0F4" math="\sqsubseteq " /> + <symbol char="F0F5" math="\sqsupseteq " /> +</special-symbol-set> + +<!-- Euclid Symbol is a 16-bit non-unicode font containing various symbols, + e.g. greek letters as symbols --> +<special-symbol-set name="'Euclid Symbol'" fontenc="any" eight-bit="true" math-mode="true"> +<!-- TODO --> +</special-symbol-set> + +<!-- support for phonetic font "IPAKielSeven" --> +<special-symbol-set name="IPAKielSeven" requires="tipa" fontenc="T3" eight-bit="true"> + <symbol char="F041" text="A" /> + <symbol char="F042" text="B" /> + <symbol char="F043" text="\c{c}" /> + <symbol char="F044" text="D" /> + <symbol char="F045" text="\textschwa " /> + <symbol char="F046" text="F" /> + <symbol char="F047" text="\;G" /> + <symbol char="F048" text="H" /> + <symbol char="F049" text="I" /> + <symbol char="F04a" text="J" /> + <symbol char="F04b" text="\j" /> + <symbol char="F04c" text="\textlyoghlig " /> + <symbol char="F04d" text="M" /> + <symbol char="F04e" text="N" /> + <symbol char="F04f" text="O" /> + <symbol char="F050" text="\textclosereversepsilon " /> + <symbol char="F051" text="\textnormal{?}" /> + <symbol char="F052" text="\;R" /> + <symbol char="F053" text="S" /> + <symbol char="F054" text="T" /> + <symbol char="F055" text="\textturnm " /> + <symbol char="F056" text="G" /> + <symbol char="F057" text="\;W" /> + <symbol char="F058" text="X" /> + <symbol char="F059" text="Y" /> + <symbol char="F05a" text="Z" /> + <symbol char="F041" text="a" /> + <symbol char="F062" text="b" /> + <symbol char="F063" text="c" /> + <symbol char="F064" text="d" /> + <symbol char="F065" text="e" /> + <symbol char="F066" text="f" /> + <symbol char="F067" text="g" /> + <symbol char="F068" text="h" /> + <symbol char="F069" text="i" /> + <symbol char="F06a" text="j" /> + <symbol char="F06b" text="k" /> + <symbol char="F06c" text="l" /> + <symbol char="F06d" text="m" /> + <symbol char="F06e" text="n" /> + <symbol char="F06f" text="o" /> + <symbol char="F070" text="p" /> + <symbol char="F071" text="q" /> + <symbol char="F072" text="r" /> + <symbol char="F073" text="s" /> + <symbol char="F074" text="t" /> + <symbol char="F075" text="u" /> + <symbol char="F076" text="v" /> + <symbol char="F077" text="w" /> + <symbol char="F078" text="x" /> + <symbol char="F079" text="y" /> + <symbol char="F07a" text="z" /> + <symbol char="F081" text="\textturnscripta " /> + <symbol char="F0f5" text="\;B" /> + <symbol char="F082" text="C" /> + <symbol char="F0eb" text="\texthtd " /> + <symbol char="F0ab" text="\textreve " /> + <symbol char="F0ec" text="\texthtscg " /> + <symbol char="F0fd" text="\;H" /> + <symbol char="F0ee" text="\textsuperscript{h}" /> + <symbol char="F0f6" text="\textbari " /> + <symbol char="F0ef" text="\textbardotlessj " /> + <symbol char="F0f0" text="\:l" /> + <symbol char="F0f1" text="\textbeltl " /> + <symbol char="F0e5" text="\textturnmrleg " /> + <symbol char="F0f7" text="\textsuperscript{n}" /> + <symbol char="F0af" text="\textturnv " /> + <symbol char="F0b8" text="\textrevepsilon " /> + <symbol char="F0ce" text="\textscoelig " /> + <symbol char="F0e4" text="\textinvscr " /> + <symbol char="F0ea" text="\texththeng " /> + <symbol char="F0cb" text="\textsuper{\texttheta}" /> + <symbol char="F0ac" text="\textbaru " /> + <symbol char="F0d7" text="\textsuperscript{\textgamma}" /> + <symbol char="F0b9" text="\textsc{\texttheta}" /> + <symbol char="F0ca" text="\textundercross " /> + <symbol char="F09d" text="\textsuperscript{u}" /> + <symbol char="F0fc" text="\textctz " /> + <symbol char="F08c" text="\textturna " /> + <symbol char="F0ba" text="\texthtb " /> + <symbol char="F08d" text="\textbottomtiebar " /> + <symbol char="F0d9" text="\textrtaild " /> + <symbol char="F0e3" text="\o " /> + <symbol char="F0f4" text="\texthtg " /> + <symbol char="F0a9" text="\textcrh " /> + <symbol char="F0fa" text="\texthtbardotlessj " /> + <symbol char="F092" text="i" /> + <symbol char="F0c6" text="\textsuperscript{j}" /> + <symbol char="F0fb" text="\textscl " /> + <symbol char="F0c2" text="\textsuperscript{l}" /> + <symbol char="F04d" text="\textltailm " /> + <symbol char="F0d0" text="\textscn " /> + <symbol char="F0bf" text="\textramshorns " /> + <symbol char="F0d3" text="\textepsilon " /> + <symbol char="F0cf" text="\ae " /> + <symbol char="F0a8" text="\textturnr " /> + <symbol char="F0a7" text="\textrtails " /> + <symbol char="F0e6" text="\textrtailt " /> + <symbol char="F0e7" text="\textupsilon " /> + <symbol char="F0c3" text="\textscriptv " /> + <symbol char="F085" text="\textturnw " /> + <symbol char="F0c5" text="\textsuperscript{x}" /> + <symbol char="F0b4" text="\textturny " /> + <symbol char="F0bd" text="\textrtailz " /> + <symbol char="F096" text="\textpipe " /> + <symbol char="F031" text="\textsubbridge{}" /> + <symbol char="F032" text="\textsubbar{}" /> + <symbol char="F033" text="\textsubwedge{}" /> + <symbol char="F034" text="\textsubsquare " /> + <symbol char="F035" text="\textadvancing{}" /> + <symbol char="F036" text="\textraising{}" /> + <symbol char="F037" text="\c{}" /> + <symbol char="F038" text="\textsubarch{]}" /> + <symbol char="F039" text="\textsubring{}" /> + <symbol char="F030" text="\textsubtilde{}" /> + <symbol char="F02d" text="-" dashes="true" /> + <symbol char="F03d" text="\textrtailn " /> + <symbol char="F084" text="\textdoublepipe " /> + <symbol char="F021" text="\'{}" /> + <symbol char="F040" text="\={}" /> + <symbol char="F023" text="\v{}" /> + <symbol char="F024" text="\^{}" /> + <symbol char="F02a" text="\u{}" /> + <symbol char="F028" text="\r{}" /> + <symbol char="F029" text="\~{}" /> + <symbol char="F05f" text="\H{}" /> + <symbol char="F02b" text="\textovercross{}" /> + <symbol char="F07e" text="\`{}" /> + <symbol char="F0a1" text="\texttoptiebar{}" /> + <symbol char="F0e1" text="\"{}" /> + <symbol char="F0d1" text="\H*{}" /> + <symbol char="F0b1" text="\rhoticity " /> + <symbol char="F060" text="\textsecstress{}" /> + <symbol char="F0c1" text="\textinvsubbridge{}" /> + <symbol char="F0aa" text="\textsubplus{}" /> + <symbol char="F0a3" text="\textseagull{}" /> + <symbol char="F0a2" text="\textsubdot{}" /> + <symbol char="F0b0" text="\textretracting{}" /> + <symbol char="F0a4" text="\textlowering{}" /> + <symbol char="F0a6" text="\textpolhook{}" /> + <symbol char="F0a5" text="\textbottomtiebar{}" /> + <symbol char="F0bb" text="\textsubumlaut{}" /> + <symbol char="F0bc" text="\textbar{}" /> + <symbol char="F0f3" text="=" /> + <symbol char="F0ad" text="\textltailn " /> + <symbol char="F02c" text="," /> + <symbol char="F02e" text="." /> + <symbol char="F02f" text="\textglotstop " /> + <symbol char="F05b" text="[" /> + <symbol char="F05d" text="]" /> + <symbol char="F03b" text=";" /> + <symbol char="F027" text="'" /> + <symbol char="F05c" text="/" /> + <symbol char="F03c" text="!" /> + <symbol char="F03e" text="\textbullseye " /> + <symbol char="F03f" text="\textrevglotstop " /> + <symbol char="F07b" text="\oe " /> + <symbol char="F07d" text="\textcorner " /> + <symbol char="F03a" text="\textltilde " /> + <symbol char="F022" text="\textprimstress " /> + <symbol char="F07c" text="\textfishhookr " /> + <symbol char="F0f8" text="\textdoublebarpipe " /> + <symbol char="F0f9" text="\textvertline " /> + <symbol char="F0c0" text="\textbarrevglotstop " /> + <symbol char="F0ff" text="\" /> + <symbol char="F0d5" text="\textturnrrtail " /> + <symbol char="F0f2" text="\texthalflength " /> + <symbol char="F0ae" text="\textsecstress " /> + <symbol char="F0c8" text="\textturnlonglegr " /> + <symbol char="F0f8" text="\textdoublevertline " /> + <symbol char="F0f9" text="\textsuperscript{\textrevglotstop}" /> + <symbol char="F0c0" text="\textbarglotstop " /> + <symbol char="F0ff" text="(" /> + <symbol char="F0d5" text=")" /> + <symbol char="F0f2" text="\textlengthmark " /> + <symbol char="F0ae" text="`" /> + <symbol char="F0c8" text="\textrtailr " /> +</special-symbol-set> + +<!-- support for phonetic font "IPAExtras" --> +<special-symbol-set name="IPAExtras" requires="tipa" fontenc="T3"> + <symbol char="F041" text="\textturncelig " /> + <symbol char="F0ee" text="\textsuperscript{h}" /> + <symbol char="F0f6" text="\textiota " /> + <symbol char="F061" text="\ensuremath{\alpha}" /> + <symbol char="F062" text="\={}" /> + <symbol char="F063" text="\texthtc " /> + <symbol char="F064" text="\textsuperimposetilde{d}" /> + <symbol char="F065" text="\textrhookschwa " /> + <symbol char="F066" text="\textbardotlessj " /> + <symbol char="F067" text="\textg " /> + <symbol char="F068" text="\textturnh " /> + <symbol char="F069" text="\textiota " /> + <symbol char="F06a" text="\^{j}\textrthook " /> + <symbol char="F06b" text="\texthtk " /> + <symbol char="F06e" text="\textnrleg " /> + <symbol char="F06f" text="\textcloseomega " /> + <symbol char="F070" text="\texthtp " /> + <symbol char="F071" text="\texthtq " /> + <symbol char="F072" text="\textlonglegr " /> + <symbol char="F074" text="\texthtt " /> + <symbol char="F075" text="\textscriptv " /> + <symbol char="F076" text="\textacute " /> + <symbol char="F077" text="\.{}" /> + <symbol char="F078" text="\ensuremath{^{^{\infty}}}" /> + <symbol char="F07a" text="\v{z}" /> +</special-symbol-set> + +</symbols> + + + +<!-- +These symbols have been replaced by simpler versions (eg. c/o and \frac13) +Should maybe be reintroduced later (via an option). +<symbol char="2100" text="\textvulgarfrac{a}{c}" /> +<symbol char="2101" text="\textvulgarfrac{a}{s}" /> +<symbol char="2105" text="\textvulgarfrac{c}{o}" /> +<symbol char="2106" text="\textvulgarfrac{c}{u}" /> +<symbol char="2153" text="\textvulgarfrac13"/> +<symbol char="2154" text="\textvulgarfrac23"/> +<symbol char="2155" text="\textvulgarfrac15"/> +<symbol char="2156" text="\textvulgarfrac25"/> +<symbol char="2157" text="\textvulgarfrac35"/> +<symbol char="2158" text="\textvulgarfrac45"/> +<symbol char="2159" text="\textvulgarfrac16"/> +<symbol char="215A" text="\textvulgarfrac56"/> +<symbol char="215B" text="\textvulgarfrac18"/> +<symbol char="215C" text="\textvulgarfrac38"/> +<symbol char="215D" text="\textvulgarfrac58"/> +<symbol char="215E" text="\textvulgarfrac78"/> +<symbol char="215F" text="\textvulgarfrac1{ }"/> +--> diff --git a/source/java/writer2latex/latex/util/BeforeAfter.java b/source/java/writer2latex/latex/util/BeforeAfter.java new file mode 100644 index 0000000..3cd211e --- /dev/null +++ b/source/java/writer2latex/latex/util/BeforeAfter.java @@ -0,0 +1,75 @@ +/************************************************************************ + * + * BeforeAfter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-12-02) + * + */ + +package writer2latex.latex.util; + +/** Utility class to hold LaTeX code to put before/after other LaTeX code + */ +public class BeforeAfter { + private String sBefore=""; + private String sAfter=""; + + /** Constructor to initialize the object with a pair of strings + * @param sBefore1 LaTeX code to put before + * @param sAfter1 LaTeX code to put after + */ + public BeforeAfter(String sBefore1, String sAfter1) { + sBefore=sBefore1; sAfter=sAfter1; + } + + /** Default constructor: Create with empty strings + */ + public BeforeAfter() { } + + /** <p>Add data to the <code>BeforeAfter</code></p> + * <p>The new data will be be added "inside", thus for example</p> + * <ul><li><code>add("\textsf{","}");</code> + * <li><code>add("\textit{","}");</code></ul> + * <p>will create the pair <code>\textsf{\textit{</code>, <code>}}</code></p> + * + * @param sBefore1 LaTeX code to put before + * @param sAfter1 LaTeX code to put after + */ + public void add(String sBefore1, String sAfter1) { + sBefore+=sBefore1; sAfter=sAfter1+sAfter; + } + + /** Get LaTeX code to put before + * @return then LaTeX code + */ + public String getBefore() { return sBefore; } + + /** Get LaTeX code to put after + * @return then LaTeX code + */ + public String getAfter() { return sAfter; } + + /** Check if this <code>BeforeAfter</code> contains any data + * @return true if there is data in at least one part + */ + public boolean isEmpty() { return sBefore.length()==0 && sAfter.length()==0; } + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/util/Context.java b/source/java/writer2latex/latex/util/Context.java new file mode 100644 index 0000000..d963e37 --- /dev/null +++ b/source/java/writer2latex/latex/util/Context.java @@ -0,0 +1,310 @@ +/************************************************************************ + * + * Context.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2007-11-23) + * + */ + +package writer2latex.latex.util; + +import writer2latex.office.XMLString; +import writer2latex.office.StyleWithProperties; + +/** <p>LaTeX code is in general very context dependent. This class tracks the + * current context, which is the used by the converter to create valid and + * optimal LaTeX code.</p> + */ +public class Context { + + // *** Formatting Info (current values in the source OOo document) *** + + // Current list style + private String sListStyleName = null; + + // Current background color + private String sBgColor = null; + + // Current character formatting attributes + private String sFontName = null; + private String sFontStyle = null; + private String sFontVariant = null; + private String sFontWeight = null; + private String sFontSize = null; + private String sFontColor = null; + private String sLang = null; + private String sCountry = null; + + // *** Structural Info (identifies contructions in the LaTeX document) *** + + // within the header or footer of a pagestyle + private boolean bInHeaderFooter = false; + + // within a table cell + private boolean bInTable = false; // any column + private boolean bInLastTableColumn = false; // last column + private boolean bInSimpleTable = false; // l, c or r-column + + // within a multicols environment + private boolean bInMulticols = false; + + // within a list of this level + private int nListLevel = 0; + + // within a section command + private boolean bInSection = false; + + // within a caption + private boolean bInCaption = false; + + // within a floating figure (figure environment) + private boolean bInFigureFloat = false; + + // within a floating table (table envrionment) + private boolean bInTableFloat = false; + + // within a minipage environment + private boolean bInFrame = false; + + // within a \footnote or \endnote + private boolean bInFootnote = false; + + // in verbatim mode + private boolean bVerbatim = false; + + // in math mode + private boolean bMathMode = false; + + // *** Special Info *** + + // Inside (inline) verbatim text, where line breaks are disallowed + private boolean bNoLineBreaks = false; + + // Inside a construction, where footnotes are disallowed + private boolean bNoFootnotes = false; + + // Inside an area, where lists are ignored + private boolean bIgnoreLists = false; + + // *** Accessor Methods *** + + public void setBgColor(String sBgColor) { this.sBgColor = sBgColor; } + + public String getBgColor() { return sBgColor; } + + public void setListStyleName(String sListStyleName) { this.sListStyleName = sListStyleName; } + + public String getListStyleName() { return sListStyleName; } + + public void setFontName(String sFontName) { this.sFontName = sFontName; } + + public String getFontName() { return sFontName; } + + public void setFontStyle(String sFontStyle) { this.sFontStyle = sFontStyle; } + + public String getFontStyle() { return sFontStyle; } + + public void setFontVariant(String sFontVariant) { this.sFontVariant = sFontVariant; } + + public String getFontVariant() { return sFontVariant; } + + public void setFontWeight(String sFontWeight) { this.sFontWeight = sFontWeight; } + + public String getFontWeight() { return sFontWeight; } + + public void setFontSize(String sFontSize) { this.sFontSize = sFontSize; } + + public String getFontSize() { return sFontSize; } + + public void setFontColor(String sFontColor) { this.sFontColor = sFontColor; } + + public String getFontColor() { return sFontColor; } + + public void setLang(String sLang) { this.sLang = sLang; } + + public String getLang() { return sLang; } + + public void setCountry(String sCountry) { this.sCountry = sCountry; } + + public String getCountry() { return sCountry; } + + public void setInHeaderFooter(boolean bInHeaderFooter) { + this.bInHeaderFooter = bInHeaderFooter; + } + + public boolean isInHeaderFooter() { return bInHeaderFooter; } + + public void setInTable(boolean bInTable) { this.bInTable = bInTable; } + + public boolean isInTable() { return bInTable; } + + public void setInLastTableColumn(boolean bInLastTableColumn) { this.bInLastTableColumn = bInLastTableColumn; } + + public boolean isInLastTableColumn() { return bInLastTableColumn; } + + public void setInSimpleTable(boolean bInSimpleTable) { this.bInSimpleTable = bInSimpleTable; } + + public boolean isInSimpleTable() { return bInSimpleTable; } + + public void setInMulticols(boolean bInMulticols) { + this.bInMulticols = bInMulticols; + } + + public boolean isInMulticols() { return bInMulticols; } + + public void setListLevel(int nListLevel) { this.nListLevel = nListLevel; } + + public void incListLevel() { nListLevel++; } + + public int getListLevel() { return nListLevel; } + + public void setInSection(boolean bInSection) { this.bInSection = bInSection; } + + public boolean isInSection() { return bInSection; } + + public void setInCaption(boolean bInCaption) { this.bInCaption = bInCaption; } + + public boolean isInCaption() { return bInCaption; } + + public void setInFigureFloat(boolean bInFigureFloat) { this.bInFigureFloat = bInFigureFloat; } + + public boolean isInFigureFloat() { return bInFigureFloat; } + + public void setInTableFloat(boolean bInTableFloat) { this.bInTableFloat = bInTableFloat; } + + public boolean isInTableFloat() { return bInTableFloat; } + + public void setInFrame(boolean bInFrame) { this.bInFrame = bInFrame; } + + public boolean isInFrame() { return bInFrame; } + + public void setInFootnote(boolean bInFootnote) { + this.bInFootnote = bInFootnote; + } + + public boolean isInFootnote() { return bInFootnote; } + + public void setNoFootnotes(boolean bNoFootnotes) { + this.bNoFootnotes = bNoFootnotes; + } + + public boolean isNoFootnotes() { return bNoFootnotes; } + + public void setIgnoreLists(boolean bIgnoreLists) { + this.bIgnoreLists = bIgnoreLists; + } + + public boolean isIgnoreLists() { return bIgnoreLists; } + + public void setNoLineBreaks(boolean bNoLineBreaks) { + this.bNoLineBreaks = bNoLineBreaks; + } + public boolean isNoLineBreaks() { return bNoLineBreaks; } + + public boolean isVerbatim() { return bVerbatim; } + + public void setVerbatim(boolean bVerbatim) { this.bVerbatim = bVerbatim; } + + public boolean isMathMode() { return bMathMode; } + + public void setMathMode(boolean bMathMode) { this.bMathMode = bMathMode; } + + // update context + + public void updateFormattingFromStyle(StyleWithProperties style) { + String s; + + if (style==null) { return; } + + s = style.getProperty(XMLString.STYLE_FONT_NAME); + if (s!=null) { setFontName(s); } + + s = style.getProperty(XMLString.FO_FONT_STYLE); + if (s!=null) { setFontStyle(s); } + + s = style.getProperty(XMLString.FO_FONT_VARIANT); + if (s!=null) { setFontVariant(s); } + + s = style.getProperty(XMLString.FO_FONT_WEIGHT); + if (s!=null) { setFontWeight(s); } + + s = style.getProperty(XMLString.FO_FONT_SIZE); + if (s!=null) { setFontSize(s); } + + s = style.getProperty(XMLString.FO_COLOR); + if (s!=null) { setFontColor(s); } + + s = style.getProperty(XMLString.FO_LANGUAGE); + if (s!=null) { setLang(s); } + + s = style.getProperty(XMLString.FO_COUNTRY); + if (s!=null) { setCountry(s); } + } + + public void resetFormattingFromStyle(StyleWithProperties style) { + setFontName(null); + setFontStyle(null); + setFontVariant(null); + setFontWeight(null); + setFontSize(null); + setFontColor(null); + setLang(null); + setCountry(null); + updateFormattingFromStyle(style); + } + + + // clone this Context + public Object clone() { + Context newContext = new Context(); + + newContext.setListStyleName(sListStyleName); + newContext.setBgColor(sBgColor); + newContext.setFontName(sFontName); + newContext.setFontStyle(sFontStyle); + newContext.setFontVariant(sFontVariant); + newContext.setFontWeight(sFontWeight); + newContext.setFontSize(sFontSize); + newContext.setFontColor(sFontColor); + newContext.setLang(sLang); + newContext.setCountry(sCountry); + newContext.setInHeaderFooter(bInHeaderFooter); + newContext.setInTable(bInTable); + newContext.setInLastTableColumn(bInLastTableColumn); + newContext.setInSimpleTable(bInSimpleTable); + newContext.setInMulticols(bInMulticols); + newContext.setListLevel(nListLevel); + newContext.setInSection(bInSection); + newContext.setInCaption(bInCaption); + newContext.setInFigureFloat(bInFigureFloat); + newContext.setInTableFloat(bInTableFloat); + newContext.setInFrame(bInFrame); + newContext.setInFootnote(bInFootnote); + newContext.setVerbatim(bVerbatim); + newContext.setMathMode(bMathMode); + newContext.setNoFootnotes(bNoFootnotes); + newContext.setIgnoreLists(bIgnoreLists); + newContext.setNoLineBreaks(bNoLineBreaks); + + return newContext; + } + +} diff --git a/source/java/writer2latex/latex/util/HeadingMap.java b/source/java/writer2latex/latex/util/HeadingMap.java new file mode 100644 index 0000000..2637c75 --- /dev/null +++ b/source/java/writer2latex/latex/util/HeadingMap.java @@ -0,0 +1,69 @@ +/************************************************************************ + * + * HeadingMap.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2006 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2006-11-02) + * + */ + +package writer2latex.latex.util; + +/** This class contains data for the mapping of OOo headings to LaTeX headings. + A LaTeX heading is characterized by a name and a level. + The heading is inserted with \name{...} or \name[...]{...} + The headings are supposed to be "normal" LaTeX headings, + ie. the names are also counter names, and the headings + can be reformatted using \@startsection etc. + Otherwise max-level should be zero. +*/ +public class HeadingMap { + private int nMaxLevel; + private String[] sName; + private int[] nLevel; + + /** Constructor: Create a new HeadingMap + @param nMaxLevel the maximal level of headings that are mapped */ + public HeadingMap(int nMaxLevel) { + reset(nMaxLevel); + } + + /** Clear all data associated with this HeadingMap (in order to reuse it) */ + public void reset(int nMaxLevel) { + this.nMaxLevel = nMaxLevel; + sName = new String[nMaxLevel+1]; + nLevel = new int[nMaxLevel+1]; + } + + /** Set data associated with a specific heading level */ + public void setLevelData(int nWriterLevel, String sName, int nLevel) { + this.sName[nWriterLevel] = sName; + this.nLevel[nWriterLevel] = nLevel; + } + + /** Returns the maximal Writer level associated with this HeadingMap */ + public int getMaxLevel() { return nMaxLevel; } + + /** Return the name (for counter and \@startsection) for this level */ + public String getName(int nWriterLevel) { return sName[nWriterLevel]; } + + /** Return the LaTeX level for this Writer level (for \@startsection) */ + public int getLevel(int nWriterLevel) { return nLevel[nWriterLevel]; } +} diff --git a/source/java/writer2latex/latex/util/Info.java b/source/java/writer2latex/latex/util/Info.java new file mode 100644 index 0000000..24a0424 --- /dev/null +++ b/source/java/writer2latex/latex/util/Info.java @@ -0,0 +1,76 @@ +/************************************************************************ + * + * Info.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.latex.util; + +import org.w3c.dom.Element; + +import writer2latex.latex.LaTeXConfig; +import writer2latex.util.Misc; +import writer2latex.office.OfficeReader; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.latex.LaTeXDocumentPortion; +import writer2latex.latex.ConverterHelper; +import writer2latex.latex.ConverterPalette; + + +/** + * <p>This class creates various information to the user about the conversion.</p> + */ +public class Info extends ConverterHelper { + + public Info(OfficeReader ofr, LaTeXConfig config, ConverterPalette palette) { + super(ofr,config,palette); + } + + public void addDebugInfo(Element node, LaTeXDocumentPortion ldp) { + if (config.debug()) { + ldp.append("% ").append(node.getNodeName()); + addDebugInfo(node,ldp,XMLString.TEXT_ID); + addDebugInfo(node,ldp,XMLString.TEXT_NAME); + addDebugInfo(node,ldp,XMLString.TABLE_NAME); + addDebugInfo(node,ldp,XMLString.TEXT_STYLE_NAME); + if (node.getNodeName().equals(XMLString.TEXT_P) || node.getNodeName().equals(XMLString.TEXT_H)) { + StyleWithProperties style = ofr.getParStyle(node.getAttribute(XMLString.TEXT_STYLE_NAME)); + if (style!=null && style.isAutomatic()) { + ldp.append(" ("+style.getParentName()+")"); + } + ldp.append(" ("+ofr.getParStyles().getDisplayName(node.getAttribute(XMLString.TEXT_STYLE_NAME))+")"); + } + ldp.nl(); + } + } + + private void addDebugInfo(Element node, LaTeXDocumentPortion ldp, String sAttribute) { + String sValue = Misc.getAttribute(node,sAttribute); + if (sValue!=null) { + ldp.append(" ").append(sAttribute).append("=\"").append(sValue).append("\""); + } + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/latex/util/Package.html b/source/java/writer2latex/latex/util/Package.html new file mode 100644 index 0000000..e59bf06 --- /dev/null +++ b/source/java/writer2latex/latex/util/Package.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.xhtml.util</title> +</head> + +<body> +<p>Some general utility classes for LaTeX export.</p> +</body> +</html> diff --git a/source/java/writer2latex/latex/util/StyleMap.java b/source/java/writer2latex/latex/util/StyleMap.java new file mode 100644 index 0000000..d4469ef --- /dev/null +++ b/source/java/writer2latex/latex/util/StyleMap.java @@ -0,0 +1,99 @@ +/************************************************************************ + * + * StyleMap.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2007-07-30) + * + */ + +package writer2latex.latex.util; + +import java.util.Hashtable; +import java.util.Enumeration; + +public class StyleMap { + private Hashtable items = new Hashtable(); + + public void put(String sName, String sBefore, String sAfter, boolean bLineBreak, boolean bVerbatim) { + StyleMapItem item = new StyleMapItem(); + item.sBefore = sBefore; + item.sAfter = sAfter; + item.sNext = ";;"; + item.bLineBreak = bLineBreak; + item.bVerbatim = bVerbatim; + items.put(sName,item); + } + + public void put(String sName, String sBefore, String sAfter, String sNext, boolean bVerbatim) { + StyleMapItem item = new StyleMapItem(); + item.sBefore = sBefore; + item.sAfter = sAfter; + item.sNext = ";"+sNext+";"; + item.bLineBreak = true; + item.bVerbatim = bVerbatim; + items.put(sName,item); + } + + public void put(String sName, String sBefore, String sAfter) { + StyleMapItem item = new StyleMapItem(); + item.sBefore = sBefore; + item.sAfter = sAfter; + item.sNext = ";;"; + item.bLineBreak = true; + item.bVerbatim = false; + items.put(sName,item); + } + + public boolean contains(String sName) { + return sName!=null && items.containsKey(sName); + } + + public String getBefore(String sName) { + return ((StyleMapItem) items.get(sName)).sBefore; + } + + public String getAfter(String sName) { + return ((StyleMapItem) items.get(sName)).sAfter; + } + + public String getNext(String sName) { + String sNext = ((StyleMapItem) items.get(sName)).sNext; + return sNext.substring(1,sNext.length()-1); + } + + public boolean isNext(String sName, String sNext) { + String sNext1 = ((StyleMapItem) items.get(sName)).sNext; + return sNext1.indexOf(";"+sNext+";")>-1; + } + + public boolean getLineBreak(String sName) { + return contains(sName) && ((StyleMapItem) items.get(sName)).bLineBreak; + } + + public boolean getVerbatim(String sName) { + return contains(sName) && ((StyleMapItem) items.get(sName)).bVerbatim; + } + + public Enumeration getNames() { + return items.keys(); + } + +} diff --git a/source/java/writer2latex/latex/util/StyleMapItem.java b/source/java/writer2latex/latex/util/StyleMapItem.java new file mode 100644 index 0000000..a4ecb61 --- /dev/null +++ b/source/java/writer2latex/latex/util/StyleMapItem.java @@ -0,0 +1,36 @@ +/************************************************************************ + * + * StyleMapItem.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2007-07-30) + * + */ + +package writer2latex.latex.util; + +// A struct to hold data about a style map +class StyleMapItem { + String sBefore; + String sAfter; + String sNext; + boolean bLineBreak; + boolean bVerbatim; +} diff --git a/source/java/writer2latex/office/BibMark.java b/source/java/writer2latex/office/BibMark.java new file mode 100644 index 0000000..ce3426f --- /dev/null +++ b/source/java/writer2latex/office/BibMark.java @@ -0,0 +1,146 @@ +/************************************************************************ + * + * BibMark.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; + +import writer2latex.util.*; +//import writer2latex.office.*; + +/** + * <p>This class represents a single bibliography-mark.</p> + */ +public final class BibMark { + // Available fields + public static final int ADDRESS = 0; + public static final int ANNOTE = 1; + public static final int AUTHOR = 2; + public static final int BOOKTITLE = 3; + public static final int CHAPTER = 4; + // public static final int CROSSREF = 5; // BibTeX, missing in OOo + public static final int EDITION = 6; + public static final int EDITOR = 7; + public static final int HOWPUBLISHED = 8; + public static final int INSTITUTION = 9; + public static final int JOURNAL = 10; + // public static final int KEY = 11; // BibTeX, missing in OOo + public static final int MONTH = 12; + public static final int NOTE = 13; + public static final int NUMBER = 14; + public static final int ORGANIZATIONS = 15; // BibTeX: organization + public static final int PAGES = 16; + public static final int PUBLISHER = 17; + public static final int SCHOOL = 18; + public static final int SERIES = 19; + public static final int TITLE = 20 ; + public static final int REPORT_TYPE = 21; // BibTeX: report + public static final int VOLUME = 22; + public static final int YEAR = 23; + // remaining fields are not standard in BibTeX + public static final int URL = 24; + public static final int CUSTOM1 = 25; + public static final int CUSTOM2 = 26; + public static final int CUSTOM3 = 27; + public static final int CUSTOM4 = 28; + public static final int CUSTOM5 = 29; + public static final int ISBN = 30; + public static final int FIELD_COUNT = 31; + + + // Private data + private String sIdentifier; + private String sEntryType; + private String[] fields = new String[FIELD_COUNT]; + + /** + * <p>Create a new BibMark from scratch.</p> + */ + public BibMark(String sIdentifier, String sEntryType) { + this.sIdentifier = sIdentifier; + this.sEntryType = sEntryType; + } + + /** + * <p>Create a new BibMark from a text:bibliography-mark node.</p> + */ + public BibMark(Node node) { + sIdentifier = Misc.getAttribute(node,XMLString.TEXT_IDENTIFIER); + sEntryType = Misc.getAttribute(node,XMLString.TEXT_BIBLIOGRAPHY_TYPE); + if (sEntryType==null) { // bug in OOo 1.0! + sEntryType = Misc.getAttribute(node,XMLString.TEXT_BIBILIOGRAPHIC_TYPE); + } + fields[ADDRESS] = Misc.getAttribute(node,XMLString.TEXT_ADDRESS); + fields[ANNOTE] = Misc.getAttribute(node,XMLString.TEXT_ANNOTE); + fields[AUTHOR] = Misc.getAttribute(node,XMLString.TEXT_AUTHOR); + fields[BOOKTITLE] = Misc.getAttribute(node,XMLString.TEXT_BOOKTITLE); + fields[CHAPTER] = Misc.getAttribute(node,XMLString.TEXT_CHAPTER); + fields[EDITION] = Misc.getAttribute(node,XMLString.TEXT_EDITION); + fields[EDITOR] = Misc.getAttribute(node,XMLString.TEXT_EDITOR); + fields[HOWPUBLISHED] = Misc.getAttribute(node,XMLString.TEXT_HOWPUBLISHED); + fields[INSTITUTION] = Misc.getAttribute(node,XMLString.TEXT_INSTITUTION); + fields[JOURNAL] = Misc.getAttribute(node,XMLString.TEXT_JOURNAL); + fields[MONTH] = Misc.getAttribute(node,XMLString.TEXT_MONTH); + fields[NOTE] = Misc.getAttribute(node,XMLString.TEXT_NOTE); + fields[NUMBER] = Misc.getAttribute(node,XMLString.TEXT_NUMBER); + fields[ORGANIZATIONS] = Misc.getAttribute(node,XMLString.TEXT_ORGANIZATIONS); + fields[PAGES] = Misc.getAttribute(node,XMLString.TEXT_PAGES); + fields[PUBLISHER] = Misc.getAttribute(node,XMLString.TEXT_PUBLISHER); + fields[SCHOOL] = Misc.getAttribute(node,XMLString.TEXT_SCHOOL); + fields[SERIES] = Misc.getAttribute(node,XMLString.TEXT_SERIES); + fields[TITLE] = Misc.getAttribute(node,XMLString.TEXT_TITLE); + fields[REPORT_TYPE] = Misc.getAttribute(node,XMLString.TEXT_REPORT_TYPE); + fields[VOLUME] = Misc.getAttribute(node,XMLString.TEXT_VOLUME); + fields[YEAR] = Misc.getAttribute(node,XMLString.TEXT_YEAR); + fields[URL] = Misc.getAttribute(node,XMLString.TEXT_URL); + fields[CUSTOM1] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM1); + fields[CUSTOM2] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM2); + fields[CUSTOM3] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM3); + fields[CUSTOM4] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM4); + fields[CUSTOM5] = Misc.getAttribute(node,XMLString.TEXT_CUSTOM5); + fields[ISBN] = Misc.getAttribute(node,XMLString.TEXT_ISBN); + } + + /** + * <p>Get the identifier.</p> + */ + public String getIdentifier() { return sIdentifier; } + + /** + * <p>Get the entry type.</p> + */ + public String getEntryType() { return sEntryType; } + + /** + * <p>Set a specific field.</p> + */ + public void setField(int nField,String sValue) { fields[nField] = sValue; } + + /** + * <p>Return a specific field.</p> + */ + public String getField(int nField) { return fields[nField]; } +} \ No newline at end of file diff --git a/source/java/writer2latex/office/CellView.java b/source/java/writer2latex/office/CellView.java new file mode 100644 index 0000000..62aefbc --- /dev/null +++ b/source/java/writer2latex/office/CellView.java @@ -0,0 +1,40 @@ +/************************************************************************ + * + * CellView.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-07) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Element; + +/** + * This class represent a cell in a table view</p> + */ +public class CellView { + public Element cell = null; + public int nRowSpan = 1; + public int nColSpan = 1; + public int nOriginalRow = -1; + public int nOriginalCol = -1; +} \ No newline at end of file diff --git a/source/java/writer2latex/office/ControlReader.java b/source/java/writer2latex/office/ControlReader.java new file mode 100644 index 0000000..a7f0f8a --- /dev/null +++ b/source/java/writer2latex/office/ControlReader.java @@ -0,0 +1,151 @@ +/************************************************************************ + * + * ControlReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.office; + +//import java.util.Hashtable; +import java.util.Vector; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import writer2latex.util.Misc; + +/** <p> This class reads a form control in an OOo document (a form:control + * node). A control always has an owner form. + * Properties and events are ignored.</p> + */ +public class ControlReader { + + private FormReader ownerForm; // a control always belongs to a form + private String sId; // a control is identified by id + private Element control; // the control element + private Element controlType; // the type specific child element + private Vector items = new Vector(); // the options/items of a list/combobox + + /** <p>The constructor reads the content of a control element</p> + * The representation in OpenDocument differs slightly from OOo 1.x. + + * @param control a DOM element, which must be control node + */ + public ControlReader(Element control, FormReader ownerForm) { + this.ownerForm = ownerForm; + this.control = control; + sId = control.getAttribute(XMLString.FORM_ID); + // Read the control type specific info + if (control.getTagName().equals(XMLString.FORM_CONTROL)) { // old format + controlType = Misc.getFirstChildElement(control); + } + else { // oasos + controlType = control; + } + if (controlType!=null) { // must always be the case! + // Collect options/items + Node child = controlType.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE && ( + child.getNodeName().equals(XMLString.FORM_OPTION) || + child.getNodeName().equals(XMLString.FORM_ITEM))) { + items.add(child); + } + child = child.getNextSibling(); + } + } + } + + /** <p>A control in OOo is identified by id (<code>form:control-id</code> + * attribute. The id is accessed by this method.</p> + * @return the id of the control + */ + public String getId() { return sId; } + + /** <p>A control in OOo belongs to a form.</p> + * @return the form containing this control + */ + public FormReader getOwnerForm() { return ownerForm; } + + /** <p>Get an attribute of the control. If the attribute does not exist, + * this method returns <code>null</code>. + * @param sName the name of the attribute + * @return the value of the attribute, or <code>null</code> + */ + public String getAttribute(String sName) { + return control.hasAttribute(sName) ? control.getAttribute(sName) : null; + } + + /** <p>The type of the control is identified by a name, eg. form:submit</p> + * @return the type of this control + */ + public String getControlType() { return controlType.getTagName(); } + + /** <p>Get an attribute specific to this type of control. + * If the attribute does not exist, this method returns <code>null</code>. + * @param sName the name of the attribute + * @return the value of the attribute, or <code>null</code> + */ + public String getTypeAttribute(String sName) { + return controlType!=null && controlType.hasAttribute(sName) ? + controlType.getAttribute(sName) : null; + } + + /** <p>Return the number of options/items in this control. + * Only listbox (options) and combobox (items) controls can have these, + * for other controls this will return 0. + * @return the number of options/items + */ + public int getItemCount() { return items.size(); } + + /** <p>Get an attribute of an option/item. + * If the index and/or the attribute does not exist, this method returns + * <code>null</code>. + * @param nIndex the index of the option/item + * @param sName the name of the attribute + * @return the value of the attribute, or <code>null</code> + */ + public String getItemAttribute(int nIndex, String sName) { + if (0<=nIndex && nIndex<=items.size()) { + return ((Element)items.get(nIndex)).hasAttribute(sName) ? + ((Element)items.get(nIndex)).getAttribute(sName) : null; + } + else { + return null; + } + } + + /** <p>Get the value of an option/item. + * If the index does not exist, this method returns + * <code>null</code>. + * @param nIndex the index of the option/item + * @return the value of the option/item, or <code>null</code> + */ + public String getItemValue(int nIndex) { + if (0<=nIndex && nIndex<=items.size()) { + return Misc.getPCDATA((Element)items.get(nIndex)); + } + else { + return null; + } + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/FontDeclaration.java b/source/java/writer2latex/office/FontDeclaration.java new file mode 100644 index 0000000..e286f83 --- /dev/null +++ b/source/java/writer2latex/office/FontDeclaration.java @@ -0,0 +1,61 @@ +/************************************************************************ + * + * FontDeclaration.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2005 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2005-10-10) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; + +/** <p> Class representing a font declaration in OOo</p> + */ +public class FontDeclaration extends OfficeStyle { + private PropertySet properties = new PropertySet(); + + private String sFontFamily = null; + private String sFontFamilyGeneric = null; + private String sFontPitch = null; + + public void loadStyleFromDOM(Node node) { + super.loadStyleFromDOM(node); + properties.loadFromDOM(node); + sFontFamily = properties.getProperty(XMLString.FO_FONT_FAMILY); + if (sFontFamily==null) { // oasis + sFontFamily = properties.getProperty(XMLString.SVG_FONT_FAMILY); + } + sFontFamilyGeneric = properties.getProperty(XMLString.STYLE_FONT_FAMILY_GENERIC); + sFontPitch = properties.getProperty(XMLString.STYLE_FONT_PITCH); + } + + public String getProperty(String sProperty){ + return properties.getProperty(sProperty); + } + + public String getFontFamily() { return sFontFamily; } + + public String getFontFamilyGeneric() { return sFontFamilyGeneric; } + + public String getFontPitch() { return sFontPitch; } + +} diff --git a/source/java/writer2latex/office/FormReader.java b/source/java/writer2latex/office/FormReader.java new file mode 100644 index 0000000..25dea9d --- /dev/null +++ b/source/java/writer2latex/office/FormReader.java @@ -0,0 +1,79 @@ +/************************************************************************ + * + * FormReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + */ + +// Version 1.0 (2008-11-22) + +package writer2latex.office; + +//import java.util.Hashtable; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.util.Misc; + +/** <p> This class reads a form in an OOo document (a form:form node)</p> + * Note: Subforms, properties and events are ignored. + */ +public class FormReader { + + //private FormsReader forms; // the global collection of all forms + private String sName; // a form is identified by name + private Element form; // the form element + + /** <p>The constructor reads the content of a <code>form:form</code> element</p> + * @param form a DOM element, which must be <code>form:form</code> node + */ + public FormReader(Element form, FormsReader forms) { + //this.forms = forms; + this.form = form; + sName = form.getAttribute(XMLString.FORM_NAME); + // Collect all controls contained in this form + Node child = form.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + String sId = Misc.getAttribute((Element)child,XMLString.FORM_ID); + if (sId!=null) { + ControlReader control = new ControlReader((Element) child, this); + forms.addControl(control); + } + } + child = child.getNextSibling(); + } + } + + /** <p>A form in OOo is identified by name (<code>form:name</code> + * attribute. The name is accessed by this method.</p> + * @return the name of the form + */ + public String getName() { return sName; } + + /** <p>Get an attribute of the form. If the attribute does not exist, + * this method returns <code>null</code>. + * @param sName the name of the attribute + * @return the value of the attribute, or <code>null</code> + */ + public String getAttribute(String sName) { + return form.hasAttribute(sName) ? form.getAttribute(sName) : null; + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/FormsReader.java b/source/java/writer2latex/office/FormsReader.java new file mode 100644 index 0000000..978f5cb --- /dev/null +++ b/source/java/writer2latex/office/FormsReader.java @@ -0,0 +1,114 @@ +/************************************************************************ + * + * FormsReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.office; + +import java.util.Hashtable; +import java.util.Iterator; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** <p> This class reads the collection of all forms in an OOo document + * (the <code>office:forms</code> element).</p> + * <p>An OOo document may contain any number of forms; these are declared + * within this element. In OOo, unlike eg. html, the form declaration is + * separated from the presentation. This element contains the + * <em>declaration</em>. The <em>presentation</em> is given by inclusion of + * <code>draw:control</code> elements in the document flow. These refer to form + * controls by id.</p> + * <p>Note: A form is identified by a unique name, a control is + * identified by a (globally) unique id.</p> + */ +public class FormsReader { + + private Element formsElement; // The office:forms element + private Hashtable forms = new Hashtable(); // all forms, indexed by name + private Hashtable controls = new Hashtable(); // all controls, indexed by id + + /** <p>Read the content of an <code>office:forms</code> element</p> + * @param formsElement a DOM element, which must be <code>office:forms</code> node + */ + public void read(Element formsElement) { + this.formsElement = formsElement; + // Collect all forms + Node child = formsElement.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE && + child.getNodeName().equals(XMLString.FORM_FORM)) { + FormReader form = new FormReader((Element) child, this); + forms.put(form.getName(),form); + } + child = child.getNextSibling(); + } + } + + /** <p>Get an attribute of the forms. If the attribute does not exist, + * this method returns <code>null</code>. + * @param sName the name of the attribute + * @return the value of the attribute, or <code>null</code> + */ + public String getAttribute(String sName) { + return formsElement.hasAttribute(sName) ? formsElement.getAttribute(sName) : null; + } + + /** <p>Get a <code>Iterator</code> over all forms.</p> + * @return a <code>Iterator</code> over all forms + */ + public Iterator getFormsIterator() { + return forms.values().iterator(); + } + + /** <p>Get a form by name</p> + * @param sName the <code>form:name</code> of the form + * @return the form as a <code>FormReader</code> object + */ + public FormReader getForm(String sName) { + return (FormReader) forms.get(sName); + } + + /** <p>Get a <code>Iterator</code> over all controls.</p> + * @return a <code>Iterator</code> over all controls + */ + public Iterator getControlsIterator() { + return controls.values().iterator(); + } + + /** <p>Get a control by id</p> + * @param sId the <code>form:control-id</code> of the control + * @return the control as a <code>ControlReader</code> object + */ + public ControlReader getControl(String sId) { + return (ControlReader) controls.get(sId); + } + + /** <p>Add a control</p> + * @param control a <code>ControlReader</code> representing the control + */ + protected void addControl(ControlReader control) { + controls.put(control.getId(),control); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/ImageLoader.java b/source/java/writer2latex/office/ImageLoader.java new file mode 100644 index 0000000..68c6f42 --- /dev/null +++ b/source/java/writer2latex/office/ImageLoader.java @@ -0,0 +1,194 @@ +/************************************************************************ + * + * ImageLoader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.office; + +//import java.io.ByteArrayInputStream; +//import java.io.ByteArrayOutputStream; +//import java.io.IOException; +import java.util.HashSet; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.api.GraphicConverter; +import writer2latex.util.Base64; +import writer2latex.util.Misc; +import writer2latex.xmerge.BinaryGraphicsDocument; +import writer2latex.xmerge.EmbeddedObject; +import writer2latex.xmerge.EmbeddedBinaryObject; +import writer2latex.xmerge.OfficeDocument; + +//import writer2latex.util.*; + +/** + * <p>This class extracts images from an OOo file. + * The images are returned as BinaryGraphicsDocument.</p> + */ +public final class ImageLoader { + // The Office document to load images from + private OfficeDocument oooDoc; + + // Data for file name generation + private String sOutFileName; + private boolean bUseSubdir = false; + private int nImageCount = 0; + + // should EPS be extracted from SVM? + private boolean bExtractEPS; + + // Data for image conversion + private GraphicConverter gcv = null; + private boolean bAcceptOtherFormats = true; + private String sDefaultFormat = null; + private String sDefaultVectorFormat = null; + private HashSet acceptedFormats = new HashSet(); + + public ImageLoader(OfficeDocument oooDoc, String sOutFileName, boolean bExtractEPS) { + this.oooDoc = oooDoc; + this.sOutFileName = sOutFileName; + this.bExtractEPS = bExtractEPS; + } + + public void setOutFileName(String sOutFileName) { this.sOutFileName = sOutFileName; } + + public void setUseSubdir(boolean bUseSubdir) { this.bUseSubdir = bUseSubdir; } + + public void setAcceptOtherFormats(boolean b) { bAcceptOtherFormats = b; } + + public void setDefaultFormat(String sMime) { + addAcceptedFormat(sMime); + sDefaultFormat = sMime; + } + + public void setDefaultVectorFormat(String sMime) { + addAcceptedFormat(sMime); + sDefaultVectorFormat = sMime; + } + + public void addAcceptedFormat(String sMime) { acceptedFormats.add(sMime); } + + private boolean isAcceptedFormat(String sMime) { return acceptedFormats.contains(sMime); } + + public void setGraphicConverter(GraphicConverter gcv) { this.gcv = gcv; } + + public BinaryGraphicsDocument getImage(Node node) { + // node must be a draw:image element. + // variables to hold data about the image: + String sMIME = null; + String sExt = null; + byte[] blob = null; + + String sHref = Misc.getAttribute(node,XMLString.XLINK_HREF); + if (sHref==null || sHref.length()==0) { + // Image must be contained in an office:binary-element as base64: + Node obd = Misc.getChildByTagName(node,XMLString.OFFICE_BINARY_DATA); + if (obd!=null) { + StringBuffer buf = new StringBuffer(); + NodeList nl = obd.getChildNodes(); + int nLen = nl.getLength(); + for (int i=0; i<nLen; i++) { + if (nl.item(i).getNodeType()==Node.TEXT_NODE) { + buf.append(nl.item(i).getNodeValue()); + } + } + blob = Base64.decode(buf.toString()); + sMIME = MIMETypes.getMagicMIMEType(blob); + sExt = MIMETypes.getFileExtension(sMIME); + } + } + else { + // Image may be embedded in package: + if (sHref.startsWith("#")) { sHref = sHref.substring(1); } + if (sHref.startsWith("./")) { sHref = sHref.substring(2); } + EmbeddedObject obj = oooDoc.getEmbeddedObject(sHref); + if (obj!=null && obj instanceof EmbeddedBinaryObject) { + EmbeddedBinaryObject object = (EmbeddedBinaryObject) obj; + blob = object.getBinaryData(); + sMIME = object.getType(); + sExt = MIMETypes.getFileExtension(sMIME); + } + else { + // This is a linked image + // TODO: Perhaps we should download the image from the url in sHref? + // Alternatively BinaryGraphicsDocument should be extended to + // handle external graphics. + } + } + + if (blob==null) { return null; } + + // Assign a name (without extension) + String sName = sOutFileName+"-img"+(++nImageCount); + if (bUseSubdir) { sName = sOutFileName + "-img/" + sName; } + + BinaryGraphicsDocument bgd = null; + + if (bExtractEPS && MIMETypes.SVM.equals(MIMETypes.getMagicMIMEType(blob))) { + // Look for postscript: + int[] offlen = new int[2]; + if (SVMReader.readSVM(blob,offlen)) { + bgd = new BinaryGraphicsDocument(sName, + MIMETypes.EPS_EXT,MIMETypes.EPS); + bgd.read(blob,offlen[0],offlen[1]); + } + } + + if (bgd==null) { + // If we have a converter AND a default format AND this image + // is not in an accepted format AND the converter knows how to + // convert it - try to convert... + if (gcv!=null && !isAcceptedFormat(sMIME) && sDefaultFormat!=null) { + String sTargetMIME = null; + + if (MIMETypes.isVectorFormat(sMIME) && sDefaultVectorFormat!=null && + gcv.supportsConversion(sMIME,sDefaultVectorFormat,false,false)) { + sTargetMIME = sDefaultVectorFormat; + } + else if (gcv.supportsConversion(sMIME,sDefaultFormat,false,false)) { + sTargetMIME = sDefaultFormat; + } + + if (sTargetMIME!=null) { + byte[] newBlob = gcv.convert(blob,sMIME,sTargetMIME); + if (newBlob!=null) { + // Conversion succesful - create new data + blob = newBlob; + sMIME = sTargetMIME; + sExt = MIMETypes.getFileExtension(sMIME); + } + } + } + + if (isAcceptedFormat(sMIME) || bAcceptOtherFormats) { + bgd = new BinaryGraphicsDocument(sName,sExt,sMIME); + bgd.read(blob); + } + } + + return bgd; + } +} diff --git a/source/java/writer2latex/office/IndexMark.java b/source/java/writer2latex/office/IndexMark.java new file mode 100644 index 0000000..09827b7 --- /dev/null +++ b/source/java/writer2latex/office/IndexMark.java @@ -0,0 +1,97 @@ +/************************************************************************ + * + * IndexMark.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; + +import writer2latex.util.*; + +/** + * <p>This class contains static methods to read an index-mark.</p> + */ +public final class IndexMark { + + // Node must be text:*-mark or text:*-mark-start + public final static String getIndexValue(Node node) { + if (!node.getNodeName().endsWith("start")) { + return Misc.getAttribute(node,XMLString.TEXT_STRING_VALUE); + } + else { + return collectMark(node); + } + } + + // Node must be text:*-mark or text:*-mark-start + public final static String getKey1(Node node) { + return Misc.getAttribute(node,XMLString.TEXT_KEY1); + } + + // Node must be text:*-mark or text:*-mark-start + public final static String getKey2(Node node) { + return Misc.getAttribute(node,XMLString.TEXT_KEY2); + } + + // Collect a mark + private final static Node getRightNode(Node node) { + Node nextNode; + do {nextNode = node.getNextSibling(); + if (nextNode!=null) { return nextNode; } + node = node.getParentNode(); + } while (node!=null); + return null; + } + + private final static String collectMark(Node node) { + StringBuffer buf = new StringBuffer(); + String sId = Misc.getAttribute(node,XMLString.TEXT_ID); + node = getRightNode(node); + while (node!=null) { + if (node.getNodeType()==Node.TEXT_NODE) { + buf.append(node.getNodeValue()); + node = getRightNode(node); + } + else if (node.getNodeType()==Node.ELEMENT_NODE) { + boolean bReady = false; + //String sNodeName = node.getNodeName(); + if (sId.equals(Misc.getAttribute(node,XMLString.TEXT_ID))) { + node = null; // found the end mark + bReady = true; + } + else if (OfficeReader.isTextElement(node) && + !OfficeReader.isNoteElement(node)) { + if (node.hasChildNodes()) { + node = node.getFirstChild(); bReady=true; + } + } + if (!bReady) { node=getRightNode(node); }; + } + } + return buf.toString(); + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/ListCounter.java b/source/java/writer2latex/office/ListCounter.java new file mode 100644 index 0000000..18a9584 --- /dev/null +++ b/source/java/writer2latex/office/ListCounter.java @@ -0,0 +1,126 @@ +/************************************************************************ + * + * ListCounter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2005 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2007-10-17) + * + */ + +package writer2latex.office; + +import writer2latex.util.*; + +/** + * <p>This class produces labels for OOo lists/outlines (for xhtml + * and text, which cannot produce them on their own).</p> + * + */ +public class ListCounter { + private int nCounter[] = new int[11]; + private String sNumFormat[] = new String[11]; + private ListStyle style; + private int nLevel=1; // current level + + public ListCounter() { + // Create a dummy counter + this.style = null; + for (int i=1; i<=10; i++) { + sNumFormat[i] = null; + } + } + + public ListCounter(ListStyle style) { + this(); + if (style!=null) { + this.style = style; + for (int i=1; i<=10; i++) { + sNumFormat[i] = style.getLevelProperty(i,XMLString.STYLE_NUM_FORMAT); + } + } + } + + public ListCounter step(int nLevel) { + // Make sure no higher levels are zero + // This means that unlike eg. LaTeX, step(1).step(3) does not create + // the value 1.0.1 but rather 1.1.1 + for (int i=1; i<nLevel; i++) { + if (nCounter[i]==0) { nCounter[i]=1; } + } + // Then step this level + nCounter[nLevel]++; + // Finally clear lower levels + if (nLevel<10) { restart(nLevel+1); } + this.nLevel = nLevel; + return this; + } + + public ListCounter restart(int nLevel) { + restart(nLevel,0); + return this; + } + + public ListCounter restart(int nLevel, int nValue) { + nCounter[nLevel] = nValue; + for (int i=nLevel+1; i<=10; i++) { + nCounter[i] = 0; + } + return this; + } + + public int getValue(int nLevel) { + return nCounter[nLevel]; + } + + public int[] getValues() { + int[] nCounterSnapshot = new int[11]; + System.arraycopy(nCounter,0,nCounterSnapshot,0,11); + return nCounterSnapshot; + } + + public String getLabel() { + if (sNumFormat[nLevel]==null) return ""; + int nLevels = Misc.getPosInteger(style.getLevelProperty(nLevel, + XMLString.TEXT_DISPLAY_LEVELS),1); + String sPrefix = style.getLevelProperty(nLevel,XMLString.STYLE_NUM_PREFIX); + String sSuffix = style.getLevelProperty(nLevel,XMLString.STYLE_NUM_SUFFIX); + String sLabel=""; + if (sPrefix!=null) { sLabel+=sPrefix; } + for (int j=nLevel-nLevels+1; j<nLevel; j++) { + sLabel+=formatNumber(nCounter[j],sNumFormat[j],true)+"."; + } + // TODO: Lettersync + sLabel+=formatNumber(nCounter[nLevel],sNumFormat[nLevel],true); + if (sSuffix!=null) { sLabel+=sSuffix; } + return sLabel; + } + + // Utility method to generate number + private String formatNumber(int number,String sStyle,boolean bLetterSync) { + if ("a".equals(sStyle)) { return Misc.int2alph(number,bLetterSync); } + else if ("A".equals(sStyle)) { return Misc.int2Alph(number,bLetterSync); } + else if ("i".equals(sStyle)) { return Misc.int2roman(number); } + else if ("I".equals(sStyle)) { return Misc.int2Roman(number); } + else if ("1".equals(sStyle)) { return Misc.int2arabic(number); } + else return ""; + } + + +} diff --git a/source/java/writer2latex/office/ListStyle.java b/source/java/writer2latex/office/ListStyle.java new file mode 100644 index 0000000..e849b57 --- /dev/null +++ b/source/java/writer2latex/office/ListStyle.java @@ -0,0 +1,128 @@ +/************************************************************************ + * + * ListStyle.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.4 (2004-02-16) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.util.Misc; + +/** <p> Class representing a list style (including outline numbering) in OOo Writer</p> +*/ +public class ListStyle extends OfficeStyle { + // the file format doesn't specify a maximum nesting level, but OOo + // currently supports 10 + private static final int MAX_LEVEL = 10; + private PropertySet[] level; + private PropertySet[] levelStyle; + + public ListStyle() { + level = new PropertySet[MAX_LEVEL+1]; + levelStyle = new PropertySet[MAX_LEVEL+1]; + for (int i=1; i<=MAX_LEVEL; i++) { + level[i] = new PropertySet(); + levelStyle[i] = new PropertySet(); + } + } + + public String getLevelType(int i) { + if (i>=1 && i<=MAX_LEVEL) { + return level[i].getName(); + } + else { + return null; + } + } + + public boolean isNumber(int i) { + return XMLString.TEXT_LIST_LEVEL_STYLE_NUMBER.equals(level[i].getName()); + } + + public boolean isBullet(int i) { + return XMLString.TEXT_LIST_LEVEL_STYLE_BULLET.equals(level[i].getName()); + } + + public boolean isImage(int i) { + return XMLString.TEXT_LIST_LEVEL_STYLE_IMAGE.equals(level[i].getName()); + } + + public String getLevelProperty(int i, String sName) { + if (i>=1 && i<=MAX_LEVEL) { + return level[i].getProperty(sName); + } + else { + return null; + } + } + + public String getLevelStyleProperty(int i, String sName) { + if (i>=1 && i<=MAX_LEVEL) { + return levelStyle[i].getProperty(sName); + } + else { + return null; + } + } + + public void loadStyleFromDOM(Node node) { + super.loadStyleFromDOM(node); + // Collect level information from child elements: + if (node.hasChildNodes()){ + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++ ) { + Node child=nl.item(i); + if (child.getNodeType()==Node.ELEMENT_NODE){ + String sLevel = Misc.getAttribute(child,XMLString.TEXT_LEVEL); + if (sLevel!=null) { + int nLevel = Misc.getPosInteger(sLevel,1); + if (nLevel>=1 && nLevel<=MAX_LEVEL) { + level[nLevel].loadFromDOM(child); + // Also include style:properties + if (child.hasChildNodes()){ + NodeList nl2 = child.getChildNodes(); + int nLen2 = nl2.getLength(); + for (int i2 = 0; i2 < nLen2; i2++ ) { + Node child2=nl2.item(i2); + if (child2.getNodeType()==Node.ELEMENT_NODE){ + if (child2.getNodeName().equals(XMLString.STYLE_PROPERTIES)) { + levelStyle[nLevel].loadFromDOM(child2); + } + if (child2.getNodeName().equals(XMLString.STYLE_LIST_LEVEL_PROPERTIES)) { // oasis + levelStyle[nLevel].loadFromDOM(child2); + } + } + } + } + } + } + } + } + } + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/LoftReader.java b/source/java/writer2latex/office/LoftReader.java new file mode 100644 index 0000000..c124bae --- /dev/null +++ b/source/java/writer2latex/office/LoftReader.java @@ -0,0 +1,135 @@ +/************************************************************************ + * + * LoftReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.office; + +//import java.util.Hashtable; +//import java.util.Set; + +import org.w3c.dom.Element; +//import org.w3c.dom.Node; + +import writer2latex.util.Misc; + +/** + * <p>The class reads a <code>text:illustration-index</code> or + * <code>text:table-index</code> element.</p> + */ +public class LoftReader { + + private Element loftSource = null; + private Element indexBody = null; + + private String sName = null; // (section) name for this lof/lot + private String sStyleName = null; // section style name + + private boolean bUseCaption = true; + private String sCaptionSequenceName = null; + private boolean bIsByChapter = false; // default is document + + private Element indexTitleTemplate = null; + private Element loftEntryTemplate = null; + + private boolean bIsTableIndex; + + + /** <p>Initialize the LoftReader with a illustration/table index node + * @param onode a <code>text:*-index</code> + */ + public LoftReader(Element onode) { + bIsTableIndex = onode.getTagName().equals(XMLString.TEXT_TABLE_INDEX); + + sName = Misc.getAttribute(onode,XMLString.TEXT_NAME); + sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + + loftSource = bIsTableIndex ? + Misc.getChildByTagName(onode,XMLString.TEXT_TABLE_INDEX_SOURCE) : + Misc.getChildByTagName(onode,XMLString.TEXT_ILLUSTRATION_INDEX_SOURCE); + + indexBody = Misc.getChildByTagName(onode,XMLString.TEXT_INDEX_BODY); + + if (loftSource!=null) { + bUseCaption = !"false".equals(loftSource.getAttribute(XMLString.TEXT_USE_CAPTION)); + sCaptionSequenceName = loftSource.getAttribute(XMLString.TEXT_CAPTION_SEQUENCE_NAME); + bIsByChapter = "chapter".equals(loftSource.getAttribute(XMLString.TEXT_INDEX_SCOPE)); + + indexTitleTemplate = Misc.getChildByTagName(loftSource,XMLString.TEXT_INDEX_TITLE_TEMPLATE); + loftEntryTemplate = bIsTableIndex ? + Misc.getChildByTagName(loftSource,XMLString.TEXT_TABLE_INDEX_ENTRY_TEMPLATE) : + Misc.getChildByTagName(loftSource,XMLString.TEXT_ILLUSTRATION_INDEX_ENTRY_TEMPLATE); + } + } + + /** <p>Get the (section) name for this loft </p> + * @return the name of the loft + */ + public String getName() { return sName; } + + /** <p>Get the (section) style name for this loft </p> + * @return name of the section style to use for this loft + */ + public String getStyleName() { return sStyleName; } + + /** <p>Is this a table index or a figure index? </p> + * @return true if it's a table index + */ + public boolean isTableIndex() { return bIsTableIndex; } + + /** <p>Is this loft by chapter? </p> + * @return true if the scope is a chapter only + */ + public boolean isByChapter() { return bIsByChapter; } + + /** <p>Is this loft generated by captions? (otherwise: by object names)</p> + * @return true if we use captions + */ + public boolean useCaption() { return bUseCaption; } + + /** <p>Get the sequence name to use for the caption</p> + * @return the name of the caption + */ + public String getCaptionSequenceName() { return sCaptionSequenceName; } + + /** <p>Get the index title template for this loft</p> + * @return the <code>text:index-title-template</code> element, or null + */ + public Element getIndexTitleTemplate() { return indexTitleTemplate; } + + /** <p>Get the entry template for this loft at a specific level</p> + * @param nLevel the outline level + * @return the <code>text:table-of-content-entry-template</code> element, or null + */ + public Element getLoftEntryTemplate(int nLevel) { + return loftEntryTemplate; + } + + /** <p>Return the generated content of this loft, if available</p> + * @return the <code>text:index-body</code> element + */ + public Element getIndexBody() { return indexBody; } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/MIMETypes.java b/source/java/writer2latex/office/MIMETypes.java new file mode 100644 index 0000000..3ae462e --- /dev/null +++ b/source/java/writer2latex/office/MIMETypes.java @@ -0,0 +1,159 @@ +/************************************************************************ + * + * MIMETypes.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-24) + * + */ + +package writer2latex.office; + +/* Some helpers to handle the MIME types used by OOo + */ + +public final class MIMETypes extends writer2latex.api.MIMETypes { + // OOo MIME types, taken from + // http://framework.openoffice.org/documentation/mimetypes/mimetypes.html + public static final String WRITER="application/vnd.sun.xml.writer"; + public static final String CALC="application/vnd.sun.xml.calc"; + public static final String IMPRESS="application/vnd.sun.xml.impress"; + public static final String DRAW="application/vnd.sun.xml.draw"; + public static final String CHART="application/vnd.sun.xml.chart"; + public static final String MATH="application/vnd.sun.xml.math"; + // OpenDocument MIME types (from spec) + public static final String ODT="application/vnd.oasis.opendocument.text"; + public static final String ODS="application/vnd.oasis.opendocument.spreadsheet"; + public static final String ODP="application/vnd.oasis.opendocument.presentation"; + public static final String ODF="application/vnd.oasis.opendocument.formula"; + + // zip + public static final String ZIP="application/zip"; + + // Magic signatures for some binary files + public static final byte[] PNG_SIG = { (byte) 0x89, 0x50, 0x4e, 0x47 }; // .PNG + public static final byte[] JPEG_SIG = { (byte) 0xff, (byte) 0xd8, (byte) 0xff, (byte) 0xe0 }; + public static final byte[] GIF87_SIG = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }; // GIF87a + public static final byte[] GIF89_SIG = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }; // GIF89a + public static final byte[] TIFF_SIG = { 0x49, 0x49, 0x2A }; // II* + public static final byte[] BMP_SIG = { 0x42, 0x4d }; // BM + public static final byte[] WMF_SIG = { (byte) 0xd7, (byte) 0xcd, (byte) 0xc6, (byte) 0x9a }; + public static final byte[] WMF30_SIG = { 1, 0, 9, 0 }; // Old WMF format, not reliable - see below + public static final byte[] EPS_SIG = { 0x25, 0x21 }; // %! + public static final byte[] SVM_SIG = { 0x56, 0x43, 0x4c, 0x4d, 0x54, 0x46 }; // VCLMTF + public static final byte[] ZIP_SIG = { 0x50, 0x4b, 0x03, 0x04 }; // PK.. + + + // Preferred file extensions for some files + public static final String LATEX_EXT = ".tex"; + public static final String BIBTEX_EXT = ".bib"; + public static final String XHTML_EXT = ".html"; + public static final String XHTML_MATHML_EXT = ".xhtml"; + public static final String XHTML_MATHML_XSL_EXT = ".xml"; + public static final String PNG_EXT = ".png"; + public static final String JPEG_EXT = ".jpg"; // this is the default in graphicx.sty + public static final String GIF_EXT = ".gif"; + public static final String TIFF_EXT = ".tif"; + public static final String BMP_EXT = ".bmp"; + public static final String WMF_EXT = ".wmf"; + public static final String EPS_EXT = ".eps"; + public static final String SVM_EXT = ".svm"; + public static final String PDF_EXT = ".pdf"; + + private static final boolean isType(byte[] blob, byte[] sig) { + int n = sig.length; + for (int i=0; i<n; i++) { + if (blob[i]!=sig[i]) { return false; } + } + return true; + } + + public static final String getMagicMIMEType(byte[] blob) { + if (isType(blob,PNG_SIG)) { return PNG; } + if (isType(blob,JPEG_SIG)) { return JPEG; } + if (isType(blob,GIF87_SIG)) { return GIF; } + if (isType(blob,GIF89_SIG)) { return GIF; } + if (isType(blob,TIFF_SIG)) { return TIFF; } + if (isType(blob,BMP_SIG)) { return BMP; } + if (isType(blob,WMF_SIG)) { return WMF; } + if (isType(blob,WMF30_SIG)) { return WMF; } // do not trust this.. + if (isType(blob,EPS_SIG)) { return EPS; } + if (isType(blob,SVM_SIG)) { return SVM; } + if (isType(blob,ZIP_SIG)) { return ZIP; } + return ""; + } + + public static final String getFileExtension(String sMIME) { + if (PNG.equals(sMIME)) { return PNG_EXT; } + if (JPEG.equals(sMIME)) { return JPEG_EXT; } + if (GIF.equals(sMIME)) { return GIF_EXT; } + if (TIFF.equals(sMIME)) { return TIFF_EXT; } + if (BMP.equals(sMIME)) { return BMP_EXT; } + if (WMF.equals(sMIME)) { return WMF_EXT; } + if (EPS.equals(sMIME)) { return EPS_EXT; } + if (SVM.equals(sMIME)) { return SVM_EXT; } + if (PDF.equals(sMIME)) { return PDF_EXT; } + if (LATEX.equals(sMIME)) { return LATEX_EXT; } + if (BIBTEX.equals(sMIME)) { return BIBTEX_EXT; } + if (XHTML.equals(sMIME)) { return XHTML_EXT; } + if (XHTML_MATHML.equals(sMIME)) { return XHTML_MATHML_EXT; } + if (XHTML_MATHML_XSL.equals(sMIME)) { return XHTML_MATHML_XSL_EXT; } + return ""; + } + + public static boolean isVectorFormat(String sMIME) { + return WMF.equals(sMIME) || EPS.equals(sMIME) || SVM.equals(sMIME) || PDF.equals(sMIME); + } + + +} + +/* Notes on old WMF format: + Found on math type objects: 1 0 - 9 0 - 0 3 - 90 1 - 0 0 + + Explanation from http://wvware.sourceforge.net/caolan/ora-wmf.html : + The standard Windows metafile header is 18 bytes in length and is structured as follows: + +typedef struct _WindowsMetaHeader +{ + WORD FileType; Type of metafile (0=memory, 1=disk) + WORD HeaderSize; Size of header in WORDS (always 9) + WORD Version; Version of Microsoft Windows used + DWORD FileSize; Total size of the metafile in WORDs + WORD NumOfObjects; Number of objects in the file + DWORD MaxRecordSize; The size of largest record in WORDs + WORD NumOfParams; Not Used (always 0) +} WMFHEAD; + +FileType contains a value which indicates the location of the metafile data. A value of 0 indicates that the metafile is stored in memory, while a 1 indicates that it is stored on disk. + +HeaderSize contains the size of the metafile header in 16-bit WORDs. This value is always 9. + +Version stores the version number of Microsoft Windows that created the metafile. This value is always read in hexadecimal format. For example, in a metafile created by Windows 3.0 and 3.1, this item would have the value 0x0300. + +FileSize specifies the total size of the metafile in 16-bit WORDs. + +NumOfObjects specifies the number of objects that are in the metafile. + +MaxRecordSize specifies the size of the largest record in the metafile in WORDs. + +NumOfParams is not used and is set to a value of 0. + +*/ diff --git a/source/java/writer2latex/office/MasterPage.java b/source/java/writer2latex/office/MasterPage.java new file mode 100644 index 0000000..d23d3d8 --- /dev/null +++ b/source/java/writer2latex/office/MasterPage.java @@ -0,0 +1,66 @@ +/************************************************************************ + * + * MasterPage.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2005 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2005-10-10) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; +import writer2latex.util.Misc; + +/** <p> Class representing a master page in OOo Writer </p> */ +public class MasterPage extends OfficeStyle { + private PropertySet properties = new PropertySet(); + private Node header = null; + private Node headerLeft = null; + private Node footer = null; + private Node footerLeft = null; + + public String getProperty(String sPropName) { + return properties.getProperty(sPropName); + } + + public String getPageLayoutName() { + String sName = properties.getProperty(XMLString.STYLE_PAGE_MASTER_NAME); + if (sName==null) { // try oasis format + sName = properties.getProperty(XMLString.STYLE_PAGE_LAYOUT_NAME); + } + return sName; + } + + public Node getHeader() { return header; } + public Node getHeaderLeft() { return headerLeft; } + public Node getFooter() { return footer; } + public Node getFooterLeft() { return footerLeft; } + + public void loadStyleFromDOM(Node node) { + super.loadStyleFromDOM(node); + properties.loadFromDOM(node); + header = Misc.getChildByTagName(node,XMLString.STYLE_HEADER); + headerLeft = Misc.getChildByTagName(node,XMLString.STYLE_HEADER_LEFT); + footer = Misc.getChildByTagName(node,XMLString.STYLE_FOOTER); + footerLeft = Misc.getChildByTagName(node,XMLString.STYLE_FOOTER_LEFT); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/MetaData.java b/source/java/writer2latex/office/MetaData.java new file mode 100644 index 0000000..2c72fa3 --- /dev/null +++ b/source/java/writer2latex/office/MetaData.java @@ -0,0 +1,169 @@ +/************************************************************************ + * + * MetaData.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.util.*; +//import writer2latex.office.*; + +import writer2latex.xmerge.OfficeDocument; + +/** + * <p>This class represents the metadata of an OOo Writer document.</p> + */ +public class MetaData { + // Dublin Core + private String sTitle = ""; + private String sCreator = ""; + private String sInitialCreator = ""; + private String sDate = ""; + private String sDescription = ""; + private String sLanguage = ""; + private String sSubject = ""; + // Keywords + private String sKeywords = ""; + + /** <p>Construct a new instance from an OOo Writer document.</p> + * @param oooDoc is the OOo document + */ + public MetaData(OfficeDocument oooDoc) { + // get the DOM (either package or flat) + Document dom = oooDoc.getMetaDOM(); + if (dom==null) { dom = oooDoc.getContentDOM(); } + + // get the office:meta element + NodeList list = dom.getElementsByTagName(XMLString.OFFICE_META); + if (list.getLength() == 0) { return; } // oops, no metadata - fails silently + Node meta = list.item(0); + if (!meta.hasChildNodes()) { return; } + + // traverse the metadata + CSVList keywords = new CSVList(", "); + list = meta.getChildNodes(); + int nLen = list.getLength(); + for (int i=0; i<nLen; i++) { + Node child = list.item(i); + String sName = child.getNodeName(); + if (XMLString.DC_TITLE.equals(sName)) { + sTitle = getContent(child); + } + if (XMLString.DC_CREATOR.equals(sName)) { + sCreator = getContent(child); + } + if (XMLString.DC_DATE.equals(sName)) { + sDate = getContent(child); + } + if (XMLString.DC_DESCRIPTION.equals(sName)) { + sDescription = getContent(child); + } + if (XMLString.DC_LANGUAGE.equals(sName)) { + sLanguage = getContent(child); + } + if (XMLString.DC_SUBJECT.equals(sName)) { + sSubject = getContent(child); + } + if (XMLString.META_INITIAL_CREATOR.equals(sName)) { + sInitialCreator = getContent(child); + } + if (XMLString.META_KEYWORD.equals(sName)) { // oasis + keywords.addValue(getContent(child)); + } + if (XMLString.META_KEYWORDS.equals(sName)) { + // Old format: Keywords are contained within meta:keywords + if (child.hasChildNodes()) { + // traverse the keywords + NodeList keywordList = child.getChildNodes(); + int nWordCount = keywordList.getLength(); + for (int j=0; j<nWordCount; j++) { + Node grandchild = keywordList.item(j); + if (XMLString.META_KEYWORD.equals(grandchild.getNodeName())){ + keywords.addValue(getContent(grandchild)); + } + } + } + } + } + sKeywords = keywords.toString(); + } + + /** <p> Get the title of this document (may be null)</p> + * @return the title of the document + */ + public String getTitle() { return sTitle; } + + /** <p> Get the creator of this document (may be null)</p> + * @return the creator of the document (or the initial creator if none is specified) + */ + public String getCreator() { return sCreator==null ? sInitialCreator : sCreator; } + + /** <p> Get the initial creator of this document (may be null)</p> + * @return the initial creator of the document + */ + public String getInitialCreator() { return sInitialCreator; } + + /** <p> Get the date of this document (may be null)</p> + * @return the date of the document + */ + public String getDate() { return sDate; } + + /** <p> Get the description of this document (may be null)</p> + * @return the description of the document + */ + public String getDescription() { return sDescription; } + + /** <p> Get the language of this document (may be null)</p> + * @return the language of the document + */ + public String getLanguage() { return sLanguage; } + + /** <p> Get the subject of this document (may be null)</p> + * @return the subject of the document + */ + public String getSubject() { return sSubject; } + + /** <p> Get the keywords of this document as a comma separated list (may be null)</p> + * @return the keywords of the document + */ + public String getKeywords() { return sKeywords; } + + private String getContent(Node node) { + if (!node.hasChildNodes()) { return null; } + String s=""; + NodeList list = node.getChildNodes(); + int nLen = list.getLength(); + for (int i=0; i<nLen; i++) { + if (list.item(i).getNodeType()==Node.TEXT_NODE) { + s+= list.item(i).getNodeValue(); + } + } + return s; + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/OfficeReader.java b/source/java/writer2latex/office/OfficeReader.java new file mode 100644 index 0000000..1b64924 --- /dev/null +++ b/source/java/writer2latex/office/OfficeReader.java @@ -0,0 +1,1149 @@ +/************************************************************************ + * + * OfficeReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-22) + * + */ + +package writer2latex.office; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.HashSet; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Document; +import writer2latex.xmerge.OfficeDocument; +import writer2latex.util.Misc; + +/** <p> This class reads and collects global information about an OOo document. + * This includes styles, forms, information about indexes and references etc. + * </p> + */ +public class OfficeReader { + + /////////////////////////////////////////////////////////////////////////// + // Static methods + + /** Checks, if a node is an element in the text namespace + * @param node the node to check + * @return true if this is a text element + */ + public static boolean isTextElement(Node node) { + return node.getNodeType()==Node.ELEMENT_NODE && + node.getNodeName().startsWith(XMLString.TEXT_); + } + + /** Checks, if a node is an element in the table namespace + * @param node the node to check + * @return true if this is a table element + */ + public static boolean isTableElement(Node node) { + return node.getNodeType()==Node.ELEMENT_NODE && + node.getNodeName().startsWith(XMLString.TABLE_); + } + + /** Checks, if a node is an element in the draw namespace + * @param node the node to check + * @return true if this is a draw element + */ + public static boolean isDrawElement(Node node) { + return node.getNodeType()==Node.ELEMENT_NODE && + node.getNodeName().startsWith(XMLString.DRAW_); + } + + /** Checks, if a node is an element representing a note (footnote/endnote) + * @param node the node to check + * @return true if this is a note element + */ + public static boolean isNoteElement(Node node) { + return node.getNodeType()==Node.ELEMENT_NODE && + ( node.getNodeName().equals(XMLString.TEXT_NOTE) || + node.getNodeName().equals(XMLString.TEXT_FOOTNOTE) || + node.getNodeName().equals(XMLString.TEXT_ENDNOTE) ); + } + + /** Checks, if this node contains at most one element, and that this is a + * paragraph. + * @param node the node to check + * @return true if the node contains a single paragraph or nothing + */ + public static boolean isSingleParagraph(Node node) { + boolean bFoundPar = false; + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + if (child.getNodeName().equals(XMLString.TEXT_P)) { + if (bFoundPar) { return false; } + else { bFoundPar = true; } + } + else { + return false; + } + } + child = child.getNextSibling(); + } + return bFoundPar; + } + + /** <p>Checks, if the only text content of this node is whitespace</p> + * @param node the node to check (should be a paragraph node or a child + * of a paragraph node) + * @return true if the node contains whitespace only + */ + public static boolean isWhitespaceContent(Node node) { + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + if (child.getNodeName().equals(XMLString.TEXT_SPAN)) { + if (!isWhitespaceContent(child)) { return false; } + } + else if (child.getNodeName().equals(XMLString.TEXT_A)) { + if (!isWhitespaceContent(child)) { return false; } + } + else if (child.getNodeName().equals(XMLString.TEXT_BIBLIOGRAPHY_MARK)) { + if (!isWhitespaceContent(child)) { return false; } + } + else if (!isTextElement(child)) { + return false; // found non-text content! + } + } + else if (child.getNodeType()==Node.TEXT_NODE) { + if (!isWhitespace(child.getNodeValue())) { return false; } + } + child = child.getNextSibling(); + } + return true; // found nothing! + } + + /** <p>Checks, if this text is whitespace</p> + * @param s the String to check + * @return true if the String contains whitespace only + */ + public static boolean isWhitespace(String s) { + int nLen = s.length(); + for (int i=0; i<nLen; i++) { + if (!Character.isWhitespace(s.charAt(i))) { return false; } + } + return true; + } + + /** Counts the number of characters (text nodes) in this element + * excluding footnotes etc. + * @param node the node to count in + * @return the number of characters + */ + public static int getCharacterCount(Node node) { + Node child = node.getFirstChild(); + int nCount = 0; + while (child!=null) { + short nodeType = child.getNodeType(); + + switch (nodeType) { + case Node.TEXT_NODE: + nCount += child.getNodeValue().length(); + break; + + case Node.ELEMENT_NODE: + String sName = child.getNodeName(); + if (sName.equals(XMLString.TEXT_S)) { + nCount += Misc.getPosInteger(Misc.getAttribute(child,XMLString.TEXT_C),1); + } + else if (sName.equals(XMLString.TEXT_TAB_STOP)) { + nCount++; // treat as single space + } + else if (sName.equals(XMLString.TEXT_TAB)) { // oasis + nCount++; // treat as single space + } + else if (isNoteElement(child)) { + // ignore + } + else if (isTextElement(child)) { + nCount += getCharacterCount(child); + } + } + child = child.getNextSibling(); + } + return nCount; + } + + public String getTextContent(Node node) { + String s = ""; + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + s += getTextContent(child); + } + else if (child.getNodeType()==Node.TEXT_NODE) { + s += child.getNodeValue(); + } + child = child.getNextSibling(); + } + return s; + } + + /** Return the next character in logical order + */ + public static char getNextChar(Node node) { + Node next = node; + do { + next = getNextNode(next); + if (next!=null && next.getNodeType()==Node.TEXT_NODE && + next.getNodeValue().length()>0) { + // Found the next character! + return next.getNodeValue().charAt(0); + } + //else if (next!=null && next.getNodeType()==Node.ELEMENT_NODE && + //XMLString.TEXT_S.equals(next.getNodeName())) { + // Next character is a space (first of several) + //return ' '; + //} + } while (next!=null); + // No more text in this paragraph! + return '\u0000'; + } + + // Return the next node of *this paragraph* in logical order + // (Parents before children, siblings from left to right) + // Do not descend into draw elements and footnotes/endnotes + private static Node getNextNode(Node node) { + // If element node: Next node is first child + if (node.getNodeType()==Node.ELEMENT_NODE && node.hasChildNodes() && + !isDrawElement(node) && !isNoteElement(node)) { + return node.getFirstChild(); + } + // else iterate for next node, but don't leave this paragraph + Node next = node; + do { + // First look for next sibling + if (next.getNextSibling()!=null) { return next.getNextSibling(); } + // Then move to parent, if this is the text:p node, we are done + next = next.getParentNode(); + if (next.getNodeType()==Node.ELEMENT_NODE && + next.getNodeName().equals(XMLString.TEXT_P)) { + return null; + } + } while (next!=null); + return null; + } + + /////////////////////////////////////////////////////////////////////////// + // Data + + // The Document + private OfficeDocument oooDoc = null; + + // Font declarations + private OfficeStyleFamily font = new OfficeStyleFamily(FontDeclaration.class); + + // Styles + private OfficeStyleFamily text = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily par = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily section = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily table = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily column = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily row = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily cell = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily frame = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily presentation = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily drawingPage = new OfficeStyleFamily(StyleWithProperties.class); + private OfficeStyleFamily list = new OfficeStyleFamily(ListStyle.class); + private OfficeStyleFamily pageLayout = new OfficeStyleFamily(PageLayout.class); + private OfficeStyleFamily masterPage = new OfficeStyleFamily(MasterPage.class); + + // Document-wide styles + private ListStyle outline = new ListStyle(); + private PropertySet footnotes = null; + private PropertySet endnotes = null; + + // Special styles + private StyleWithProperties[] heading = new StyleWithProperties[11]; + private MasterPage firstMasterPage = null; + //private String sFirstMasterPageName = null; + + // All indexes + private Hashtable indexes = new Hashtable(); + private HashSet indexSourceStyles = new HashSet(); + private HashSet figureSequenceNames = new HashSet(); + private HashSet tableSequenceNames = new HashSet(); + private String sAutoFigureSequenceName = null; + private String sAutoTableSequenceName = null; + + // Map paragraphs to sequence names (caption helper) + private Hashtable sequenceNames = new Hashtable(); + + // Map sequence reference names to sequence names + private Hashtable seqrefNames = new Hashtable(); + + // All references + private HashSet footnoteRef = new HashSet(); + private HashSet endnoteRef = new HashSet(); + private HashSet referenceRef = new HashSet(); + private HashSet bookmarkRef = new HashSet(); + private HashSet sequenceRef = new HashSet(); + + // Reference marks and bookmarks contained in headings + private HashSet referenceHeading = new HashSet(); + private HashSet bookmarkHeading = new HashSet(); + + // All internal hyperlinks + private HashSet links = new HashSet(); + + // Forms + private FormsReader forms = new FormsReader(); + + // The main content element + private Element content = null; + + // Identify OASIS OpenDocument format + private boolean bOpenDocument = false; + + // Identify individual genres + private boolean bText = false; + private boolean bSpreadsheet = false; + private boolean bPresentation = false; + + /////////////////////////////////////////////////////////////////////////// + // Various methods + + /** Checks whether or not this document is in package format + * @return true if it's in package format + */ + public boolean isPackageFormat() { return oooDoc.isPackageFormat(); } + + /** Checks whether this url is internal to the package + * @param sUrl the url to check + * @return true if the url is internal to the package + */ + public boolean isInPackage(String sUrl) { + if (!bOpenDocument && sUrl.startsWith("#")) { return true; } // old format + if (sUrl.startsWith("./")) { sUrl=sUrl.substring(2); } + return oooDoc.getEmbeddedObject(sUrl)!=null; + } + + /////////////////////////////////////////////////////////////////////////// + // Accessor methods + + /** <p>Get the collection of all font declarations.</p> + * @return the <code>OfficeStyleFamily</code> of font declarations + */ + public OfficeStyleFamily getFontDeclarations() { return font; } + + /** <p>Get a specific font declaration</p> + * @param sName the name of the font declaration + * @return a <code>FontDeclaration</code> representing the font + */ + public FontDeclaration getFontDeclaration(String sName) { + return (FontDeclaration) font.getStyle(sName); + } + + // Accessor methods for styles + public OfficeStyleFamily getTextStyles() { return text; } + public StyleWithProperties getTextStyle(String sName) { + return (StyleWithProperties) text.getStyle(sName); + } + + public OfficeStyleFamily getParStyles() { return par; } + public StyleWithProperties getParStyle(String sName) { + return (StyleWithProperties) par.getStyle(sName); + } + public StyleWithProperties getDefaultParStyle() { + return (StyleWithProperties) par.getDefaultStyle(); + } + + public OfficeStyleFamily getSectionStyles() { return section; } + public StyleWithProperties getSectionStyle(String sName) { + return (StyleWithProperties) section.getStyle(sName); + } + + public OfficeStyleFamily getTableStyles() { return table; } + public StyleWithProperties getTableStyle(String sName) { + return (StyleWithProperties) table.getStyle(sName); + } + public OfficeStyleFamily getColumnStyles() { return column; } + public StyleWithProperties getColumnStyle(String sName) { + return (StyleWithProperties) column.getStyle(sName); + } + + public OfficeStyleFamily getRowStyles() { return row; } + public StyleWithProperties getRowStyle(String sName) { + return (StyleWithProperties) row.getStyle(sName); + } + + public OfficeStyleFamily getCellStyles() { return cell; } + public StyleWithProperties getCellStyle(String sName) { + return (StyleWithProperties) cell.getStyle(sName); + } + public StyleWithProperties getDefaultCellStyle() { + return (StyleWithProperties) cell.getDefaultStyle(); + } + + public OfficeStyleFamily getFrameStyles() { return frame; } + public StyleWithProperties getFrameStyle(String sName) { + return (StyleWithProperties) frame.getStyle(sName); + } + public StyleWithProperties getDefaultFrameStyle() { + return (StyleWithProperties) frame.getDefaultStyle(); + } + + public OfficeStyleFamily getPresentationStyles() { return presentation; } + public StyleWithProperties getPresentationStyle(String sName) { + return (StyleWithProperties) presentation.getStyle(sName); + } + public StyleWithProperties getDefaultPresentationStyle() { + return (StyleWithProperties) presentation.getDefaultStyle(); + } + + public OfficeStyleFamily getDrawingPageStyles() { return drawingPage; } + public StyleWithProperties getDrawingPageStyle(String sName) { + return (StyleWithProperties) drawingPage.getStyle(sName); + } + public StyleWithProperties getDefaultDrawingPageStyle() { + return (StyleWithProperties) drawingPage.getDefaultStyle(); + } + + public OfficeStyleFamily getListStyles() { return list; } + public ListStyle getListStyle(String sName) { + return (ListStyle) list.getStyle(sName); + } + + public OfficeStyleFamily getPageLayouts() { return pageLayout; } + public PageLayout getPageLayout(String sName) { + return (PageLayout) pageLayout.getStyle(sName); + } + + public OfficeStyleFamily getMasterPages() { return masterPage; } + public MasterPage getMasterPage(String sName) { + return (MasterPage) masterPage.getStyle(sName); + } + + public ListStyle getOutlineStyle() { return outline; } + + public PropertySet getFootnotesConfiguration() { return footnotes; } + + public PropertySet getEndnotesConfiguration() { return endnotes; } + + /** <p>Returns the paragraph style associated with headings of a specific + * level. Returns <code>null</code> if no such style is known. + * <p>In principle, different styles can be used for each heading, in + * practice the same (soft) style is used for all headings of a specific + * level. + * @param nLevel the level of the heading + * @return a <code>StyleWithProperties</code> object representing the style + */ + public StyleWithProperties getHeadingStyle(int nLevel) { + return 1<=nLevel && nLevel<=10 ? heading[nLevel] : null; + } + + /** <p>Returns the first master page used in the document. If no master + * page is used explicitly, the first master page found in the styles is + * returned. Returns null if no master pages exists. + * @return a <code>MasterPage</code> object representing the master page + */ + public MasterPage getFirstMasterPage() { return firstMasterPage; } + + /** Return the iso language used in most paragaph styles (in a well-structured + * document this will be the default language) + * TODO: Base on content rather than style + * @return the iso language + */ + public String getMajorityLanguage() { + Hashtable langs = new Hashtable(); + + // Read the default language from the default paragraph style + String sDefaultLang = null; + StyleWithProperties style = getDefaultParStyle(); + if (style!=null) { + sDefaultLang = style.getProperty(XMLString.FO_LANGUAGE); + } + + // Collect languages from paragraph styles + Enumeration enumeration = getParStyles().getStylesEnumeration(); + while (enumeration.hasMoreElements()) { + style = (StyleWithProperties) enumeration.nextElement(); + String sLang = style.getProperty(XMLString.FO_LANGUAGE); + if (sLang==null) { sLang = sDefaultLang; } + if (sLang!=null) { + int nCount = 1; + if (langs.containsKey(sLang)) { + nCount = ((Integer) langs.get(sLang)).intValue()+1; + } + langs.put(sLang,new Integer(nCount)); + } + } + + // Find the most common language + int nMaxCount = 0; + String sMajorityLanguage = null; + enumeration = langs.keys(); + while (enumeration.hasMoreElements()) { + String sLang = (String) enumeration.nextElement(); + int nCount = ((Integer) langs.get(sLang)).intValue(); + if (nCount>nMaxCount) { + nMaxCount = nCount; + sMajorityLanguage = sLang; + } + } + return sMajorityLanguage; + } + + + /** <p>Returns a reader for a specific toc + * @param onode the <code>text:table-of-content-node</code> + * @return the reader, or null + */ + public TocReader getTocReader(Element onode) { + if (indexes.containsKey(onode)) { return (TocReader) indexes.get(onode); } + else { return null; } + } + + /** <p>Is this style used in some toc as an index source style?</p> + * @param sStyleName the name of the style + * @return true if this is an index source style + */ + public boolean isIndexSourceStyle(String sStyleName) { + return indexSourceStyles.contains(sStyleName); + } + + /** <p>Does this sequence name belong to a lof?</p> + * @param sName the name of the sequence + * @return true if it belongs to an index + */ + public boolean isFigureSequenceName(String sName) { + return figureSequenceNames.contains(sName); + } + + /** <p>Does this sequence name belong to a lot?</p> + * @param sName the name of the sequence + * @return true if it belongs to an index + */ + public boolean isTableSequenceName(String sName) { + return tableSequenceNames.contains(sName); + } + + /** <p>Add a sequence name for table captions.</p> + * <p>OpenDocument has a very weak notion of table captions: A caption is a + * paragraph containing a text:sequence element. Moreover, the only source + * to identify which sequence number to use is the list(s) of tables. + * If there's no list of tables, captions cannot be identified. + * Thus this method lets the user add a sequence name to identify the + * table captions. + * @param sName the name to add + */ + public void addTableSequenceName(String sName) { + tableSequenceNames.add(sName); + } + + /** <p>Add a sequence name for figure captions.</p> + * <p>OpenDocument has a very weak notion of figure captions: A caption is a + * paragraph containing a text:sequence element. Moreover, the only source + * to identify which sequence number to use is the list(s) of figures. + * If there's no list of figures, captions cannot be identified. + * Thus this method lets the user add a sequence name to identify the + * figure captions. + * @param sName the name to add + */ + public void addFigureSequenceName(String sName) { + figureSequenceNames.add(sName); + } + /** <p>Get the sequence name associated with a paragraph</p> + * @param par the paragraph to look up + * @return the sequence name or null + */ + public String getSequenceName(Element par) { + return sequenceNames.containsKey(par) ? (String) sequenceNames.get(par) : null; + } + + /** <p>Get the sequence name associated with a reference name</p> + * @param sRefName the reference name to use + * @return the sequence name or null + */ + public String getSequenceFromRef(String sRefName) { + return (String) seqrefNames.get(sRefName); + } + + + /** <p>Is there a reference to this footnote id? + * @param sId the id of the footnote + * @return true if there is a reference + */ + public boolean hasFootnoteRefTo(String sId) { + return footnoteRef.contains(sId); + } + + /** <p>Is there a reference to this endnote? + * @param sId the id of the endnote + * @return true if there is a reference + */ + public boolean hasEndnoteRefTo(String sId) { + return endnoteRef.contains(sId); + } + + /** Is this reference mark contained in a heading? + * @param sName the name of the reference mark + * @return true if so + */ + public boolean referenceMarkInHeading(String sName) { + return referenceHeading.contains(sName); + } + + /** Is there a reference to this reference mark? + * @param sName the name of the reference mark + * @return true if there is a reference + */ + public boolean hasReferenceRefTo(String sName) { + return referenceRef.contains(sName); + } + + /** Is this bookmark contained in a heading? + * @param sName the name of the bookmark + * @return true if so + */ + public boolean bookmarkInHeading(String sName) { + return bookmarkHeading.contains(sName); + } + + /** <p>Is there a reference to this bookmark? + * @param sName the name of the bookmark + * @return true if there is a reference + */ + public boolean hasBookmarkRefTo(String sName) { + return bookmarkRef.contains(sName); + } + + /** <p>Is there a reference to this sequence field? + * @param sId the id of the sequence field + * @return true if there is a reference + */ + public boolean hasSequenceRefTo(String sId) { + return sequenceRef.contains(sId); + } + + /** <p>Is there a link to this sequence anchor name? + * @param sName the name of the anchor + * @return true if there is a link + */ + public boolean hasLinkTo(String sName) { + return links.contains(sName); + } + + /** <p>Is this an OASIS OpenDocument or an OOo 1.0 document? + * @return true if it's an OASIS OpenDocument + */ + public boolean isOpenDocument() { return bOpenDocument; } + + /** <p>Is this an text document? + * @return true if it's a text document + */ + public boolean isText() { return bText; } + + /** <p>Is this a spreadsheet document? + * @return true if it's a spreadsheet document + */ + public boolean isSpreadsheet() { return bSpreadsheet; } + + /** <p>Is this a presentation document? + * @return true if it's a presentation document + */ + public boolean isPresentation() { return bPresentation; } + + /** <p>Get the content element</p> + * <p>In the old file format this means the <code>office:body</code> element + * <p>In the OpenDocument format this means a <code>office:text</code>, + * <code>office:spreadsheet</code> or <code>office:presentation</code> + * element.</p> + * @return the content <code>Element</code> + */ + public Element getContent() { + return content; + } + + /** <p>Get the forms belonging to this document.</p> + * @return a <code>FormsReader</code> representing the forms + */ + public FormsReader getForms() { return forms; } + + /** <p>Read a table from a table:table node</p> + * @param node the table:table Element node + * @return a <code>TableReader</code> object representing the table + */ + public TableReader getTableReader(Element node) { + return new TableReader(this,node); + } + + /** Constructor; read a document */ + public OfficeReader(OfficeDocument oooDoc, boolean bAllParagraphsAreSoft) { + this.oooDoc = oooDoc; + loadStylesFromDOM(oooDoc.getStyleDOM(),oooDoc.getContentDOM(),bAllParagraphsAreSoft); + loadContentFromDOM(oooDoc.getContentDOM()); + } + + /////////////////////////////////////////////////////////////////////////// + // Helpers + + /*private void collectMasterPage(StyleWithProperties style) { + if (style==null || firstMasterPage!=null) { return; } + String s = style.getMasterPageName(); + if (s!=null && s.length()>0) { + firstMasterPage = getMasterPage(s); + } + }*/ + + private void loadStylesFromDOM(Node node, boolean bAllParagraphsAreSoft) { + // node should be office:master-styles, office:styles or office:automatic-styles + boolean bAutomatic = XMLString.OFFICE_AUTOMATIC_STYLES.equals(node.getNodeName()); + if (node.hasChildNodes()){ + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++ ) { + Node child=nl.item(i); + if (child.getNodeType()==Node.ELEMENT_NODE){ + if (child.getNodeName().equals(XMLString.STYLE_STYLE)){ + String sFamily = Misc.getAttribute(child,XMLString.STYLE_FAMILY); + if ("text".equals(sFamily)){ + text.loadStyleFromDOM(child,bAutomatic); + } + else if ("paragraph".equals(sFamily)){ + par.loadStyleFromDOM(child,bAutomatic && !bAllParagraphsAreSoft); + } + else if ("section".equals(sFamily)){ + section.loadStyleFromDOM(child,bAutomatic); + } + else if ("table".equals(sFamily)){ + table.loadStyleFromDOM(child,bAutomatic); + } + else if ("table-column".equals(sFamily)){ + column.loadStyleFromDOM(child,bAutomatic); + } + else if ("table-row".equals(sFamily)){ + row.loadStyleFromDOM(child,bAutomatic); + } + else if ("table-cell".equals(sFamily)){ + cell.loadStyleFromDOM(child,bAutomatic); + } + else if ("graphics".equals(sFamily)){ + frame.loadStyleFromDOM(child,bAutomatic); + } + else if ("graphic".equals(sFamily)){ // oasis + frame.loadStyleFromDOM(child,bAutomatic); + } + else if ("presentation".equals(sFamily)){ + presentation.loadStyleFromDOM(child,bAutomatic); + } + else if ("drawing-page".equals(sFamily)){ + // Bug in OOo 1.x: The same name may be used for a real and an automatic style... + if (drawingPage.getStyle(Misc.getAttribute(child,XMLString.STYLE_NAME))==null) { + drawingPage.loadStyleFromDOM(child,bAutomatic); + } + } + } + else if (child.getNodeName().equals(XMLString.STYLE_PAGE_MASTER)) { // old + pageLayout.loadStyleFromDOM(child,bAutomatic); + } + else if (child.getNodeName().equals(XMLString.STYLE_PAGE_LAYOUT)) { // oasis + pageLayout.loadStyleFromDOM(child,bAutomatic); + } + else if (child.getNodeName().equals(XMLString.STYLE_MASTER_PAGE)) { + masterPage.loadStyleFromDOM(child,bAutomatic); + if (firstMasterPage==null) { + firstMasterPage = (MasterPage) masterPage.getStyle(Misc.getAttribute(child,XMLString.STYLE_NAME)); + } + } + else if (child.getNodeName().equals(XMLString.TEXT_LIST_STYLE)) { + list.loadStyleFromDOM(child,bAutomatic); + } + else if (child.getNodeName().equals(XMLString.TEXT_OUTLINE_STYLE)) { + outline.loadStyleFromDOM(child); + } + else if (child.getNodeName().equals(XMLString.STYLE_DEFAULT_STYLE)){ + String sFamily = Misc.getAttribute(child,XMLString.STYLE_FAMILY); + if ("paragraph".equals(sFamily)) { + StyleWithProperties defaultPar = new StyleWithProperties(); + defaultPar.loadStyleFromDOM(child); + par.setDefaultStyle(defaultPar); + } + else if ("graphics".equals(sFamily) || "graphic".equals(sFamily)) { // oasis: no s + StyleWithProperties defaultFrame = new StyleWithProperties(); + defaultFrame.loadStyleFromDOM(child); + frame.setDefaultStyle(defaultFrame); + } + else if ("table-cell".equals(sFamily)) { + StyleWithProperties defaultCell = new StyleWithProperties(); + defaultCell.loadStyleFromDOM(child); + cell.setDefaultStyle(defaultCell); + } + } + } + } + } + } + + private void loadStylesFromDOM(Document stylesDOM, Document contentDOM, boolean bAllParagraphsAreSoft){ + // Flat xml: stylesDOM will be null and contentDOM contain everything + // This is only the case for old versions of xmerge; newer versions + // creates DOM for styles, content, meta and settings. + NodeList list; + + // font declarations: Try old format first + if (stylesDOM==null) { + list = contentDOM.getElementsByTagName(XMLString.OFFICE_FONT_DECLS); + } + else { + list = stylesDOM.getElementsByTagName(XMLString.OFFICE_FONT_DECLS); + } + // If that fails, try oasis format + if (list.getLength()==0) { + if (stylesDOM==null) { + list = contentDOM.getElementsByTagName(XMLString.OFFICE_FONT_FACE_DECLS); + } + else { + list = stylesDOM.getElementsByTagName(XMLString.OFFICE_FONT_FACE_DECLS); + } + } + + if (list.getLength()!=0) { + Node node = list.item(0); + if (node.hasChildNodes()){ + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++ ) { + Node child = nl.item(i); + if (child.getNodeType()==Node.ELEMENT_NODE){ + if (child.getNodeName().equals(XMLString.STYLE_FONT_DECL)){ + font.loadStyleFromDOM(child,false); + } + else if (child.getNodeName().equals(XMLString.STYLE_FONT_FACE)){ + font.loadStyleFromDOM(child,false); + } + } + } + } + } + + // soft formatting: + if (stylesDOM==null) { + list = contentDOM.getElementsByTagName(XMLString.OFFICE_STYLES); + } + else { + list = stylesDOM.getElementsByTagName(XMLString.OFFICE_STYLES); + } + if (list.getLength()!=0) { + loadStylesFromDOM(list.item(0),bAllParagraphsAreSoft); + } + + // master styles: + if (stylesDOM==null) { + list = contentDOM.getElementsByTagName(XMLString.OFFICE_MASTER_STYLES); + } + else { + list = stylesDOM.getElementsByTagName(XMLString.OFFICE_MASTER_STYLES); + } + if (list.getLength()!=0) { + loadStylesFromDOM(list.item(0),bAllParagraphsAreSoft); + } + + // hard formatting: + // Load from styles.xml first. Problem: There may be name clashes + // with automatic styles from content.xml + if (stylesDOM!=null) { + list = stylesDOM.getElementsByTagName(XMLString.OFFICE_AUTOMATIC_STYLES); + if (list.getLength()!=0) { + loadStylesFromDOM(list.item(0),bAllParagraphsAreSoft); + } + } + list = contentDOM.getElementsByTagName(XMLString.OFFICE_AUTOMATIC_STYLES); + if (list.getLength()!=0) { + loadStylesFromDOM(list.item(0),bAllParagraphsAreSoft); + } + + // footnotes configuration: + if (stylesDOM==null) { + list = contentDOM.getElementsByTagName(XMLString.TEXT_FOOTNOTES_CONFIGURATION); + } + else { + list = stylesDOM.getElementsByTagName(XMLString.TEXT_FOOTNOTES_CONFIGURATION); + } + if (list.getLength()!=0) { + footnotes = new PropertySet(); + footnotes.loadFromDOM(list.item(0)); + } + + // endnotes configuration: + if (stylesDOM==null) { + list = contentDOM.getElementsByTagName(XMLString.TEXT_ENDNOTES_CONFIGURATION); + } + else { + list = stylesDOM.getElementsByTagName(XMLString.TEXT_ENDNOTES_CONFIGURATION); + } + if (list.getLength()!=0) { + endnotes = new PropertySet(); + endnotes.loadFromDOM(list.item(0)); + } + + // if it failed, try oasis format + if (footnotes==null || endnotes==null) { + if (stylesDOM==null) { + list = contentDOM.getElementsByTagName(XMLString.TEXT_NOTES_CONFIGURATION); + } + else { + list = stylesDOM.getElementsByTagName(XMLString.TEXT_NOTES_CONFIGURATION); + } + int nLen = list.getLength(); + for (int i=0; i<nLen; i++) { + String sClass = Misc.getAttribute(list.item(i),XMLString.TEXT_NOTE_CLASS); + if ("endnote".equals(sClass)) { + endnotes = new PropertySet(); + endnotes.loadFromDOM(list.item(i)); + } + else { + footnotes = new PropertySet(); + footnotes.loadFromDOM(list.item(i)); + } + } + } + + + } + + private void loadContentFromDOM(Document contentDOM) { + // Get the office:body element + NodeList list = contentDOM.getElementsByTagName(XMLString.OFFICE_BODY); + if (list.getLength()>0) { + // There may be several bodies, but the first one is the main body + Element body = (Element) list.item(0); + + // Now get the content and identify the type of document + content = Misc.getChildByTagName(body,XMLString.OFFICE_TEXT); + if (content!=null) { // OpenDocument Text + bOpenDocument = true; bText = true; + } + else { + content = Misc.getChildByTagName(body,XMLString.OFFICE_SPREADSHEET); + if (content!=null) { // OpenDocument Spreadsheet + bOpenDocument = true; bSpreadsheet = true; + } + else { + content = Misc.getChildByTagName(body,XMLString.OFFICE_PRESENTATION); + if (content!=null) { // OpenDocument Presentation + bOpenDocument = true; bPresentation = true; + } + else { + content = body; + // OOo 1.x file format - look through content to determine genre + bSpreadsheet = true; + bPresentation = false; + Node child = body.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (XMLString.TEXT_P.equals(sName)) { + bSpreadsheet = false; + } + else if (XMLString.TEXT_H.equals(sName)) { + bSpreadsheet = false; + } + else if (XMLString.TEXT_ORDERED_LIST.equals(sName)) { + bSpreadsheet = false; + } + else if (XMLString.TEXT_ORDERED_LIST.equals(sName)) { + bSpreadsheet = false; + } + else if (XMLString.TEXT_SECTION.equals(sName)) { + bSpreadsheet = false; + } + else if (XMLString.DRAW_PAGE.equals(sName)) { + bPresentation = true; bSpreadsheet = false; + } + else if (XMLString.DRAW_PAGE.equals(sName)) { + bSpreadsheet = false; + } + } + child = child.getNextSibling(); + } + bText = !bSpreadsheet && !bPresentation; + } + } + } + + traverseContent(body); + + if (sAutoFigureSequenceName!=null) { + addFigureSequenceName(sAutoFigureSequenceName); + } + if (sAutoTableSequenceName!=null) { + addTableSequenceName(sAutoTableSequenceName); + } + } + + /*if (firstMasterPage==null) { + firstMasterPage = getMasterPage(sFirstMasterPageName); + }*/ + } + + private Element getParagraph(Element node) { + Element parent = (Element) node.getParentNode(); + if (parent.getTagName().equals(XMLString.TEXT_P) || parent.getTagName().equals(XMLString.TEXT_H)) { + return parent; + } + return getParagraph(parent); + } + + private void traverseContent(Element node) { + // Handle this node first + String sName = node.getTagName(); + if (sName.equals(XMLString.TEXT_P)) { + //collectMasterPage(getParStyle(node.getAttribute(XMLString.TEXT_STYLE_NAME))); + } + else if (sName.equals(XMLString.TEXT_H)) { + int nLevel = Misc.getPosInteger(node.getAttribute(XMLString.TEXT_LEVEL),1); + StyleWithProperties style = getParStyle(node.getAttribute(XMLString.TEXT_STYLE_NAME)); + //collectMasterPage(style); + if (1<=nLevel && nLevel<=10 && heading[nLevel]==null) { + if (style!=null && style.isAutomatic()) { + heading[nLevel] = getParStyle(style.getParentName()); + } + else { + heading[nLevel] = null; + } + } + } + else if (sName.equals(XMLString.TEXT_SEQUENCE)) { + String sSeqName = Misc.getAttribute(node,XMLString.TEXT_NAME); + String sRefName = Misc.getAttribute(node,XMLString.TEXT_REF_NAME); + if (sSeqName!=null) { + Element par = getParagraph(node); + if (!sequenceNames.containsKey(par)) { + // Only the first text:seqence should be registered as possible caption sequence + sequenceNames.put(par,sSeqName); + } + if (sRefName!=null) { + seqrefNames.put(sRefName,sSeqName); + } + } + } + else if (sName.equals(XMLString.TEXT_FOOTNOTE_REF)) { + collectRefName(footnoteRef,node); + } + else if (sName.equals(XMLString.TEXT_ENDNOTE_REF)) { + collectRefName(endnoteRef,node); + } + else if (sName.equals(XMLString.TEXT_NOTE_REF)) { // oasis + String sClass = Misc.getAttribute(node,XMLString.TEXT_NOTE_CLASS); + if ("footnote".equals(sClass)) { collectRefName(footnoteRef,node); } + else if ("endnote".equals(sClass)) { collectRefName(endnoteRef,node); } + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK)) { + collectMarkInHeading(referenceHeading,node); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK_START)) { + collectMarkInHeading(referenceHeading,node); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_REF)) { + collectRefName(referenceRef,node); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK)) { + collectMarkInHeading(bookmarkHeading,node); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK_START)) { + collectMarkInHeading(bookmarkHeading,node); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK_REF)) { + collectRefName(bookmarkRef,node); + } + else if (sName.equals(XMLString.TEXT_SEQUENCE_REF)) { + collectRefName(sequenceRef,node); + } + else if (sName.equals(XMLString.TEXT_A)) { + String sHref = node.getAttribute(XMLString.XLINK_HREF); + if (sHref!=null && sHref.startsWith("#")) { + links.add(sHref.substring(1)); + } + } + else if (sName.equals(XMLString.OFFICE_FORMS)) { + forms.read(node); + } + else if (sName.equals(XMLString.TEXT_TABLE_OF_CONTENT)) { + TocReader tocReader = new TocReader(node); + indexes.put(node,tocReader); + indexSourceStyles.addAll(tocReader.getIndexSourceStyles()); + } + else if (sName.equals(XMLString.TEXT_TABLE_INDEX) || + sName.equals(XMLString.TEXT_ILLUSTRATION_INDEX)) { + LoftReader loftReader = new LoftReader(node); + indexes.put(node,loftReader); + if (loftReader.useCaption()) { + if (loftReader.isTableIndex()) { + tableSequenceNames.add(loftReader.getCaptionSequenceName()); + } + else { + figureSequenceNames.add(loftReader.getCaptionSequenceName()); + } + } + } + // todo: other indexes + + // Traverse the children + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + traverseContent((Element) child); + } + child = child.getNextSibling(); + } + + // Collect automatic captions sequences + // Use OOo defaults: Captions have style names Illustration and Table resp. + if ((sAutoFigureSequenceName==null || sAutoTableSequenceName==null) && sName.equals(XMLString.TEXT_P)) { + String sStyleName = getParStyles().getDisplayName(node.getAttribute(XMLString.TEXT_STYLE_NAME)); + if (sAutoFigureSequenceName==null) { + if ("Illustration".equals(sStyleName)) { + sAutoFigureSequenceName = getSequenceName(node); + } + } + if (sAutoTableSequenceName==null) { + if ("Table".equals(sStyleName)) { + sAutoTableSequenceName = getSequenceName(node); + } + } + } + + } + + private void collectRefName(HashSet ref, Element node) { + String sRefName = node.getAttribute(XMLString.TEXT_REF_NAME); + if (sRefName!=null && sRefName.length()>0) { + ref.add(sRefName); + } + } + + private void collectMarkInHeading(HashSet marks, Element node) { + String sName = node.getAttribute(XMLString.TEXT_NAME); + if (sName!=null && sName.length()>0) { + Element par = getParagraph(node); + if (XMLString.TEXT_H.equals(par.getTagName())) { + marks.add(sName); + } + } + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/OfficeStyle.java b/source/java/writer2latex/office/OfficeStyle.java new file mode 100644 index 0000000..e613f65 --- /dev/null +++ b/source/java/writer2latex/office/OfficeStyle.java @@ -0,0 +1,66 @@ +/************************************************************************ + * + * OfficeStyle.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2006 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2006-11-23) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; +import writer2latex.util.Misc; + +/** <p> Abstract class representing a style in OOo </p> */ +public abstract class OfficeStyle { + // These attributes are defined by OfficeStyleFamily upon collection of styles + protected String sName; + protected OfficeStyleFamily family; + protected boolean bAutomatic; + + private String sDisplayName; + private String sParentName; + private String sListStyleName; + private String sMasterPageName; + + public String getName() { return sName; } + + public OfficeStyleFamily getFamily() { return family; } + + public boolean isAutomatic() { return bAutomatic; } + + public String getDisplayName() { return sDisplayName; } + + public String getParentName() { return sParentName; } + + public String getListStyleName() { return sListStyleName; } + + public String getMasterPageName() { return sMasterPageName; } + + public void loadStyleFromDOM(Node node){ + sDisplayName = Misc.getAttribute(node,XMLString.STYLE_DISPLAY_NAME); + if (sDisplayName==null) { sDisplayName = sName; } + sParentName = Misc.getAttribute(node,XMLString.STYLE_PARENT_STYLE_NAME); + sListStyleName = Misc.getAttribute(node,XMLString.STYLE_LIST_STYLE_NAME); + sMasterPageName = Misc.getAttribute(node,XMLString.STYLE_MASTER_PAGE_NAME); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/OfficeStyleFamily.java b/source/java/writer2latex/office/OfficeStyleFamily.java new file mode 100644 index 0000000..36426e3 --- /dev/null +++ b/source/java/writer2latex/office/OfficeStyleFamily.java @@ -0,0 +1,137 @@ +/************************************************************************ + * + * OfficeStyleFamily.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; +import java.util.Hashtable; +import java.util.Enumeration; +import writer2latex.util.Misc; + +/** Container class representing a style family in OOo */ +public class OfficeStyleFamily { + private Hashtable styles = new Hashtable(); + private Class styleClass; + + private Hashtable displayNames = new Hashtable(); + + private OfficeStyle defaultStyle = null; + + /** Create a new OfficeStyleFamily based on a class + * @param styleClass the subclass of OfficeStyle used to represent styles + * in this family + */ + public OfficeStyleFamily(Class styleClass) { + this.styleClass = styleClass; + } + + /** Define the default style for this family, ie. an unnamed style providing + * defaults for some style properties. This style cannot be found using + * getStyle or getStyleByDisplayName. + * @param style the new default style + */ + public void setDefaultStyle(OfficeStyle style) { + defaultStyle = style; + } + + /** Get the default style for this family + * @return the default style, or null if none is defined + */ + public OfficeStyle getDefaultStyle() { + return defaultStyle; + } + + /** Get a style by name + * @param sName the name of the style + * @return the style, or null if such a style does not exist + */ + public OfficeStyle getStyle(String sName) { + if (sName==null) { return null; } + else { return (OfficeStyle) styles.get(sName); } + } + + /** Get a style by display name. Automatic styles does not have a display + * name, so only common styles can be retrieved with this method + * @param sDisplayName the display name of the style + * @return the style, or null if such a style does not exist + */ + public OfficeStyle getStyleByDisplayName(String sDisplayName) { + if (sDisplayName==null) { return null; } + else { return getStyle((String) displayNames.get(sDisplayName)); } + } + + /** Get the display name for the style with the specified name. + * If this is an automatic style, the parent style is used + * @param sName the style name + * @return the display name, or null if the style does not exist + */ + public String getDisplayName(String sName) { + OfficeStyle style = getStyle(sName); + if (style==null) { return null; } + if (style.isAutomatic()) { + style = getStyle(style.getParentName()); + if (style==null) { return null; } + } + return style.getDisplayName(); + } + + /** Get all named styles in the family (ie. excluding the default style) + * @return an enumeration of all styles represented by OfficeStyle objects + */ + public Enumeration getStylesEnumeration(){ + return styles.elements(); + } + + /** Load a style from a DOM representation + * @param node the style:... node representing the style + * @param bAutomatic if true, the style is an automatic style + */ + public void loadStyleFromDOM(Node node, boolean bAutomatic) { + String sName = Misc.getAttribute(node,XMLString.STYLE_NAME); + if (sName!=null) { + try { + OfficeStyle style = (OfficeStyle) styleClass.newInstance(); + style.sName=sName; + style.family=this; + style.bAutomatic=bAutomatic; + style.loadStyleFromDOM(node); + styles.put(sName,style); + if (!bAutomatic) { + // Create backlink from display name to name + displayNames.put(style.getDisplayName(),sName); + } + } + catch (InstantiationException e) { + // Will not happen if a proper class is passed in the constructor + } + catch (IllegalAccessException e) { + // Should also not happen + } + } + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/Package.html b/source/java/writer2latex/office/Package.html new file mode 100644 index 0000000..a425c9c --- /dev/null +++ b/source/java/writer2latex/office/Package.html @@ -0,0 +1,13 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.office</title> +</head> + +<body> +<p>A collection of classes that reads ODF files.</p> +<p>In general the DOM tree is used directly, but these classes provides a more +convenient interface to specific parts of the document.</p> +</body> +</html> diff --git a/source/java/writer2latex/office/PageLayout.java b/source/java/writer2latex/office/PageLayout.java new file mode 100644 index 0000000..6226a56 --- /dev/null +++ b/source/java/writer2latex/office/PageLayout.java @@ -0,0 +1,91 @@ +/************************************************************************ + * + * PageLayout.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-03-17) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; +import writer2latex.util.Misc; + +/** <p> Class representing a page master in OOo Writer. This is represented + * like other styles + a separate style for header and footer</p> + */ +public class PageLayout extends StyleWithProperties { + private String sPageUsage = null; + + private boolean bHasHeaderStyle = false; + private PropertySet headerStyle = new PropertySet(); + + private boolean bHasFooterStyle = false; + private PropertySet footerStyle = new PropertySet(); + + + public String getPageUsage() { + return sPageUsage; + } + + public boolean hasHeaderStyle() { return bHasHeaderStyle; } + + public String getHeaderProperty(String sPropName) { + return headerStyle.getProperty(sPropName); + } + + public boolean hasFooterStyle() { return bHasFooterStyle; } + + public String getFooterProperty(String sPropName) { + return footerStyle.getProperty(sPropName); + } + + public void loadStyleFromDOM(Node node) { + super.loadStyleFromDOM(node); + sPageUsage = Misc.getAttribute(node,XMLString.STYLE_PAGE_USAGE); + + Node hsNode = Misc.getChildByTagName(node,XMLString.STYLE_HEADER_STYLE); + if (hsNode!=null) { + Node hsProperties = Misc.getChildByTagName(hsNode,XMLString.STYLE_PROPERTIES); + if (hsProperties==null) { // oasis: + hsProperties = Misc.getChildByTagName(hsNode,XMLString.STYLE_HEADER_FOOTER_PROPERTIES); + } + if (hsProperties!=null) { + bHasHeaderStyle = true; + headerStyle.loadFromDOM(hsProperties); + } + } + + Node fsNode = Misc.getChildByTagName(node,XMLString.STYLE_FOOTER_STYLE); + if (fsNode!=null) { + Node fsProperties = Misc.getChildByTagName(fsNode,XMLString.STYLE_PROPERTIES); + if (fsProperties==null) { // oasis: + fsProperties = Misc.getChildByTagName(fsNode,XMLString.STYLE_HEADER_FOOTER_PROPERTIES); + } + if (fsProperties!=null) { + bHasFooterStyle = true; + footerStyle.loadFromDOM(fsProperties); + } + } + + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/PropertySet.java b/source/java/writer2latex/office/PropertySet.java new file mode 100644 index 0000000..0c67af0 --- /dev/null +++ b/source/java/writer2latex/office/PropertySet.java @@ -0,0 +1,98 @@ +/************************************************************************ + * + * PropertySet.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-03-17) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; +import java.util.Enumeration; +import java.util.Hashtable; + +/** <p> Class representing a set of style properties in OOo (actually this + is simply the set of attributes of an element). </p> + */ +public class PropertySet { + private Hashtable properties = new Hashtable(); + private String sName; + + public PropertySet() { + properties = new Hashtable(); + sName=""; + } + + public String getProperty(String sPropName) { + if (sPropName!=null) { + String sValue = (String) properties.get(sPropName); + if (sValue!=null && sValue.endsWith("inch")) { + // Cut of inch to in + return sValue.substring(0,sValue.length()-2); + } + else { + return sValue; + } + } + else { + return null; + } + } + + public String getName() { return sName; } + + public void loadFromDOM(Node node) { + sName = node.getNodeName(); + // read the attributes of the node, if any + if (node!=null) { + NamedNodeMap attrNodes = node.getAttributes(); + if (attrNodes!=null) { + int nLen = attrNodes.getLength(); + for (int i=0; i<nLen; i++){ + Node attr = attrNodes.item(i); + properties.put(attr.getNodeName(),attr.getNodeValue()); + } + } + } + } + + public boolean containsProperty(String sProperty) { + return sProperty!=null && properties.containsKey(sProperty); + } + + public void setProperty(String sProperty, String sValue){ + properties.put(sProperty,sValue); + } + + public String toString() { + String s=""; + Enumeration keys = properties.keys(); + while (keys.hasMoreElements()) { + String sKey = (String) keys.nextElement(); + String sValue = (String) properties.get(sKey); + s += sKey+"="+sValue+" "; + } + return s; + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/SVMReader.java b/source/java/writer2latex/office/SVMReader.java new file mode 100644 index 0000000..131ca60 --- /dev/null +++ b/source/java/writer2latex/office/SVMReader.java @@ -0,0 +1,232 @@ +/************************************************************************ + * + * SVMReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2004 by Urban Widmark + * + * All Rights Reserved. + * + * Version 0.4 (2004-02-26) + * + */ + +package writer2latex.office; + +/** This class contains a static method to extract EPS data from an SVM file */ +public final class SVMReader { + + /* Notes on SVM file format: + + Relevant OO 1.1.0 Source Files include: + tools/source/stream/vcompat.cxx + vcl/source/gdi/gdimtf.cxx + vcl/inc/gdimtf.hxx + vcl/source/gdi/metaact.cxx + vcl/inc/metaact.hxx + vcl/source/gdi/mapmod.cxx + vcl/inc/mapmod.hxx + tools/source/generic/fract.cxx + tools/inc/fract.hxx + tools/source/generic/gen.cxx + tools/inc/gen.hxx + + VersionCompat (6 bytes) + version - 2 bytes + totalsize - 4 bytes + + Point (Pair) (8 bytes) + X - 4 bytes (long) + Y - 4 bytes (long) + + Fraction (8 bytes) + nNumerator - 4 bytes (long) + nDenominator - 4 bytes (long) + + MapMode (6 + 2 + 8 + 8 + 8 + 1 = 33 bytes) + VersionCompat + meUnit - UINT16 + maOrigin - Point + maScaleX - Fraction + maScaleY - Fraction + mbSimple - BOOL (1 byte) + + Size (Pair) (8 bytes) + width - 4 bytes (long) + height - 4 bytes (long) + + GfxLink (16 or 57 bytes) + VersionCompat + nType - 2 bytes + nSize - 4 bytes + nUserId - 4 bytes + aSize - Size (version >=2) + aMapMode - MapMode (version >=2) + + MetaEPSAction + VersionCompat + maGfxLink - GfxLink + data[maGfxLink.nSize] - bytes + maPoint - Point + maSize - Size + maSubst - GDIMetaFile (alternative image?) + + SVM file + "VCLMTF" + Compat - VersionCompat + nStmCompressMode - UINT32 + aPrefMapMode - MapMode + aPrefSize - Size + count - UINT32 + action[count] - MetaAction + + + Example header from an EPS image included in a Writer document: + 00000000: 5643 4c4d 5446 0100 3100 0000 0000 0000 VCLMTF..1....... + 00000010: 0100 1b00 0000 0800 0000 0000 0000 0000 ................ + 00000020: 0100 0000 0100 0000 0100 0000 0100 0000 ................ + 00000030: 0169 0100 00fd 0000 0001 0000 008f 0001 .i.............. + 00000040: 0096 3200 0002 0033 0000 0001 003f 3000 ..2....3.....?0. + 00000050: 0000 0000 0000 0000 0000 0000 0001 001b ................ + 00000060: 0000 000a 0000 0000 0000 0000 0001 0000 ................ + 00000070: 0001 0000 0001 0000 0001 0000 0001 2521 ..............%! + + 5643 4c4d 5446 "VCLMTF" 0 + + 0100 version 6 + 3100 0000 totalsize + + 0000 0000 compress 12 + + 0100 aPrefMapMode.version 16 + 1b00 0000 aPrefMapMode.totalsize + 0800 aPrefMapMode.meUnit + 0000 0000 aPrefMapMode.maOrigin + 0000 0000 + 0100 0000 aPrefMapMode.maScaleX + 0100 0000 + 0100 0000 aPrefMapMode.maScaleY + 0100 0000 + 01 aPrefMapMode.mbSimple + + 6901 0000 aPrefSize 49 + fd00 0000 + + 0100 0000 nCount 57 + + 8f00 type == META_EPS_ACTION 61 + + 0100 version + 9632 0000 totalsize + + 0200 version + 3300 0000 totalsize + + 0100 nType + 3f30 0000 nSize 75 + 0000 0000 nUserId + + 0000 0000 aSize + 0000 0000 + + 0100 + 1b00 0000 + 0a00 + + 0000 0000 + 0000 0000 + 0100 0000 + 0100 0000 + 0100 0000 + 0100 0000 + 01 + + Beginning of EPS data: + + 2521 + + Note that maPoint/maSize/maSubst are all after the EPS file. + + */ + + /** Determine if this SVM contains an EPS document and retrieve start and + * end positions if so. + * + * @param blob byte array containing SVM file + * @param offlen integer array to retrieve the offset into the SVM file + * (offlen[0]) and the length (offlen[1]) of the EPS + * document. If the method returns false, the array + * will be unchanged. + * + * @return returns true if the SVM contains an EPS document + */ + public static final boolean readSVM(byte[] blob, int[] offlen) { + int pos = 57; + int nCount = getInt(blob, pos); + pos += 4; + + for (int i=0; i<nCount; i++) { + int type = getShort(blob, pos); + pos += 2; + + // We only understand META_EPS_ACTION + if (type != 143) + return false; + + pos += 6; + int version = getShort(blob, pos); + pos += 6; // version + totalsize + pos += 2; // nType + + // This is the size of the EPS data. + int size = getInt(blob, pos); + pos += 8; + + if (version >= 2) + pos += 41; + + for (int j=0; j<MIMETypes.EPS_SIG.length; j++) + if (MIMETypes.EPS_SIG[j] != blob[pos + j]) + return false; + + offlen[0] = pos; + offlen[1] = size; + + // For now we only understand files where the EPS entry is + // the first MetaAction + break; + } + + return true; + } + + private static int getInt(byte[] blob, int pos) + { + return + ((blob[pos+0] & 0xff) << 0) + + ((blob[pos+1] & 0xff) << 8) + + ((blob[pos+2] & 0xff) << 16) + + ((blob[pos+3] & 0xff) << 24); + } + + private static int getShort(byte[] blob, int pos) + { + return + ((blob[pos+0] & 0xff) << 0) + + ((blob[pos+1] & 0xff) << 8); + } + +} diff --git a/source/java/writer2latex/office/StyleWithProperties.java b/source/java/writer2latex/office/StyleWithProperties.java new file mode 100644 index 0000000..a86679c --- /dev/null +++ b/source/java/writer2latex/office/StyleWithProperties.java @@ -0,0 +1,298 @@ +/************************************************************************ + * + * StyleWithProperties.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + */ + +package writer2latex.office; + +//import org.w3c.dom.Element; +import org.w3c.dom.Node; +//import org.w3c.dom.NamedNodeMap; +//import java.util.Hashtable; +import writer2latex.util.Misc; + +/** <p> Class representing a style in OOo which contains a style:properties + * element </p> + */ +public class StyleWithProperties extends OfficeStyle { + private final static int OLDPROPS = 0; + private final static int TEXT = 1; + private final static int PAR = 2; + private final static int SECTION = 3; + private final static int TABLE = 4; + private final static int COLUMN = 5; + private final static int ROW = 6; + private final static int CELL = 7; + private final static int GRAPHIC = 8; + private final static int PAGE = 9; + //private final static int DRAWPAGE = 10; + private final static int COUNT = 10; + + private PropertySet[] properties = new PropertySet[COUNT]; + private boolean bIsOldProps = false; + + private PropertySet backgroundImageProperties = new PropertySet(); + + private int nColCount = 0; + + private boolean bHasFootnoteSep = false; + private PropertySet footnoteSep = new PropertySet(); + + + public StyleWithProperties() { + for (int i=0; i<COUNT; i++) { + properties[i] = new PropertySet(); + } + } + + public void loadStyleFromDOM(Node node) { + super.loadStyleFromDOM(node); + // read the properties of the style, if any + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (XMLString.STYLE_PROPERTIES.equals(sName)) { + bIsOldProps = true; // style:properties identifies old format + loadPropertiesFromDOM(OLDPROPS,child); + } + else if (XMLString.STYLE_TEXT_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(TEXT,child); + } + else if (XMLString.STYLE_PARAGRAPH_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(PAR,child); + } + else if (XMLString.STYLE_SECTION_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(SECTION,child); + } + else if (XMLString.STYLE_TABLE_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(TABLE,child); + } + else if (XMLString.STYLE_TABLE_COLUMN_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(COLUMN,child); + } + else if (XMLString.STYLE_TABLE_ROW_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(ROW,child); + } + else if (XMLString.STYLE_TABLE_CELL_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(CELL,child); + } + else if (XMLString.STYLE_GRAPHIC_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(GRAPHIC,child); + } + else if (XMLString.STYLE_PAGE_LAYOUT_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(PAGE,child); + } + else if (XMLString.STYLE_DRAWING_PAGE_PROPERTIES.equals(sName)) { + loadPropertiesFromDOM(PAGE,child); + } + } + child = child.getNextSibling(); + } + } + + private void loadPropertiesFromDOM(int nIndex,Node node) { + properties[nIndex].loadFromDOM(node); + // Several property sets may contain these complex properties, but only one per style: + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (XMLString.STYLE_BACKGROUND_IMAGE.equals(sName)) { + backgroundImageProperties.loadFromDOM(child); + } + else if (XMLString.STYLE_COLUMNS.equals(sName)) { + nColCount = Misc.getPosInteger(Misc.getAttribute(child, + XMLString.FO_COLUMN_COUNT),1); + // TODO: read individual columns + } + else if (XMLString.STYLE_FOOTNOTE_SEP.equals(sName)) { + bHasFootnoteSep = true; + footnoteSep.loadFromDOM(child); + } + } + child = child.getNextSibling(); + } + } + + protected String getProperty(int nIndex, String sName, boolean bInherit) { + int nRealIndex = bIsOldProps ? OLDPROPS : nIndex; + if (properties[nRealIndex].containsProperty(sName)) { + String sValue = properties[nRealIndex].getProperty(sName); + return Misc.truncateLength(sValue); + } + else if (bInherit && getParentName()!=null) { + StyleWithProperties parentStyle = (StyleWithProperties) family.getStyle(getParentName()); + if (parentStyle!=null) { + return parentStyle.getProperty(nIndex,sName,bInherit); + } + } + return null; // no value + } + + public String getTextProperty(String sName, boolean bInherit) { + return getProperty(TEXT,sName,bInherit); + } + + public String getParProperty(String sName, boolean bInherit) { + return getProperty(PAR,sName,bInherit); + } + + public String getSectionProperty(String sName, boolean bInherit) { + return getProperty(SECTION,sName,bInherit); + } + + public String getTableProperty(String sName, boolean bInherit) { + return getProperty(TABLE,sName,bInherit); + } + + public String getColumnProperty(String sName, boolean bInherit) { + return getProperty(COLUMN,sName,bInherit); + } + + public String getRowProperty(String sName, boolean bInherit) { + return getProperty(ROW,sName,bInherit); + } + + public String getCellProperty(String sName, boolean bInherit) { + return getProperty(CELL,sName,bInherit); + } + + public String getGraphicProperty(String sName, boolean bInherit) { + return getProperty(GRAPHIC,sName,bInherit); + } + + // TODO: Remove this method + public String getProperty(String sProperty, boolean bInherit){ + String sValue; + for (int i=0; i<COUNT; i++) { + sValue = getProperty(i,sProperty,bInherit); + if (sValue!=null) { return sValue; } + } + return null; // no value + } + + // TODO: Remove this method + public String getProperty(String sProperty){ + return getProperty(sProperty,true); + } + + protected String getAbsoluteProperty(int nIndex, String sProperty){ + int nRealIndex = bIsOldProps ? OLDPROPS : nIndex; + if (properties[nRealIndex].containsProperty(sProperty)){ + String sValue=(String) properties[nRealIndex].getProperty(sProperty); + if (sValue.endsWith("%")) { + StyleWithProperties parentStyle + = (StyleWithProperties) family.getStyle(getParentName()); + if (parentStyle!=null) { + String sParentValue = parentStyle.getAbsoluteProperty(nIndex,sProperty); + if (sParentValue!=null) { return Misc.multiply(sValue,sParentValue); } + } + else if (getFamily()!=null && getFamily().getDefaultStyle()!=null) { + StyleWithProperties style = (StyleWithProperties) getFamily().getDefaultStyle(); + String sDefaultValue=(String) style.getProperty(nIndex,sProperty,false); + if (sValue !=null) { return Misc.multiply(sValue,sDefaultValue); } + } + } + else { + return Misc.truncateLength(sValue); + } + } + else if (getParentName()!=null){ + StyleWithProperties parentStyle + = (StyleWithProperties) family.getStyle(getParentName()); + if (parentStyle!=null) { + return parentStyle.getAbsoluteProperty(nIndex,sProperty); + } + } + else if (getFamily()!=null && getFamily().getDefaultStyle()!=null) { + StyleWithProperties style = (StyleWithProperties) getFamily().getDefaultStyle(); + String sValue=(String) style.getProperty(nIndex,sProperty,false); + if (sValue !=null) { return sValue; } + } + // no value! + return null; + } + + public String getAbsoluteTextProperty(String sName) { + return getAbsoluteProperty(TEXT,sName); + } + + public String getAbsoluteParProperty(String sName) { + return getAbsoluteProperty(PAR,sName); + } + + public String getAbsoluteSectionProperty(String sName) { + return getAbsoluteProperty(SECTION,sName); + } + + public String getAbsoluteTableProperty(String sName) { + return getAbsoluteProperty(TABLE,sName); + } + + public String getAbsoluteColumnProperty(String sName) { + return getAbsoluteProperty(COLUMN,sName); + } + + public String getAbsoluteRowProperty(String sName) { + return getAbsoluteProperty(ROW,sName); + } + + public String getAbsoluteCellProperty(String sName) { + return getAbsoluteProperty(CELL,sName); + } + + public String getAbsoluteGraphicProperty(String sName) { + return getAbsoluteProperty(GRAPHIC,sName); + } + + // TODO: Remove this method + public String getAbsoluteProperty(String sProperty){ + String sValue; + for (int i=0; i<COUNT; i++) { + sValue = getAbsoluteProperty(i,sProperty); + if (sValue!=null) { return sValue; } + } + return null; // no value + } + + // Get a length property that defaults to 0cm + public String getAbsoluteLength(String sProperty) { + String s = getAbsoluteProperty(sProperty); + if (s==null) { return "0cm"; } + else { return s; } + } + + public String getBackgroundImageProperty(String sName) { + return backgroundImageProperties.getProperty(sName); + } + + public int getColCount() { return nColCount; } + + public boolean hasFootnoteSep() { return bHasFootnoteSep; } + + public String getFootnoteProperty(String sPropName) { + return footnoteSep.getProperty(sPropName); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/TableLine.java b/source/java/writer2latex/office/TableLine.java new file mode 100644 index 0000000..fe40f3a --- /dev/null +++ b/source/java/writer2latex/office/TableLine.java @@ -0,0 +1,66 @@ +/************************************************************************ + * + * TableLine.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-04) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Node; +import writer2latex.util.Misc; + +/** + * <p> This class represents the properties of a row or column in a table</p> + */ +public class TableLine { + private String sStyleName; + private String sVisibility; + private String sDefaultCellStyleName; + private boolean bDisplay; + private boolean bHeader; + + public TableLine(Node node, boolean bHeader, boolean bDisplay) { + // Node must be table:table-column or table:table-row + sStyleName = Misc.getAttribute(node,XMLString.TABLE_STYLE_NAME); + sVisibility = Misc.getAttribute(node,XMLString.TABLE_VISIBILITY); + if (sVisibility==null) { sVisibility = "visible"; } + sDefaultCellStyleName = Misc.getAttribute(node,XMLString.TABLE_DEFAULT_CELL_STYLE_NAME); + this.bDisplay = bDisplay; + this.bHeader = bHeader; + } + + public String getStyleName() { return sStyleName; } + + public String getVisibility() { return sVisibility; } + + public boolean isCollapse() { return "collapse".equals(sVisibility); } + + public boolean isFilter() { return "filter".equals(sVisibility); } + + public String getDefaultCellStyleName() { return sDefaultCellStyleName; } + + public boolean isDisplay() { return bDisplay; } + + public boolean isHeader() { return bHeader; } + +} diff --git a/source/java/writer2latex/office/TableRange.java b/source/java/writer2latex/office/TableRange.java new file mode 100644 index 0000000..94e233f --- /dev/null +++ b/source/java/writer2latex/office/TableRange.java @@ -0,0 +1,122 @@ +/************************************************************************ + * + * TableRange.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-07) + * + */ + +package writer2latex.office; + +/** + * This class represent a table range within a table. A table range is defined + * as a rectangular area (such as a print range), possibly excluding filtered + * and hidden rows. A <code>TableView</code> can be derived from a table range, + * providing read access to the range. + */ +public class TableRange { + private TableReader reader; + + private int nFirstRow; + private int nLastRow; + private int nFirstCol; + private int nLastCol; + private boolean bIncludeHidden; + private boolean bIncludeFiltered; + + public TableRange(TableReader reader) { + this.reader = reader; + + // The initial view is the complete table + nFirstRow = 0; + nLastRow = reader.getRowCount()-1; + nFirstCol = 0; + nLastCol = reader.getColCount()-1; + bIncludeHidden = true; + bIncludeFiltered = true; + } + + public void setFirstRow(int nRow) { + // Adjust to a valid value (in 0..nLastRow) + if (nRow<0) { nFirstRow = 0; } + else if (nRow>nLastRow) { nFirstRow = nLastRow; } + else { nFirstRow = nRow; } + } + + public int getFirstRow() { + return nFirstRow; + } + + public void setLastRow(int nRow) { + // Adjust to a valid value (in nFirstRow..(nRowCount-1)) + if (nRow<nFirstRow) { nLastRow = nFirstRow; } + else if (nRow>=reader.getRowCount()) { nLastRow = reader.getRowCount()-1; } + else { nLastRow = nRow; } + } + + public int getLastRow() { + return nLastRow; + } + + public void setFirstCol(int nCol) { + // Adjust to a valid value (in 0..nLastCol) + if (nCol<0) { nFirstCol = 0; } + else if (nCol>nLastCol) { nFirstCol = nLastCol; } + else { nFirstCol = nCol; } + } + + public int getFirstCol() { + return nFirstCol; + } + + public void setLastCol(int nCol) { + // Adjust to a valid value (in nFirstCol..(nColCount-1)) + if (nCol<nFirstCol) { nLastCol = nFirstCol; } + else if (nCol>=reader.getColCount()) { nLastCol = reader.getColCount()-1; } + else { nLastCol = nCol; } + } + + public int getLastCol() { + return nLastCol; + } + + public void setIncludeHidden(boolean b) { + bIncludeHidden = b; + } + + public boolean includeHidden() { + return bIncludeHidden; + } + + public void setIncludeFiltered(boolean b) { + bIncludeFiltered = b; + } + + public boolean includeFiltered() { + return bIncludeFiltered; + } + + public TableView createTableView() { + return new TableView(reader, this); + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/TableRangeParser.java b/source/java/writer2latex/office/TableRangeParser.java new file mode 100644 index 0000000..f3bfa06 --- /dev/null +++ b/source/java/writer2latex/office/TableRangeParser.java @@ -0,0 +1,131 @@ +/************************************************************************ + * + * TableRangeParser.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-16) + * + */ + +package writer2latex.office; + +import writer2latex.util.Misc; +import writer2latex.util.SimpleInputBuffer; + +/** + * This class parses a space separated list of table ranges. A table range is of the form + * <sheet name>.<column><row>:<sheet name>.<column><row> + * where the sheet name is quoted (single quotes) if it contains spaces + * the column is one or two uppercase letters (A-Z) + * the row is an integer. + * The sheet name is currently ignored, and so are any syntax errors + */ +public class TableRangeParser { + private SimpleInputBuffer inbuf; + + public TableRangeParser(String s) { + inbuf = new SimpleInputBuffer(s); + inbuf.skipSpaces(); + } + + public boolean hasMoreRanges() { + return !inbuf.atEnd(); + } + + // returns { first col, first row, last col, last row } + public int[] getRange() { + if (!inbuf.atEnd()) { + int[] nFirst = parseAddress(); + parseCharacter(':'); + int[] nLast = parseAddress(); + int[] nResult = { nFirst[0], nFirst[1], nLast[0], nLast[1] }; + return nResult; + } + else { + return null; + } + } + + private void parseCharacter(char c) { + // ignore the character if it's missing + if (inbuf.peekChar()==c) { inbuf.getChar(); } + inbuf.skipSpaces(); + } + + // returns { col, row } + private int[] parseAddress() { + parseSheetName(); + parseCharacter('.'); + int[] nResult = new int[2]; + nResult[0] = parseColumn(); + nResult[1] = parseRow(); + inbuf.skipSpaces(); + return nResult; + } + + private void parseSheetName() { + if (inbuf.peekChar()=='.') { + // The sheet name may be omitted + } + else if (inbuf.peekChar()=='\'') { + // The sheet name must be quoted if it contains space, dots or apostrophes + inbuf.getChar(); // skip leading ' + while (!inbuf.atEnd() && inbuf.peekChar()!='\'') { + inbuf.getChar(); + if (inbuf.peekChar()=='\'' && inbuf.peekFollowingChar()=='\'') { + // Escaped ' + inbuf.getChar(); inbuf.getChar(); + } + } + inbuf.getChar(); // skip trailing ' + } + else { + // Unquoted sheet name, ends with dot + while (!inbuf.atEnd() && inbuf.peekChar()!='.') { + inbuf.getChar(); + } + } + inbuf.skipSpaces(); + } + + // Return column number, starting with 0 + private int parseColumn() { + if (inbuf.peekChar()>='A' && inbuf.peekChar()<='Z') { + int nFirst = inbuf.getChar()-65; + if (inbuf.peekChar()>='A' && inbuf.peekChar()<='Z') { + int nSecond = inbuf.getChar()-65; + return 26*(nFirst+1)+nSecond; + } + else { + return nFirst; + } + } + else { + return 0; // syntax error + } + } + + // Return row number, starting with 0 + private int parseRow() { + return Misc.getPosInteger(inbuf.getInteger(),1)-1; + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/TableReader.java b/source/java/writer2latex/office/TableReader.java new file mode 100644 index 0000000..58153f1 --- /dev/null +++ b/source/java/writer2latex/office/TableReader.java @@ -0,0 +1,395 @@ +/************************************************************************ + * + * TableReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + */ + +// Version 1.0 (2008-11-22) + +package writer2latex.office; + +import java.util.LinkedList; +import java.util.Vector; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import writer2latex.util.Misc; + +/** + * <p> This class reads a table from a table:table or table:sub-table element + * and presents it as an n by m grid. In addition it gives access to the + * absolute and relative widths of tables, columns and cells.</p> + */ +public class TableReader { + //private OfficeReader ofr; + private Element tableNode; + private LinkedList cols = new LinkedList(); + private LinkedList rows = new LinkedList(); + private LinkedList cells = new LinkedList(); + private int nMaxCols = 1; // real number of columns (count to last non-empty) + private int nMaxRows = 1; // real number of rows (count to last non-empty) + private String[] sColWidth; + private String[] sRelColWidth; + private String sTableWidth; + private String sRelTableWidth; + private Vector printRanges; + + /** + * <p> The constructor reads a table from a table:table or table:sub-table + * node.</p> + * @param ofr the OfficeReader object to get style information from + * @param tableNode the table node + */ + public TableReader(OfficeReader ofr, Element tableNode) { + //this.ofr = ofr; + this.tableNode = tableNode; + if (!tableNode.hasChildNodes()) { return; } // empty table! + NodeList nl = tableNode.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (sName.equals(XMLString.TABLE_TABLE_COLUMN)) { + readTableColumn(child,false,false); + } + else if (sName.equals(XMLString.TABLE_TABLE_COLUMNS)) { + readTableColumns(child,false,false); + } + else if (sName.equals(XMLString.TABLE_TABLE_COLUMN_GROUP)) { + readTableColumnGroup(child,false,false); + } + else if (sName.equals(XMLString.TABLE_TABLE_HEADER_COLUMNS)) { + readTableHeaderColumns(child,false,false); + } + else if (sName.equals(XMLString.TABLE_TABLE_ROW)) { + readTableRow(child,false,false); + } + else if (sName.equals(XMLString.TABLE_TABLE_ROWS)) { + readTableRows(child,false,false); + } + else if (sName.equals(XMLString.TABLE_TABLE_ROW_GROUP)) { + readTableRowGroup(child,false,false); + } + else if (sName.equals(XMLString.TABLE_TABLE_HEADER_ROWS)) { + readTableHeaderRows(child,false,false); + } + } + } + + // Read table width from style + StyleWithProperties tableStyle = ofr.getTableStyle(getTableStyleName()); + if (tableStyle!=null) { + sTableWidth = tableStyle.getProperty(XMLString.STYLE_WIDTH); + sRelTableWidth = tableStyle.getProperty(XMLString.STYLE_REL_WIDTH); + } + + // Determine column widths + int nCols = cols.size(); + sColWidth = new String[nCols]; + sRelColWidth = new String[nCols]; + int[] nRelColWidth = new int[nCols]; + boolean bHasRelWidth=true; // set to false if some columns does not have a relative width set + int nColSum = 0; + for (int nCol=0; nCol<nCols; nCol++) { + StyleWithProperties style = ofr.getColumnStyle(((TableLine) cols.get(nCol)).getStyleName()); + if (style!=null) { + sColWidth[nCol] = style.getProperty(XMLString.STYLE_COLUMN_WIDTH); + String s = style.getProperty(XMLString.STYLE_REL_COLUMN_WIDTH); + if (s!=null && s.endsWith("*")) { + nRelColWidth[nCol] = Misc.getPosInteger(s.substring(0,s.length()-1),1); + } + } + if (sColWidth[nCol]==null) { sColWidth[nCol] = "2cm"; } // emergency, should not happen + if (nRelColWidth[nCol]==0) { bHasRelWidth = false; } + nColSum += nRelColWidth[nCol]; + } + for (int nCol=0; nCol<nCols; nCol++) { + if (bHasRelWidth) { + sRelColWidth[nCol] = (100.0F*nRelColWidth[nCol]/nColSum)+"%"; + } + else { + sRelColWidth[nCol] = (100.0F/nCols)+"%"; + } + } + + // Now determine the actual number of rows and columns + // (Calc exports a lot of empty rows at columns bottom/right) + int nRows = cells.size(); + for (int nRow=0; nRow<nRows; nRow++) { + LinkedList row = (LinkedList) cells.get(nRow); + nCols = row.size(); + int nMaxCol = 0; + int nMaxRow = 0; + for (int nCol=0; nCol<nCols; nCol++) { + Element cell = (Element) row.get(nCol); + if (cell.hasChildNodes()) { + nMaxRow = nRow + Misc.getPosInteger(cell.getAttribute( + XMLString.TABLE_NUMBER_ROWS_SPANNED),1); + if (nMaxRow>nMaxRows) { nMaxRows = nMaxRow; } + nMaxCol = nCol + Misc.getPosInteger(cell.getAttribute( + XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1); + if (nMaxCol>nMaxCols) { nMaxCols = nMaxCol; } + } + } + } + + // Finally get the print ranges, if any + printRanges = new Vector(); + if (!"false".equals(tableNode.getAttribute(XMLString.TABLE_PRINT))) { + TableRangeParser parser = new TableRangeParser(tableNode.getAttribute(XMLString.TABLE_PRINT_RANGES)); + while (parser.hasMoreRanges()) { + int[] nRange = parser.getRange(); + TableRange range = new TableRange(this); + range.setFirstCol(nRange[0]); + range.setFirstRow(nRange[1]); + range.setLastCol(nRange[2]); + range.setLastRow(nRange[3]); + printRanges.add(range); + } + } + } + + private void readTableColumn(Node node, boolean bHeader, boolean bDisplay) { + int nRepeat = Misc.getPosInteger(Misc.getAttribute(node, + XMLString.TABLE_NUMBER_COLUMNS_REPEATED),1); + while (nRepeat-->0) { + cols.add(new TableLine(node,bHeader,bDisplay)); + } + } + + private void readTableColumns(Node node, boolean bHeader, boolean bDisplay) { + if (!node.hasChildNodes()) { return; } // no columns here! + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (sName.equals(XMLString.TABLE_TABLE_COLUMN)) { + readTableColumn(child,bHeader,bDisplay); + } + else if (sName.equals(XMLString.TABLE_TABLE_COLUMN_GROUP)) { + readTableColumnGroup(child,bHeader,bDisplay); + } + } + } + } + + private void readTableColumnGroup(Node node, boolean bHeader, boolean bDisplay) { + bDisplay = !"false".equals(Misc.getAttribute(node,XMLString.TABLE_DISPLAY)); + if (!node.hasChildNodes()) { return; } // no columns here! + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (sName.equals(XMLString.TABLE_TABLE_HEADER_COLUMNS)) { + readTableHeaderColumns(child,bHeader,bDisplay); + } + else if (sName.equals(XMLString.TABLE_TABLE_COLUMN)) { + readTableColumn(child,bHeader,bDisplay); + } + else if (sName.equals(XMLString.TABLE_TABLE_COLUMN_GROUP)) { + readTableColumnGroup(child,bHeader,bDisplay); + } + } + } + } + + private void readTableHeaderColumns(Node node, boolean bHeader, boolean bDisplay) { + readTableColumns(node,true,bDisplay); + } + + private void readTableRow(Node node, boolean bHeader, boolean bDisplay) { + int nRepeat = Misc.getPosInteger(Misc.getAttribute(node, + XMLString.TABLE_NUMBER_ROWS_REPEATED),1); + while (nRepeat-->0) { + rows.add(new TableLine(node,bHeader,bDisplay)); + + // Read the cells in the row + LinkedList row = new LinkedList(); + if (node.hasChildNodes()) { + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + Element cell = (Element) child; + String sName = cell.getTagName(); + if (sName.equals(XMLString.TABLE_TABLE_CELL)) { + int nColRepeat = Misc.getPosInteger(cell.getAttribute( + XMLString.TABLE_NUMBER_COLUMNS_REPEATED),1); + while (nColRepeat-->0) { row.add(cell); } + } + else if (sName.equals(XMLString.TABLE_COVERED_TABLE_CELL)) { + int nColRepeat = Misc.getPosInteger(cell.getAttribute( + XMLString.TABLE_NUMBER_COLUMNS_REPEATED),1); + while (nColRepeat-->0) { row.add(cell); } + } + } + } + } + cells.add(row); + } + + } + + private void readTableRows(Node node, boolean bHeader, boolean bDisplay) { + if (!node.hasChildNodes()) { return; } // no rows here! + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (sName.equals(XMLString.TABLE_TABLE_ROW)) { + readTableRow(child,bHeader,bDisplay); + } + else if (sName.equals(XMLString.TABLE_TABLE_ROW_GROUP)) { + readTableRowGroup(child,bHeader,bDisplay); + } + } + } + } + + private void readTableRowGroup(Node node, boolean bHeader, boolean bDisplay) { + bDisplay = !"false".equals(Misc.getAttribute(node,XMLString.TABLE_DISPLAY)); + if (!node.hasChildNodes()) { return; } // no rows here! + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sName = child.getNodeName(); + if (sName.equals(XMLString.TABLE_TABLE_HEADER_ROWS)) { + readTableHeaderRows(child,bHeader,bDisplay); + } + else if (sName.equals(XMLString.TABLE_TABLE_ROW)) { + readTableRow(child,bHeader,bDisplay); + } + else if (sName.equals(XMLString.TABLE_TABLE_ROW_GROUP)) { + readTableRowGroup(child,bHeader,bDisplay); + } + } + } + } + + public boolean isSubTable() { + return XMLString.TABLE_SUB_TABLE.equals(tableNode.getTagName()) || // old + "true".equals(Misc.getAttribute(tableNode,XMLString.TABLE_IS_SUB_TABLE)); // oasis + } + + private void readTableHeaderRows(Node node, boolean bHeader, boolean bDisplay) { + readTableRows(node,true,bDisplay); + } + + public String getTableName() { + return tableNode.getAttribute(XMLString.TABLE_NAME); + } + + public String getTableStyleName() { + return tableNode.getAttribute(XMLString.TABLE_STYLE_NAME); + } + + public String getTableWidth() { return sTableWidth; } + + public String getRelTableWidth() { return sRelTableWidth; } + + public int getRowCount() { return rows.size(); } + + public int getMaxRowCount() { return nMaxRows; } + + public int getFirstBodyRow() { + int nRows = rows.size(); + for (int nRow=0; nRow<nRows; nRow++) { + if (!getRow(nRow).isHeader()) { + return nRow; + } + } + return nRows; // no body rows! + } + + public int getColCount() { return cols.size(); } + + public int getMaxColCount() { return nMaxCols; } + + public String getColumnWidth(int nCol) { + return 0<=nCol && nCol<=cols.size() ? sColWidth[nCol] : null; + } + + public String getRelColumnWidth(int nCol) { + return 0<=nCol && nCol<=cols.size() ? sRelColWidth[nCol] : null; + } + + public Element getCell(int nRow, int nCol) { + if (nRow<0 || nRow>=cells.size()) { return null; } + LinkedList row = (LinkedList) cells.get(nRow); + if (nCol<0 || nCol>=row.size()) { return null; } + return (Element) row.get(nCol); + } + + public String getCellStyleName(int nRow, int nCol) { + Element cell = (Element) getCell(nRow,nCol); + if (cell==null) { return null; } + String s = cell.getAttribute(XMLString.TABLE_STYLE_NAME); // try the cell + if (s!=null && s.length()>0) { return s; } + s = getRow(nRow).getDefaultCellStyleName(); // try row default + if (s!=null && s.length()>0) { return s; } + s = getCol(nCol).getDefaultCellStyleName(); // try column default + return s; + } + + public String getCellWidth(int nRow, int nCol) { + Element cell = (Element) getCell(nRow,nCol); + if (cell==null) { return null; } + int nCols = Misc.getPosInteger(cell.getAttribute(XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1); + String sWidth = sColWidth[nCol]; + for (int i=nCol+1; i<nCol+nCols; i++) { + sWidth = Misc.add(sWidth,sColWidth[i]); + } + return sWidth; + } + + public TableLine getRow(int nRow) { + if (nRow<0 || nRow>=rows.size()) { return null; } + return (TableLine) rows.get(nRow); + } + + public TableLine getCol(int nCol) { + if (nCol<0 || nCol>=cols.size()) { return null; } + return (TableLine) cols.get(nCol); + } + + public int getPrintRangeCount() { return printRanges.size(); } + + public TableRange getPrintRange(int nIndex) { + if (0<=nIndex && nIndex<printRanges.size()) { + return (TableRange) printRanges.get(nIndex); + } + else { + return null; + } + } + +} diff --git a/source/java/writer2latex/office/TableView.java b/source/java/writer2latex/office/TableView.java new file mode 100644 index 0000000..b20b98f --- /dev/null +++ b/source/java/writer2latex/office/TableView.java @@ -0,0 +1,196 @@ +/************************************************************************ + * + * TableView.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-07) + * + */ + +package writer2latex.office; + +import org.w3c.dom.Element; + +import writer2latex.util.Misc; + +/** + * This class represents a view of a <code>TableRange</code>. A view provides + * read access to the range using a simple grid model. + */ +public class TableView { + + private TableReader reader; + private TableRange range; + + // The size of the view (visible part of the range) + private int nRowCount; + private int nColCount; + + // Map view row/col index to original index + private int[] nRowMap; + private int[] nColMap; + + // The cells in the view + private CellView[][] cells; + + public TableView(TableReader reader, TableRange range) { + this.reader = reader; + this.range = range; + + // Count visible rows & cols in this range + nRowCount = 0; + for (int nRow=range.getFirstRow(); nRow<=range.getLastRow(); nRow++) { + if (isVisibleRow(nRow)) { nRowCount++; } + } + nColCount = 0; + for (int nCol=range.getFirstCol(); nCol<=range.getLastCol(); nCol++) { + if (isVisibleCol(nCol)) { nColCount++; } + } + + // Fill the row & col maps + nRowMap = new int[nRowCount]; + int nRealRow = range.getFirstRow(); + for (int nRow=0; nRow<nRowCount; nRow++) { + // Skip invisible rows + while (!isVisibleRow(nRealRow)) { nRealRow++; } + nRowMap[nRow] = nRealRow++; + } + nColMap = new int[nColCount]; + int nRealCol = range.getFirstCol(); + for (int nCol=0; nCol<nColCount; nCol++) { + // Skip invisible cols + while (!isVisibleCol(nRealCol)) { nRealCol++; } + nColMap[nCol] = nRealCol++; + } + + // Initialize the cell views + cells = new CellView[nRowCount][nColCount]; + for (int nRow=0; nRow<nRowCount; nRow++) { + for (int nCol=0; nCol<nColCount; nCol++) { + cells[nRow][nCol] = new CellView(); + } + } + + // Fill the cell views + // (must start in the upper left corner of the original table) + int nViewRow = 0; + for (int nRow=0; nRow<=range.getLastRow(); nRow++) { + if (nViewRow<nRowCount && nRowMap[nViewRow]<nRow) { nViewRow++; } + int nViewCol = 0; + for (int nCol=0; nCol<=range.getLastCol(); nCol++) { + if (nViewCol<nColCount && nColMap[nViewCol]<nCol) { nViewCol++; } + Element cell = reader.getCell(nRow,nCol); + if (Misc.isElement(cell,XMLString.TABLE_TABLE_CELL)) { + int nRowSpan = Misc.getPosInteger(cell.getAttribute(XMLString.TABLE_NUMBER_ROWS_SPANNED),1); + int nColSpan = Misc.getPosInteger(cell.getAttribute(XMLString.TABLE_NUMBER_COLUMNS_SPANNED),1); + // Test if (parts of) the cell belongs the view + if (nViewRow<nRowCount && nRowMap[nViewRow]<nRow+nRowSpan && + nViewCol<nColCount && nColMap[nViewCol]<nCol+nColSpan) { + cells[nViewRow][nViewCol].cell=cell; + cells[nViewRow][nViewCol].nOriginalRow=nRow; + cells[nViewRow][nViewCol].nOriginalCol=nCol; + // Calculate rowspan in view + int i=nViewRow+1; + while (i<nRowCount && nRowMap[i]<nRow+nRowSpan) { i++; } + cells[nViewRow][nViewCol].nRowSpan = i-nViewRow; + // Calculate colspan in view + int j=nViewCol+1; + while (j<nColCount && nColMap[j]<nCol+nColSpan) { j++; } + cells[nViewRow][nViewCol].nColSpan = j-nViewCol; + } + } + if (Misc.isElement(cell,XMLString.TABLE_COVERED_TABLE_CELL)) { + // Don't overwrite, the position may be occupied with a relocated cell + if (cells[nViewRow][nViewCol].cell==null) { + cells[nViewRow][nViewCol].cell=cell; + cells[nViewRow][nViewCol].nOriginalRow=nRow; + cells[nViewRow][nViewCol].nOriginalCol=nCol; + } + } + } + } + } + + public String getRelTableWidth() { return reader.getRelTableWidth(); } + + public int getRowCount() { return nRowCount; } + + public int getColCount() { return nColCount; } + + public String getColumnWidth(int nCol) { + return 0<=nCol && nCol<=nColCount ? reader.getColumnWidth(nColMap[nCol]) : null; + } + + // TODO: Recalculate - the sum should be 100% even in a view!! + public String getRelColumnWidth(int nCol) { + return 0<=nCol && nCol<=nColCount ? reader.getRelColumnWidth(nColMap[nCol]) : null; + } + + public TableLine getRow(int nRow) { + return 0<=nRow && nRow<nRowCount ? reader.getRow(nRowMap[nRow]) : null; + } + + public TableLine getCol(int nCol) { + return 0<=nCol && nCol<nColCount ? reader.getCol(nColMap[nCol]) : null; + } + + public Element getCell(int nRow, int nCol) { + return 0<=nRow && nRow<nRowCount && 0<=nCol && nCol<nColCount ? + cells[nRow][nCol].cell : null; + } + + public int getRowSpan(int nRow, int nCol) { + return 0<=nRow && nRow<nRowCount && 0<=nCol && nCol<nColCount ? + cells[nRow][nCol].nRowSpan : 1; + } + + public int getColSpan(int nRow, int nCol) { + return 0<=nRow && nRow<nRowCount && 0<=nCol && nCol<nColCount ? + cells[nRow][nCol].nColSpan : 1; + } + + public String getCellStyleName(int nRow, int nCol) { + return 0<=nRow && nRow<nRowCount && 0<=nCol && nCol<nColCount ? + reader.getCellStyleName(cells[nRow][nCol].nOriginalRow, cells[nRow][nCol].nOriginalCol) : null; + } + + // TODO: Not correct, see TableReader + public String getCellWidth(int nRow, int nCol) { + return 0<=nRow && nRow<nRowCount && 0<=nCol && nCol<nColCount ? + reader.getCellWidth(cells[nRow][nCol].nOriginalRow, cells[nRow][nCol].nOriginalCol) : null; + } + + // Helper method: Is this row visible in this view? + private boolean isVisibleRow(int nRow) { + return nRow>=range.getFirstRow() && nRow<=range.getLastRow() && + (range.includeHidden() || !reader.getRow(nRow).isCollapse()) && + (range.includeFiltered() || !reader.getRow(nRow).isFilter()); + } + + // Helper method: Is this column visible in this view? + private boolean isVisibleCol(int nCol) { + return nCol>=range.getFirstCol() && nCol<=range.getLastCol() && + (range.includeHidden() || !reader.getCol(nCol).isCollapse()) && + (range.includeFiltered() || !reader.getCol(nCol).isFilter()); + } + + + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/TocReader.java b/source/java/writer2latex/office/TocReader.java new file mode 100644 index 0000000..f949995 --- /dev/null +++ b/source/java/writer2latex/office/TocReader.java @@ -0,0 +1,187 @@ +/************************************************************************ + * + * TocReader.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.office; + +import java.util.Hashtable; +import java.util.Set; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.util.Misc; + +/** + * <p>The class reads a <code>text:table-of-content</code> element.</p> + */ +public class TocReader { + + Element tocSource = null; + Element indexBody = null; + + String sName=null; // (section) name for this toc + String sStyleName=null; // section style name + + int nOutlineLevel = 10; // max level to include + boolean bUseOutlineLevel = true; // use headings + boolean bUseIndexSourceStyles = false; // use additional styles + boolean bUseIndexMarks = true; // use toc marks + boolean bIsByChapter = false; // default is document + + Element indexTitleTemplate = null; + Element[] tocEntryTemplate = new Element[11]; + Hashtable indexSourceStyles = new Hashtable(); + + + + /** <p>Initialize the TocReader with a table of content node. + * @param onode a <code>text:table-of-content</code> + */ + public TocReader(Element onode) { + sName = Misc.getAttribute(onode,XMLString.TEXT_NAME); + sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + + Element tocSource = Misc.getChildByTagName(onode,XMLString.TEXT_TABLE_OF_CONTENT_SOURCE); + //Element indexBody = Misc.getChildByTagName(onode,XMLString.TEXT_INDEX_BODY); + + if (tocSource!=null) { + nOutlineLevel = Misc.getPosInteger(tocSource.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1); + bUseOutlineLevel = !"false".equals(tocSource.getAttribute(XMLString.TEXT_USE_OUTLINE_LEVEL)); + bUseIndexSourceStyles = "true".equals(tocSource.getAttribute(XMLString.TEXT_USE_INDEX_SOURCE_STYLES)); + bUseIndexMarks = !"false".equals(tocSource.getAttribute(XMLString.TEXT_USE_INDEX_MARKS)); + bIsByChapter = "chapter".equals(tocSource.getAttribute(XMLString.TEXT_INDEX_SCOPE)); + + // traverse the source to collect templates + Node child = tocSource.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + Element elm = (Element) child; + if (XMLString.TEXT_INDEX_TITLE_TEMPLATE.equals(elm.getTagName())) { + indexTitleTemplate = elm; + } + if (XMLString.TEXT_TABLE_OF_CONTENT_ENTRY_TEMPLATE.equals(elm.getTagName())) { + int nLevel = Misc.getPosInteger(elm.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1); + if (1<=nLevel && nLevel<=10) { tocEntryTemplate[nLevel] = elm; } + } + if (XMLString.TEXT_INDEX_SOURCE_STYLES.equals(elm.getTagName())) { + int nLevel = Misc.getPosInteger(elm.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1); + if (1<=nLevel && nLevel<=10) { + // traverse to collect index source styles for this level + Node child1 = elm.getFirstChild(); + while (child1!=null) { + if (child1.getNodeType()==Node.ELEMENT_NODE) { + Element elm1 = (Element) child1; + if (XMLString.TEXT_INDEX_SOURCE_STYLE.equals(elm1.getTagName())) { + String sIndexSourceStyle = Misc.getAttribute(elm1,XMLString.TEXT_STYLE_NAME); + if (sIndexSourceStyle!=null) { + indexSourceStyles.put(sIndexSourceStyle,new Integer(nLevel)); + } + } + } + child1 = child1.getNextSibling(); + } + } + } + } + child = child.getNextSibling(); + } + } + } + + /** <p>Get the (section) name for this toc </p> + * @return the name of the toc + */ + public String getName() { return sName; } + + /** <p>Get the (section) style name for this toc </p> + * @return name of the section style to use for this toc + */ + public String getStyleName() { return sStyleName; } + + /** <p>Get max outline level for this toc </p> + * @return max outline level + */ + public int getOutlineLevel() { return nOutlineLevel; } + + /** <p>Do we use outline (headings) in this toc? </p> + * @return true if headings should be used + */ + public boolean useOutlineLevel() { return bUseOutlineLevel; } + + /** <p>Do we use additional styles in this toc? </p> + * @return true if additional styles should be used + */ + public boolean useIndexSourceStyles() { return bUseIndexSourceStyles; } + + /** <p>Do we use toc marks in this toc? </p> + * @return true if toc marks should be used + */ + public boolean useIndexMarks() { return bUseIndexMarks; } + + /** <p>Is this toc by chapter? </p> + * @return true if the scope is a chapter only + */ + public boolean isByChapter() { return bIsByChapter; } + + /** <p>Get the index title template for this toc</p> + * @return the <code>text:index-title-template</code> element, or null + */ + public Element getIndexTitleTemplate() { return indexTitleTemplate; } + + /** <p>Get the entry template for this toc at a specific level</p> + * @param nLevel the outline level + * @return the <code>text:table-of-content-entry-template</code> element, or null + */ + public Element getTocEntryTemplate(int nLevel) { + if (1<=nLevel && nLevel<=10) { return tocEntryTemplate[nLevel]; } + else { return null; } + } + + /** <p>Get a set view of all index source styles</p> + * @return a set of all index source style names + */ + public Set getIndexSourceStyles() { return indexSourceStyles.keySet(); } + + /** <p>Get the level associated with a specific index source style</p> + * @param sStyleName the style name of the index source style + * @return the level or -1 if the style is not used in this toc + */ + public int getIndexSourceStyleLevel(String sStyleName) { + if (indexSourceStyles.containsKey(sStyleName)) { + return ((Integer) indexSourceStyles.get(sStyleName)).intValue(); + } + else { + return -1; + } + } + + /** <p>Return the generated content of this toc, if available</p> + * @return the <code>text:index-body</code> element + */ + public Element getIndexBody() { return indexBody; } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/office/XMLString.java b/source/java/writer2latex/office/XMLString.java new file mode 100644 index 0000000..6ab3167 --- /dev/null +++ b/source/java/writer2latex/office/XMLString.java @@ -0,0 +1,447 @@ +/************************************************************************ + * + * XMLString.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-10) + * + */ + +package writer2latex.office; + +/* XML strings (tags and attributes) in the OOo XML namespaces + * typosafe but not typesafe :-) + */ + +public class XMLString { + // draw namespace - elements + public static final String DRAW_="draw:"; + public static final String DRAW_PAGE="draw:page"; + public static final String DRAW_A="draw:a"; + public static final String DRAW_FRAME="draw:frame"; // oasis + public static final String DRAW_IMAGE="draw:image"; + public static final String DRAW_OBJECT="draw:object"; + public static final String DRAW_OBJECT_OLE="draw:object-ole"; + public static final String DRAW_TEXT_BOX="draw:text-box"; + public static final String DRAW_G="draw:g"; + public static final String DRAW_CONTROL="draw:control"; + // draw namespace - attributes + public static final String DRAW_NAME="draw:name"; + public static final String DRAW_STYLE_NAME="draw:style-name"; + public static final String DRAW_TEXT_STYLE_NAME="draw:text-style-name"; + public static final String DRAW_MASTER_PAGE_NAME="draw:master-page-name"; + public static final String DRAW_FILL_COLOR="draw:fill-color"; + + // dc namespace - elements + public static final String DC_CREATOR="dc:creator"; + public static final String DC_DATE="dc:date"; + public static final String DC_DESCRIPTION="dc:description"; + public static final String DC_LANGUAGE="dc:language"; + public static final String DC_SUBJECT="dc:subject"; + public static final String DC_TITLE="dc:title"; + + // meta namespace - elements + public static final String META_INITIAL_CREATOR="meta:initial-creator"; + public static final String META_KEYWORDS="meta:keywords"; + public static final String META_KEYWORD="meta:keyword"; + + // manifest namespace + public static final String MANIFEST_FILE_ENTRY="manifest:file-entry"; + public static final String MANIFEST_MEDIA_TYPE="manifest:media-type"; + public static final String MANIFEST_FULL_PATH="manifest:full-path"; + + // office namespace - elements + public static final String OFFICE_DOCUMENT_CONTENT="office:document-content"; + public static final String OFFICE_MASTER_STYLES="office:master-styles"; + public static final String OFFICE_STYLES="office:styles"; + public static final String OFFICE_AUTOMATIC_STYLES="office:automatic-styles"; + public static final String OFFICE_FONT_DECLS="office:font-decls"; + public static final String OFFICE_FONT_FACE_DECLS="office:font-face-decls"; // oasis + public static final String OFFICE_BODY="office:body"; + public static final String OFFICE_TEXT="office:text"; // oasis + public static final String OFFICE_SPREADSHEET="office:spreadsheet"; // oasis + public static final String OFFICE_PRESENTATION="office:presentation"; // oasis + public static final String OFFICE_FORMS="office:forms"; + public static final String OFFICE_ANNOTATION="office:annotation"; + public static final String OFFICE_BINARY_DATA="office:binary-data"; + public static final String OFFICE_META="office:meta"; + // office namespace - attributes + public static final String OFFICE_TARGET_FRAME_NAME="office:target-frame-name"; + public static final String OFFICE_NAME="office:name"; + public static final String OFFICE_VALUE_TYPE="office:value-type"; // oasis + + // form namespace - elements + public static final String FORM_FORM="form:form"; + public static final String FORM_CONTROL="form:control"; + public static final String FORM_TEXT="form:text"; + public static final String FORM_PASSWORD="form:password"; + public static final String FORM_FILE="form:file"; + public static final String FORM_IMAGE="form:image"; + public static final String FORM_HIDDEN="form:hidden"; + public static final String FORM_CHECKBOX="form:checkbox"; + public static final String FORM_RADIO="form:radio"; + public static final String FORM_BUTTON="form:button"; + public static final String FORM_FIXED_TEXT="form:fixed-text"; + public static final String FORM_TEXTAREA="form:textarea"; + public static final String FORM_LISTBOX="form:listbox"; + public static final String FORM_COMBOBOX="form:combobox"; + public static final String FORM_ITEM="form:item"; + public static final String FORM_OPTION="form:option"; + // form namespace - attributes + public static final String FORM_NAME="form:name"; + public static final String FORM_ID="form:id"; + public static final String FORM_METHOD="form:method"; + public static final String FORM_TYPE="form:type"; + public static final String FORM_BUTTON_TYPE="form:button-type"; + public static final String FORM_DISABLED="form:disabled"; + public static final String FORM_READONLY="form:readonly"; + public static final String FORM_DEFAULT_VALUE="form:default-value"; + public static final String FORM_VALUE="form:value"; + public static final String FORM_TAB_INDEX="form:tab-index"; + public static final String FORM_TITLE="form:title"; + public static final String FORM_MAX_LENGTH="form:max-length"; + public static final String FORM_MULTIPLE="form:multiple"; + public static final String FORM_SELECTED="form:selected"; + public static final String FORM_LABEL="form:label"; + public static final String FORM_SIZE="form:size"; + + // presentation namespace - attributes + public static final String PRESENTATION_CLASS="presentation:class"; + public static final String PRESENTATION_STYLE_NAME="presentation:style-name"; + + // style namespace - elements + public static final String STYLE_PAGE_MASTER="style:page-master"; + public static final String STYLE_PAGE_LAYOUT="style:page-layout"; // oasis + public static final String STYLE_MASTER_PAGE="style:master-page"; + public static final String STYLE_FONT_DECL="style:font-decl"; + public static final String STYLE_FONT_FACE="style:font-face"; // oasis + public static final String STYLE_STYLE="style:style"; + public static final String STYLE_DEFAULT_STYLE="style:default-style"; + public static final String STYLE_PROPERTIES="style:properties"; + public static final String STYLE_TEXT_PROPERTIES="style:text-properties"; // oasis + public static final String STYLE_PARAGRAPH_PROPERTIES="style:paragraph-properties"; // oasis + public static final String STYLE_SECTION_PROPERTIES="style:section-properties"; // oasis + public static final String STYLE_TABLE_PROPERTIES="style:table-properties"; // oasis + public static final String STYLE_TABLE_ROW_PROPERTIES="style:table-row-properties"; // oasis + public static final String STYLE_TABLE_COLUMN_PROPERTIES="style:table-column-properties"; // oasis + public static final String STYLE_TABLE_CELL_PROPERTIES="style:table-cell-properties"; // oasis + public static final String STYLE_GRAPHIC_PROPERTIES="style:graphic-properties"; // oasis + public static final String STYLE_PAGE_LAYOUT_PROPERTIES="style:page-layout-properties"; // oasis + public static final String STYLE_DRAWING_PAGE_PROPERTIES="style:drawing-page-properties"; // oasis + public static final String STYLE_HEADER_FOOTER_PROPERTIES="style:header-footer-properties"; // oasis + public static final String STYLE_BACKGROUND_IMAGE="style:background-image"; + public static final String STYLE_COLUMNS="style:columns"; + public static final String STYLE_HEADER="style:header"; + public static final String STYLE_HEADER_LEFT="style:header-left"; + public static final String STYLE_FOOTER="style:footer"; + public static final String STYLE_FOOTER_LEFT="style:footer-left"; + public static final String STYLE_FOOTNOTE_SEP="style:footnote-sep"; + public static final String STYLE_HEADER_STYLE="style:header-style"; + public static final String STYLE_FOOTER_STYLE="style:footer-style"; + // style namespace - attributes + public static final String STYLE_NEXT_STYLE_NAME="style:next-style-name"; + public static final String STYLE_DISPLAY_NAME="style:display-name"; + public static final String STYLE_PAGE_MASTER_NAME="style:page-master-name"; + public static final String STYLE_PAGE_LAYOUT_NAME="style:page-layout-name"; // oasis + public static final String STYLE_MASTER_PAGE_NAME="style:master-page-name"; + public static final String STYLE_PAGE_USAGE="style:page-usage"; + public static final String STYLE_PAGE_NUMBER="style:page-number"; + public static final String STYLE_FONT_FAMILY_COMPLEX="style:font-family-complex"; + public static final String STYLE_FONT_NAME="style:font-name"; + public static final String STYLE_FONT_NAME_COMPLEX="style:font-name-complex"; + public static final String STYLE_FONT_PITCH="style:font-pitch"; + public static final String STYLE_FONT_FAMILY_GENERIC="style:font-family-generic"; + public static final String STYLE_TEXT_BACKGROUND_COLOR="style:text-background-color"; + public static final String STYLE_USE_WINDOW_FONT_COLOR="style:use-window-font-color"; + public static final String STYLE_TEXT_CROSSING_OUT="style:text-crossing-out"; + public static final String STYLE_TEXT_UNDERLINE="style:text-underline"; + public static final String STYLE_TEXT_BLINKING="style:text-blinking"; + public static final String STYLE_TEXT_LINE_THROUGH_STYLE="style:text-line-through-style"; // oasis + public static final String STYLE_TEXT_UNDERLINE_STYLE="style:text-underline-style"; // oasis + public static final String STYLE_AUTO_TEXT_INDENT="style:auto-text-indent"; + public static final String STYLE_TEXT_ALIGN_SOURCE="style:text-align-source"; + public static final String STYLE_NAME="style:name"; + public static final String STYLE_PARENT_STYLE_NAME="style:parent-style-name"; + public static final String STYLE_FAMILY="style:family"; + public static final String STYLE_TEXT_POSITION="style:text-position"; + public static final String STYLE_LIST_STYLE_NAME="style:list-style-name"; + public static final String STYLE_LIST_LEVEL_PROPERTIES="style:list-level-properties"; + public static final String STYLE_NUM_PREFIX="style:num-prefix"; + public static final String STYLE_NUM_SUFFIX="style:num-suffix"; + public static final String STYLE_NUM_FORMAT="style:num-format"; + public static final String STYLE_VERTICAL_ALIGN="style:vertical-align"; + public static final String STYLE_MAY_BREAK_BETWEEN_ROWS="style:may-break-between-rows"; + public static final String STYLE_HORIZONTAL_POS="style:horizontal-pos"; + public static final String STYLE_WRAP="style:wrap"; + public static final String STYLE_COLUMN_WIDTH="style:column-width"; + public static final String STYLE_REL_COLUMN_WIDTH="style:rel-column-width"; + public static final String STYLE_ROW_HEIGHT="style:row-height"; + public static final String STYLE_MIN_ROW_HEIGHT="style:min-row-height"; + public static final String STYLE_FIRST_PAGE_NUMBER="style:first-page-number"; + public static final String STYLE_DISTANCE_BEFORE_SEP="style:distance-before-sep"; + public static final String STYLE_DISTANCE_AFTER_SEP="style:distance-after-sep"; + public static final String STYLE_WIDTH="style:width"; + public static final String STYLE_REL_WIDTH="style:rel-width"; + public static final String STYLE_COLOR="style:color"; + public static final String STYLE_WRITING_MODE="style:writing-mode"; + public static final String STYLE_REPEAT="style:repeat"; + public static final String STYLE_POSITION="style:position"; + public static final String STYLE_ADJUSTMENT="style:adjustment"; + + // table namespace - elements + public static final String TABLE_="table:"; + public static final String TABLE_TABLE="table:table"; + public static final String TABLE_SUB_TABLE="table:sub-table"; + public static final String TABLE_SHAPES="table:shapes"; + public static final String TABLE_TABLE_COLUMN="table:table-column"; + public static final String TABLE_TABLE_COLUMNS="table:table-columns"; + public static final String TABLE_TABLE_COLUMN_GROUP="table:table-column-group"; + public static final String TABLE_TABLE_HEADER_COLUMNS="table:table-header-columns"; + public static final String TABLE_TABLE_ROW="table:table-row"; + public static final String TABLE_TABLE_ROWS="table:table-rows"; + public static final String TABLE_TABLE_ROW_GROUP="table:table-row-group"; + public static final String TABLE_TABLE_HEADER_ROWS="table:table-header-rows"; + public static final String TABLE_TABLE_CELL="table:table-cell"; + public static final String TABLE_COVERED_TABLE_CELL="table:covered-table-cell"; + // table namespace - attributes + public static final String TABLE_NAME="table:name"; + public static final String TABLE_IS_SUB_TABLE="table:is-sub-table"; // oasis + public static final String TABLE_STYLE_NAME="table:style-name"; + public static final String TABLE_VISIBILITY="table:visibility"; + public static final String TABLE_DISPLAY="table:display"; + public static final String TABLE_DEFAULT_CELL_STYLE_NAME="table:default-cell-style-name"; + public static final String TABLE_VALUE_TYPE="table:value-type"; + public static final String TABLE_NUMBER_COLUMNS_REPEATED="table:number-columns-repeated"; + public static final String TABLE_NUMBER_ROWS_REPEATED="table:number-rows-repeated"; + public static final String TABLE_NUMBER_ROWS_SPANNED="table:number-rows-spanned"; + public static final String TABLE_NUMBER_COLUMNS_SPANNED="table:number-columns-spanned"; + public static final String TABLE_ALIGN="table:align"; + public static final String TABLE_PRINT="table:print"; + public static final String TABLE_PRINT_RANGES="table:print-ranges"; + + // text namespace - elements (declarations) + public static final String TEXT_="text:"; + public static final String TEXT_FOOTNOTES_CONFIGURATION="text:footnotes-configuration"; + public static final String TEXT_ENDNOTES_CONFIGURATION="text:endnotes-configuration"; + public static final String TEXT_NOTES_CONFIGURATION="text:notes-configuration"; // oasis + public static final String TEXT_SECTION_SOURCE="text:section-source"; + public static final String TEXT_SEQUENCE_DECLS="text:sequence-decls"; + public static final String TEXT_SEQUENCE_DECL="text:sequence-decl"; + public static final String TEXT_OUTLINE_STYLE="text:outline-style"; + public static final String TEXT_LIST_STYLE="text:list-style"; + public static final String TEXT_LIST_LEVEL_STYLE_NUMBER="text:list-level-style-number"; + public static final String TEXT_LIST_LEVEL_STYLE_BULLET="text:list-level-style-bullet"; + public static final String TEXT_LIST_LEVEL_STYLE_IMAGE="text:list-level-style-image"; + // text namespace - elements (block text) + public static final String TEXT_SECTION="text:section"; + public static final String TEXT_P="text:p"; + public static final String TEXT_H="text:h"; + public static final String TEXT_LIST="text:list"; // oasis + public static final String TEXT_ORDERED_LIST="text:ordered-list"; + public static final String TEXT_UNORDERED_LIST="text:unordered-list"; + public static final String TEXT_LIST_ITEM="text:list-item"; + public static final String TEXT_LIST_HEADER="text:list-header"; + public static final String TEXT_ALPHABETICAL_INDEX="text:alphabetical-index"; + public static final String TEXT_ALPHABETICAL_INDEX_SOURCE="text:alphabetical-index-source"; + public static final String TEXT_ALPHABETICAL_INDEX_ENTRY_TEMPLATE="text:alphabetical-index-entry-template"; + public static final String TEXT_TABLE_OF_CONTENT="text:table-of-content"; + public static final String TEXT_TABLE_OF_CONTENT_SOURCE="text:table-of-content-source"; + public static final String TEXT_TABLE_OF_CONTENT_ENTRY_TEMPLATE="text:table-of-content-entry-template"; + public static final String TEXT_INDEX_SOURCE_STYLES="text:index-source-styles"; + public static final String TEXT_INDEX_SOURCE_STYLE="text:index-source-style"; + public static final String TEXT_ILLUSTRATION_INDEX="text:illustration-index"; + public static final String TEXT_ILLUSTRATION_INDEX_SOURCE="text:illustration-index-source"; + public static final String TEXT_ILLUSTRATION_INDEX_ENTRY_TEMPLATE="text:illustration-index-entry-template"; + public static final String TEXT_TABLE_INDEX="text:table-index"; + public static final String TEXT_TABLE_INDEX_SOURCE="text:table-index-source"; + public static final String TEXT_TABLE_INDEX_ENTRY_TEMPLATE="text:table-index-entry-template"; + public static final String TEXT_OBJECT_INDEX="text:object-index"; + public static final String TEXT_USER_INDEX="text:user-index"; + public static final String TEXT_BIBLIOGRAPHY="text:bibliography"; + public static final String TEXT_INDEX_TITLE_TEMPLATE="text:index-title-template"; + public static final String TEXT_INDEX_BODY="text:index-body"; + public static final String TEXT_INDEX_TITLE="text:index-title"; + public static final String TEXT_INDEX_SOURCE="text:index-source"; + // text namespace - elements (inline text) + public static final String TEXT_SPAN="text:span"; + public static final String TEXT_FOOTNOTE="text:footnote"; + public static final String TEXT_ENDNOTE="text:endnote"; + public static final String TEXT_NOTE="text:note"; // oasis + public static final String TEXT_FOOTNOTE_CITATION="text:footnote-citation"; + public static final String TEXT_FOOTNOTE_BODY="text:footnote-body"; + public static final String TEXT_ENDNOTE_CITATION="text:endnote-citation"; + public static final String TEXT_ENDNOTE_BODY="text:endnote-body"; + public static final String TEXT_NOTE_CITATION="text:note-citation"; // oasis + public static final String TEXT_NOTE_BODY="text:note-body"; // oasis + public static final String TEXT_S="text:s"; + public static final String TEXT_TAB_STOP="text:tab-stop"; + public static final String TEXT_TAB="text:tab"; // oasis + public static final String TEXT_A="text:a"; + public static final String TEXT_LINE_BREAK="text:line-break"; + public static final String TEXT_PAGE_NUMBER="text:page-number"; + public static final String TEXT_PAGE_COUNT="text:page-count"; + public static final String TEXT_CHAPTER="text:chapter"; + public static final String TEXT_SEQUENCE="text:sequence"; + public static final String TEXT_SEQUENCE_REF="text:sequence-ref"; + public static final String TEXT_BIBLIOGRAPHY_MARK="text:bibliography-mark"; + public static final String TEXT_ALPHABETICAL_INDEX_MARK="text:alphabetical-index-mark"; + public static final String TEXT_ALPHABETICAL_INDEX_MARK_START="text:alphabetical-index-mark-start"; + public static final String TEXT_ALPHABETICAL_INDEX_MARK_END="text:alphabetical-index-mark-end"; + public static final String TEXT_TOC_MARK="text:toc-mark"; + public static final String TEXT_TOC_MARK_START="text:toc-mark-start"; + public static final String TEXT_TOC_MARK_END="text:toc-mark-end"; + public static final String TEXT_REFERENCE_MARK="text:reference-mark"; + public static final String TEXT_REFERENCE_MARK_START="text:reference-mark-start"; + public static final String TEXT_REFERENCE_REF="text:reference-ref"; + public static final String TEXT_BOOKMARK="text:bookmark"; + public static final String TEXT_BOOKMARK_START="text:bookmark-start"; + public static final String TEXT_BOOKMARK_REF="text:bookmark-ref"; + public static final String TEXT_FOOTNOTE_REF="text:footnote-ref"; + public static final String TEXT_ENDNOTE_REF="text:endnote-ref"; + public static final String TEXT_NOTE_REF="text:note-ref"; // oasis + // text namespace - attributes + public static final String TEXT_USE_OUTLINE_LEVEL="text:use-outline-level"; + public static final String TEXT_USE_INDEX_SOURCE_STYLES="text:use-index-source-styles"; + public static final String TEXT_USE_INDEX_MARKS="text:use-index-marks"; + public static final String TEXT_INDEX_SCOPE="text:index-scope"; + public static final String TEXT_OUTLINE_LEVEL="text:outline-level"; + public static final String TEXT_IS_LIST_HEADER="text:is-list-header"; + public static final String TEXT_USE_CAPTION="text:use-caption"; + public static final String TEXT_CAPTION_SEQUENCE_NAME="text:caption-sequence-name"; + public static final String TEXT_STRING_VALUE="text:string-value"; + public static final String TEXT_KEY1="text:key1"; + public static final String TEXT_KEY2="text:key2"; + public static final String TEXT_LEVEL="text:level"; + public static final String TEXT_SPACE_BEFORE="text:space-before"; + public static final String TEXT_MIN_LABEL_WIDTH="text:min-label-width"; + public static final String TEXT_MIN_LABEL_DISTANCE="text:min-label-distance"; + public static final String TEXT_STYLE_NAME="text:style-name"; + public static final String TEXT_VISITED_STYLE_NAME="text:visited-style-name"; + public static final String TEXT_DISPLAY_LEVELS="text:display-levels"; + public static final String TEXT_CONTINUE_NUMBERING="text:continue-numbering"; + public static final String TEXT_C="text:c"; + public static final String TEXT_ID="text:id"; + public static final String TEXT_LABEL="text:label"; + public static final String TEXT_NAME="text:name"; + public static final String TEXT_REFERENCE_FORMAT="text:reference-format"; + public static final String TEXT_REF_NAME="text:ref-name"; + public static final String TEXT_FORMULA="text:formula"; + public static final String TEXT_NOTE_CLASS="text:note-class"; + public static final String TEXT_CITATION_BODY_STYLE_NAME="text:citation-body-style-name"; + public static final String TEXT_CITATION_STYLE_NAME="text:citation-style-name"; + public static final String TEXT_DEFAULT_STYLE_NAME="text:default-style-name"; + public static final String TEXT_START_VALUE="text:start-value"; + public static final String TEXT_START_NUMBERING_AT="text:start-numbering-at"; + public static final String TEXT_RESTART_NUMBERING="text:restart-numbering"; + public static final String TEXT_ANCHOR_TYPE="text:anchor-type"; + public static final String TEXT_BULLET_CHAR="text:bullet-char"; + public static final String TEXT_DISPLAY="text:display"; + public static final String TEXT_DISPLAY_OUTLINE_LEVEL="text:display-outline-level"; + public static final String TEXT_SEPARATION_CHARACTER="text:separation-character"; + + public static final String TEXT_IDENTIFIER="text:identifier"; + public static final String TEXT_BIBLIOGRAPHY_TYPE="text:bibliography-type"; + public static final String TEXT_BIBILIOGRAPHIC_TYPE="text:bibiliographic-type"; // bug in OOo 1.0 + public static final String TEXT_ADDRESS="text:address"; + public static final String TEXT_ANNOTE="text:annote"; + public static final String TEXT_AUTHOR="text:author"; + public static final String TEXT_BOOKTITLE="text:booktitle"; + //public static final String TEXT_CHAPTER="text:chapter"; + public static final String TEXT_EDITION="text:edition"; + public static final String TEXT_EDITOR="text:editor"; + public static final String TEXT_HOWPUBLISHED="text:howpublished"; + public static final String TEXT_INSTITUTION="text:institution"; + public static final String TEXT_JOURNAL="text:journal"; + public static final String TEXT_MONTH="text:month"; + // public static final String TEXT_NOTE="text:note"; defined above as an element name + public static final String TEXT_NUMBER="text:number"; + public static final String TEXT_ORGANIZATIONS="text:organizations"; + public static final String TEXT_PAGES="text:pages"; + public static final String TEXT_PUBLISHER="text:publisher"; + public static final String TEXT_SCHOOL="text:school"; + public static final String TEXT_SERIES="text:series"; + public static final String TEXT_TITLE="text:title"; + public static final String TEXT_REPORT_TYPE="text:report-type"; + public static final String TEXT_VOLUME="text:volume"; + public static final String TEXT_YEAR="text:year"; + public static final String TEXT_URL="text:url"; + public static final String TEXT_CUSTOM1="text:custom1"; + public static final String TEXT_CUSTOM2="text:custom2"; + public static final String TEXT_CUSTOM3="text:custom3"; + public static final String TEXT_CUSTOM4="text:custom4"; + public static final String TEXT_CUSTOM5="text:custom5"; + public static final String TEXT_ISBN="text:isbn"; + + // fo namespace + public static final String FO_LANGUAGE="fo:language"; + public static final String FO_COUNTRY="fo:country"; + public static final String FO_TEXT_SHADOW="fo:text-shadow"; + public static final String FO_COLOR="fo:color"; + public static final String FO_BACKGROUND_COLOR="fo:background-color"; + public static final String FO_TEXT_TRANSFORM="fo:text-transform"; + public static final String FO_FONT_FAMILY="fo:font-family"; + public static final String FO_FONT_SIZE="fo:font-size"; + public static final String FO_FONT_WEIGHT="fo:font-weight"; + public static final String FO_FONT_VARIANT="fo:font-variant"; + public static final String FO_FONT_STYLE="fo:font-style"; + public static final String FO_LETTER_SPACING="fo:letter-spacing"; + public static final String FO_VERTICAL_ALIGN="fo:vertical-align"; + public static final String FO_TEXT_ALIGN="fo:text-align"; + public static final String FO_TEXT_ALIGN_LAST="fo:text-align-last"; + public static final String FO_BREAK_BEFORE="fo:break-before"; + public static final String FO_BREAK_AFTER="fo:break-after"; + public static final String FO_MARGIN_LEFT="fo:margin-left"; + public static final String FO_MARGIN_RIGHT="fo:margin-right"; + public static final String FO_MARGIN_TOP="fo:margin-top"; + public static final String FO_MARGIN_BOTTOM="fo:margin-bottom"; + public static final String FO_PAGE_WIDTH="fo:page-width"; + public static final String FO_PAGE_HEIGHT="fo:page-height"; + public static final String FO_MIN_HEIGHT="fo:min-height"; + public static final String FO_BORDER="fo:border"; + public static final String FO_BORDER_LEFT="fo:border-left"; + public static final String FO_BORDER_RIGHT="fo:border-right"; + public static final String FO_BORDER_TOP="fo:border-top"; + public static final String FO_BORDER_BOTTOM="fo:border-bottom"; + public static final String FO_PADDING="fo:padding"; + public static final String FO_PADDING_LEFT="fo:padding-left"; + public static final String FO_PADDING_RIGHT="fo:padding-right"; + public static final String FO_PADDING_TOP="fo:padding-top"; + public static final String FO_PADDING_BOTTOM="fo:padding-bottom"; + public static final String FO_LINE_HEIGHT="fo:line-height"; + public static final String FO_TEXT_INDENT="fo:text-indent"; + public static final String FO_WRAP_OPTION="fo:wrap-option"; + public static final String FO_COLUMN_COUNT="fo:column-count"; + + // svg namespace + public static final String SVG_DESC="svg:desc"; + + public static final String SVG_FONT_FAMILY="svg:font-family"; // oasis (font declarations only) + public static final String SVG_X="svg:x"; + public static final String SVG_Y="svg:y"; + public static final String SVG_HEIGHT="svg:height"; + public static final String SVG_WIDTH="svg:width"; + // xlink namespace + public static final String XLINK_HREF="xlink:href"; + // math namespace + public static final String MATH_MATH="math:math"; + public static final String MATH_SEMANTICS="math:semantics"; + public static final String MATH_ANNOTATION="math:annotation"; + +} diff --git a/source/java/writer2latex/util/Base64.java b/source/java/writer2latex/util/Base64.java new file mode 100644 index 0000000..39d77a0 --- /dev/null +++ b/source/java/writer2latex/util/Base64.java @@ -0,0 +1,1811 @@ +/** + * This is Robert Harders public domain Base64 class. It is unmodified, except for the package name. + * + * <p>Encodes and decodes to and from Base64 notation.</p> + * <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p> + * + * <p>The <tt>options</tt> parameter, which appears in a few places, is used to pass + * several pieces of information to the encoder. In the "higher level" methods such as + * encodeBytes( bytes, options ) the options parameter can be used to indicate such + * things as first gzipping the bytes before encoding them, not inserting linefeeds + * (though that breaks strict Base64 compatibility), and encoding using the URL-safe + * and Ordered dialects.</p> + * + * <p>The constants defined in Base64 can be OR-ed together to combine options, so you + * might make a call like this:</p> + * + * <code>String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES );</code> + * + * <p>to compress the data before encoding it and then making the output have no newline characters.</p> + * + * + * <p> + * Change Log: + * </p> + * <ul> + * <li>v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the + * Base64.InputStream class to encode and decode on the fly which uses + * less memory than encoding/decoding an entire file into memory before writing.</li> + * <li>v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug + * when using very small files (~< 40 bytes).</li> + * <li>v2.2 - Added some helper methods for encoding/decoding directly from + * one file to the next. Also added a main() method to support command line + * encoding/decoding from one file to the next. Also added these Base64 dialects: + * <ol> + * <li>The default is RFC3548 format.</li> + * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates + * URL and file name friendly format as described in Section 4 of RFC3548. + * http://www.faqs.org/rfcs/rfc3548.html</li> + * <li>Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates + * URL and file name friendly format that preserves lexical ordering as described + * in http://www.faqs.org/qa/rfcc-1940.html</li> + * </ol> + * Special thanks to Jim Kellerman at <a href="http://www.powerset.com/">http://www.powerset.com/</a> + * for contributing the new Base64 dialects. + * </li> + * + * <li>v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.</li> + * <li>v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).</li> + * <li>v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.</li> + * <li>v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (<tt>int</tt>s that you "OR" together).</li> + * <li>v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using <tt>decode( String s, boolean gzipCompressed )</tt>. + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).</li> + * <li>v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.</li> + * <li>v1.4 - Added helper methods to read/write files.</li> + * <li>v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.</li> + * <li>v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.</li> + * <li>v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.</li> + * <li>v1.3.3 - Fixed I/O streams which were totally messed up.</li> + * </ul> + * + * <p> + * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a> + * periodically to check for updates or to contribute improvements. + * </p> + * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.2.2 + */ +package writer2latex.util; + +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding. */ + public final static int ENCODE = 1; + + + /** Specify decoding. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed. */ + public final static int GZIP = 2; + + + /** Don't break lines when encoding (violates strict Base64 specification) */ + public final static int DONT_BREAK_LINES = 8; + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. + * It is important to note that data encoded this way is <em>not</em> officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public final static int URL_SAFE = 16; + + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>. + */ + public final static int ORDERED = 32; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "UTF-8"; + + + // I think I end up not using the BAD_ENCODING indicator. + //private final static byte BAD_ENCODING = -9; // Indicates error in encoding + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + + /** The 64 valid Base64 values. */ + //private final static byte[] ALPHABET; + /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ + private final static byte[] _STANDARD_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] _STANDARD_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * <a href="http://www.faqs.org/rfcs/rfc3548.html">http://www.faqs.org/rfcs/rfc3548.html</a>. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ + private final static byte[] _URL_SAFE_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' + }; + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ + private final static byte[] _URL_SAFE_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + + +/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ + + /** + * I don't get the point of this technique, but it is described here: + * <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>. + */ + private final static byte[] _ORDERED_ALPHABET = + { + (byte)'-', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', + (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'_', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' + }; + + /** + * Used in decoding the "ordered" dialect of Base64. + */ + private final static byte[] _ORDERED_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' + 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' + 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ + + + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getAlphabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; + else return _STANDARD_ALPHABET; + + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getDecodabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; + else return _STANDARD_DECODABET; + + } // end getAlphabet + + + + /** Defeats instantiation. */ + private Base64(){} + + + /** + * Encodes or decodes two files from the command line; + * <strong>feel free to delete this method (in fact you probably should) + * if you're embedding this code into a larger program.</strong> + */ + public final static void main( String[] args ) + { + if( args.length < 3 ){ + usage("Not enough arguments."); + } // end if: args.length < 3 + else { + String flag = args[0]; + String infile = args[1]; + String outfile = args[2]; + if( flag.equals( "-e" ) ){ + Base64.encodeFileToFile( infile, outfile ); + } // end if: encode + else if( flag.equals( "-d" ) ) { + Base64.decodeFileToFile( infile, outfile ); + } // end else if: decode + else { + usage( "Unknown flag: " + flag ); + } // end else + } // end else + } // end main + + /** + * Prints command line usage. + * + * @param msg A message to include with usage info. + */ + private final static void usage( String msg ) + { + System.err.println( msg ); + System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" ); + } // end usage + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array <var>threeBytes</var> + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by <var>numSigBytes</var>. + * The array <var>threeBytes</var> needs only be as big as + * <var>numSigBytes</var>. + * Code can reuse a byte array by passing a four-byte array as <var>b4</var>. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) + { + encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); + return b4; + } // end encode3to4 + + + /** + * <p>Encodes up to three bytes of the array <var>source</var> + * and writes the resulting four Base64 bytes to <var>destination</var>. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * <var>srcOffset</var> and <var>destOffset</var>. + * This method does not check to make sure your arrays + * are large enough to accomodate <var>srcOffset</var> + 3 for + * the <var>source</var> array or <var>destOffset</var> + 4 for + * the <var>destination</var> array. + * The actual number of significant bytes in your array is + * given by <var>numSigBytes</var>.</p> + * <p>This is the lowest level of the encoding methods with + * all possible parameters.</p> + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the <var>destination</var> array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options ) + { + byte[] ALPHABET = getAlphabet( options ); + + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return <tt>null</tt>. + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return <tt>null</tt>. + * <p> + * Valid options:<pre> + * GZIP: gzip-compresses object before encoding it. + * DONT_BREAK_LINES: don't break lines at 76 characters + * <i>Note: Technically, this makes your encoding non-compliant.</i> + * </pre> + * <p> + * Example: <code>encodeObject( myObj, Base64.GZIP )</code> or + * <p> + * Example: <code>encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES )</code> + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + { + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + java.util.zip.GZIPOutputStream gzos = null; + + // Isolate options + int gzip = (options & GZIP); + int dontBreakLines = (options & DONT_BREAK_LINES); + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + + // GZip? + if( gzip == GZIP ) + { + gzos = new java.util.zip.GZIPOutputStream( b64os ); + oos = new java.io.ObjectOutputStream( gzos ); + } // end if: gzip + else + oos = new java.io.ObjectOutputStream( b64os ); + + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) + { + return encodeBytes( source, 0, source.length, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + * <p> + * Valid options:<pre> + * GZIP: gzip-compresses object before encoding it. + * DONT_BREAK_LINES: don't break lines at 76 characters + * <i>Note: Technically, this makes your encoding non-compliant.</i> + * </pre> + * <p> + * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or + * <p> + * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code> + * + * + * @param source The data to convert + * @param options Specified options + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) + { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) + { + return encodeBytes( source, off, len, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + * <p> + * Valid options:<pre> + * GZIP: gzip-compresses object before encoding it. + * DONT_BREAK_LINES: don't break lines at 76 characters + * <i>Note: Technically, this makes your encoding non-compliant.</i> + * </pre> + * <p> + * Example: <code>encodeBytes( myData, Base64.GZIP )</code> or + * <p> + * Example: <code>encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES )</code> + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options Specified options + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) + { + // Isolate options + int dontBreakLines = ( options & DONT_BREAK_LINES ); + int gzip = ( options & GZIP ); + + // Compress? + if( gzip == GZIP ) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + return null; + } // end catch + finally + { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + // Convert option to boolean in way that code likes it. + boolean breakLines = dontBreakLines == 0; + + int len43 = len * 4 / 3; + byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) + { + encode3to4( source, d+off, 3, outBuff, e, options ); + + lineLength += 4; + if( breakLines && lineLength == MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) + { + encode3to4( source, d+off, len - d, outBuff, e, options ); + e += 4; + } // end if: some padding needed + + + // Return value according to relevant encoding. + try + { + return new String( outBuff, 0, e, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( outBuff, 0, e ); + } // end catch + + } // end else: don't compress + + } // end encodeBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array <var>source</var> + * and writes the resulting bytes (up to three of them) + * to <var>destination</var>. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * <var>srcOffset</var> and <var>destOffset</var>. + * This method does not check to make sure your arrays + * are large enough to accomodate <var>srcOffset</var> + 4 for + * the <var>source</var> array or <var>destOffset</var> + 3 for + * the <var>destination</var> array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + * <p>This is the lowest level of the decoding methods with + * all possible parameters.</p> + * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return the number of decoded bytes converted + * @since 1.3 + */ + private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else + { + try{ + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + }catch( Exception e){ + System.out.println(""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + System.out.println(""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + System.out.println(""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + System.out.println(""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); + return -1; + } // end catch + } + } // end decodeToBytes + + + + + /** + * Very low-level access to decoding ASCII characters in + * the form of a byte array. Does not support automatically + * gunzipping or any other "fancy" features. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @return decoded data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + int len34 = len * 3 / 4; + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; + + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + for( i = off; i < off+len; i++ ) + { + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; + + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better + { + if( sbiDecode >= EQUALS_SIGN_ENC ) + { + b4[ b4Posn++ ] = sbiCrop; + if( b4Posn > 3 ) + { + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) + break; + } // end if: quartet built + + } // end if: equals sign or better + + } // end if: white space, equals sign or better + else + { + System.err.println( "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + return null; + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s ) + { + return decode( s, NO_OPTIONS ); + } + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @param options encode options such as URL_SAFE + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s, int options ) + { + byte[] bytes; + try + { + bytes = s.getBytes( PREFERRED_ENCODING ); + } // end try + catch( java.io.UnsupportedEncodingException uee ) + { + bytes = s.getBytes(); + } // end catch + //</change> + + // Decode + bytes = decode( bytes, 0, bytes.length, options ); + + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + if( bytes != null && bytes.length >= 4 ) + { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) + { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) + { + // Just return originally-decoded bytes + } // end catch + finally + { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns <tt>null</tt> if there was an error. + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @since 1.5 + */ + public static Object decodeToObject( String encodedObject ) + { + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream( objBytes ); + ois = new java.io.ObjectInputStream( bais ); + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + catch( java.lang.ClassNotFoundException e ) + { + e.printStackTrace(); + obj = null; + } // end catch + finally + { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @return <tt>true</tt> if successful, <tt>false</tt> otherwise + * + * @since 2.1 + */ + public static boolean encodeToFile( byte[] dataToEncode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + success = true; + } // end try + catch( java.io.IOException e ) + { + + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @return <tt>true</tt> if successful, <tt>false</tt> otherwise + * + * @since 2.1 + */ + public static boolean decodeToFile( String dataToDecode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + success = true; + } // end try + catch( java.io.IOException e ) + { + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + * @param filename Filename for reading encoded data + * @return decoded byte array or null if unsuccessful + * + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + { + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + System.err.println( "File is too big for this convenience method (" + file.length() + " bytes)." ); + return null; + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + catch( java.io.IOException e ) + { + System.err.println( "Error decoding from file " + filename ); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + * @param filename Filename for reading binary data + * @return base64-encoded string or null if unsuccessful + * + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + { + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + catch( java.io.IOException e ) + { + System.err.println( "Error encoding from file " + filename ); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + + + + /** + * Reads <tt>infile</tt> and encodes it to <tt>outfile</tt>. + * + * @param infile Input file + * @param outfile Output file + * @return true if the operation is successful + * @since 2.2 + */ + public static boolean encodeFileToFile( String infile, String outfile ) + { + boolean success = false; + java.io.InputStream in = null; + java.io.OutputStream out = null; + try{ + in = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( infile ) ), + Base64.ENCODE ); + out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); + byte[] buffer = new byte[65536]; // 64K + int read = -1; + while( ( read = in.read(buffer) ) >= 0 ){ + out.write( buffer,0,read ); + } // end while: through file + success = true; + } catch( java.io.IOException exc ){ + exc.printStackTrace(); + } finally{ + try{ in.close(); } catch( Exception exc ){} + try{ out.close(); } catch( Exception exc ){} + } // end finally + + return success; + } // end encodeFileToFile + + + + /** + * Reads <tt>infile</tt> and decodes it to <tt>outfile</tt>. + * + * @param infile Input file + * @param outfile Output file + * @return true if the operation is successful + * @since 2.2 + */ + public static boolean decodeFileToFile( String infile, String outfile ) + { + boolean success = false; + java.io.InputStream in = null; + java.io.OutputStream out = null; + try{ + in = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( infile ) ), + Base64.DECODE ); + out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); + byte[] buffer = new byte[65536]; // 64K + int read = -1; + while( ( read = in.read(buffer) ) >= 0 ){ + out.write( buffer,0,read ); + } // end while: through file + success = true; + } catch( java.io.IOException exc ){ + exc.printStackTrace(); + } finally{ + try{ in.close(); } catch( Exception exc ){} + try{ out.close(); } catch( Exception exc ){} + } // end finally + + return success; + } // end decodeFileToFile + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.InputStream} will read data from another + * <tt>java.io.InputStream</tt>, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + private int options; // Record options used to create the stream. + private byte[] alphabet; // Local copies to avoid extra method calls + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the <tt>java.io.InputStream</tt> from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) + { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + * <p> + * Valid options:<pre> + * ENCODE or DECODE: Encode or Decode as data is read. + * DONT_BREAK_LINES: don't break lines at 76 characters + * (only meaningful when encoding) + * <i>Note: Technically, this makes your encoding non-compliant.</i> + * </pre> + * <p> + * Example: <code>new Base64.InputStream( in, Base64.DECODE )</code> + * + * + * @param in the <tt>java.io.InputStream</tt> from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) + { + super( in ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + this.options = options; // Record for later, mostly to determine which alphabet to use + this.alphabet = getAlphabet(options); + this.decodabet = getDecodabet(options); + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @since 1.3 + */ + public int read() throws java.io.IOException + { + // Do we need to get data? + if( position < 0 ) + { + if( encode ) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) + { + try + { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) + { + b3[i] = (byte)b; + numBinaryBytes++; + } // end if: not end of stream + + } // end try: read + catch( java.io.IOException e ) + { + // Only a problem if we got no data at all. + if( i == 0 ) + throw e; + + } // end catch + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) + { + encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) + { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) + break; // Reads a -1 if end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) + { + numSigBytes = decode4to3( b4, 0, buffer, 0, options ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) + { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ) + return -1; + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) + position = -1; + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + // When JDK1.4 is more accepted, use an assertion here. + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or <var>len</var> bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @since 1.3 + */ + public int read( byte[] dest, int off, int len ) throws java.io.IOException + { + int i; + int b; + for( i = 0; i < len; i++ ) + { + b = read(); + + //if( b < 0 && i == 0 ) + // return -1; + + if( b >= 0 ) + dest[off + i] = (byte)b; + else if( i == 0 ) + return -1; + else + break; // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.OutputStream} will write data to another + * <tt>java.io.OutputStream</tt>, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + private int options; // Record for later + private byte[] alphabet; // Local copies to avoid extra method calls + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the <tt>java.io.OutputStream</tt> to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) + { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + * <p> + * Valid options:<pre> + * ENCODE or DECODE: Encode or Decode as data is read. + * DONT_BREAK_LINES: don't break lines at 76 characters + * (only meaningful when encoding) + * <i>Note: Technically, this makes your encoding non-compliant.</i> + * </pre> + * <p> + * Example: <code>new Base64.OutputStream( out, Base64.ENCODE )</code> + * + * @param out the <tt>java.io.OutputStream</tt> to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) + { + super( out ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + this.options = options; + this.alphabet = getAlphabet(options); + this.decodabet = getDecodabet(options); + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @since 1.3 + */ + public void write(int theByte) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to encode. + { + out.write( encode3to4( b4, buffer, bufferLength, options ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to output. + { + int len = Base64.decode4to3( buffer, 0, b4, 0, options ); + out.write( b4, 0, len ); + //out.write( Base64.decode4to3( buffer ) ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) + { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until <var>len</var> + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @since 1.3 + */ + public void write( byte[] theBytes, int off, int len ) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) + { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + */ + public void flushBase64() throws java.io.IOException + { + if( position > 0 ) + { + if( encode ) + { + out.write( encode3to4( b4, buffer, position, options ) ); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @since 1.3 + */ + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 diff --git a/source/java/writer2latex/util/CSVList.java b/source/java/writer2latex/util/CSVList.java new file mode 100644 index 0000000..858286d --- /dev/null +++ b/source/java/writer2latex/util/CSVList.java @@ -0,0 +1,69 @@ +/************************************************************************ + * + * CSVList.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.4 (2004-08-10) + * + */ + +package writer2latex.util; + +// Create a list of values separated by commas or another seperation character +public class CSVList{ + private String sSep; + private String sNameValueSep; + private boolean bEmpty = true; + private StringBuffer buf = new StringBuffer(); + + public CSVList(String sSep, String sNameValueSep) { + this.sSep=sSep; + this.sNameValueSep=sNameValueSep; + } + + public CSVList(String sSep) { + this(sSep,":"); + } + + public CSVList(char cSep) { + this(Character.toString(cSep),":"); + } + + public void addValue(String sVal){ + if (sVal==null) { return; } + if (bEmpty) { bEmpty=false; } else { buf.append(sSep); } + buf.append(sVal); + } + + public void addValue(String sName, String sVal) { + if (sName==null) { return; } + if (bEmpty) { bEmpty=false; } else { buf.append(sSep); } + buf.append(sName).append(sNameValueSep).append(sVal); + } + + public String toString() { + return buf.toString(); + } + + public boolean isEmpty() { + return bEmpty; + } + +} diff --git a/source/java/writer2latex/util/ExportNameCollection.java b/source/java/writer2latex/util/ExportNameCollection.java new file mode 100644 index 0000000..50110d1 --- /dev/null +++ b/source/java/writer2latex/util/ExportNameCollection.java @@ -0,0 +1,102 @@ +/************************************************************************ + * + * ExportNameCollection.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-02-25) + * + */ + +package writer2latex.util; + +import java.util.Enumeration; +import java.util.Hashtable; + +// Collection of export names +// Used for mapping named collections to simpler names (only A-Z, a-z and 0-9) +public class ExportNameCollection{ + private Hashtable exportNames = new Hashtable(); + private String sPrefix; + private boolean bAcceptNumbers; + + public ExportNameCollection(String sPrefix, boolean b) { + this.sPrefix=sPrefix; + bAcceptNumbers = b; + } + + public ExportNameCollection(boolean b) { + this("",b); + } + + public Enumeration keys() { + return exportNames.keys(); + } + + public void addName(String sName){ + if (containsName(sName)) { return; } + StringBuffer outbuf=new StringBuffer(); + SimpleInputBuffer inbuf=new SimpleInputBuffer(sName); + + // Don't start with a digit + if (bAcceptNumbers && inbuf.peekChar()>='0' && inbuf.peekChar()<='9') { + outbuf.append('a'); + } + + char c; + // convert numbers to roman numbers and discard unwanted characters + while ((c=inbuf.peekChar())!='\0'){ + if ((c>='a' && c<='z') || (c>='A' && c<='Z')) { + outbuf.append(inbuf.getChar()); + } + else if (c>='0' && c<='9'){ + if (bAcceptNumbers) { + outbuf.append(inbuf.getInteger()); + } + else { + outbuf.append(Misc.int2roman( + Integer.parseInt(inbuf.getInteger()))); + } + } + else { + inbuf.getChar(); // ignore this character + } + } + String sExportName=outbuf.toString(); + // the result may exist in the collecion; add a's at the end + while (exportNames.containsValue(sExportName)){ + sExportName+="a"; + } + exportNames.put(sName,sExportName); + } + + public String getExportName(String sName) { + // add the name, if it does not exist + if (!containsName(sName)) { addName(sName); } + return sPrefix + (String) exportNames.get(sName); + } + + public boolean containsName(String sName) { + return exportNames.containsKey(sName); + } + + public boolean isEmpty() { + return exportNames.size()==0; + } +} diff --git a/source/java/writer2latex/util/Misc.java b/source/java/writer2latex/util/Misc.java new file mode 100644 index 0000000..d368585 --- /dev/null +++ b/source/java/writer2latex/util/Misc.java @@ -0,0 +1,356 @@ +/************************************************************************ + * + * Misc.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.util; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.lang.Math; +import java.net.URLEncoder; +import java.net.URLDecoder; +//import java.util.Hashtable; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.NamedNodeMap; + +// This class contains some usefull, but unrelated static methods +public class Misc{ + + private final static int BUFFERSIZE = 1024; + + public static final int[] doubleIntArray(int[] array) { + int n = array.length; + int[] newArray = new int[2*n]; + for (int i=0; i<n; i++) { newArray[i] = array[i]; } + return newArray; + } + + public static final String int2roman(int number) { + StringBuffer roman=new StringBuffer(); + while (number>=1000) { roman.append('m'); number-=1000; } + if (number>=900) { roman.append("cm"); number-=900; } + if (number>=500) { roman.append('d'); number-=500; } + if (number>=400) { roman.append("cd"); number-=400; } + while (number>=100) { roman.append('c'); number-=100; } + if (number>=90) { roman.append("xc"); number-=90; } + if (number>=50) { roman.append('l'); number-=50; } + if (number>=40) { roman.append("xl"); number-=40; } + while (number>=10) { roman.append('x'); number-=10; } + if (number>=9) { roman.append("ix"); number-=9; } + if (number>=5) { roman.append('v'); number-=5; } + if (number>=4) { roman.append("iv"); number-=4; } + while (number>=1) { roman.append('i'); number-=1; } + return roman.toString(); + } + + public static final String int2Roman(int number) { + return int2roman(number).toUpperCase(); + } + + public static final String int2arabic(int number) { + return new Integer(number).toString(); + } + + public static final String int2alph(int number, boolean bLetterSync) { + // TODO: Handle overflow/lettersync + return new Character((char) (number+96)).toString(); + } + + public static final String int2Alph(int number, boolean bLetterSync) { + return int2alph(number,bLetterSync).toUpperCase(); + } + + public static final int getPosInteger(String sInteger, int nDefault){ + int n; + try { + n=Integer.parseInt(sInteger); + } + catch (NumberFormatException e) { + return nDefault; + } + return n>0 ? n : nDefault; + } + + public static final float getFloat(String sFloat, float fDefault){ + float f; + try { + f=Float.parseFloat(sFloat); + } + catch (NumberFormatException e) { + return fDefault; + } + return f; + } + + public static final int getIntegerFromHex(String sHex, int nDefault){ + int n; + try { + n=Integer.parseInt(sHex,16); + } + catch (NumberFormatException e) { + return nDefault; + } + return n; + } + + public static int min(int n, int m) { return n<m ? n : m; } + + public static String truncateLength(String sValue) { + if (sValue.endsWith("inch")) { + // Cut of inch to in + return sValue.substring(0,sValue.length()-2); + } + else { + return sValue; + } + } + + // Return units per inch for some unit + private static final float getUpi(String sUnit) { + if ("in".equals(sUnit)) { return 1.0F; } + else if ("mm".equals(sUnit)) { return 25.4F; } + else if ("cm".equals(sUnit)) { return 2.54F; } + else if ("pc".equals(sUnit)) { return 6F; } + else { return 72; } // pt or unknown + } + + // Convert a length to px assuming 96ppi (cf. css spec) + // Exception: Never return less than 1px + public static final String length2px(String sLength) { + if (sLength.equals("0")) { return "0"; } + float fLength=getFloat(sLength.substring(0,sLength.length()-2),1); + String sUnit=sLength.substring(sLength.length()-2); + float fPixels = 96.0F/getUpi(sUnit)*fLength; + if (Math.abs(fPixels)<0.01) { + // Very small, treat as zero + return "0"; + } + else if (fPixels>0) { + // Never return less that 1px + return Float.toString(fPixels<1 ? 1 : fPixels)+"px"; + } + else { + // Or above -1px + return Float.toString(fPixels>-1 ? -1 : fPixels)+"px"; + } + } + + public static final String multiply(String sPercent, String sLength){ + if (sLength.equals("0")) { return "0"; } + float fPercent=getFloat(sPercent.substring(0,sPercent.length()-1),1); + float fLength=getFloat(sLength.substring(0,sLength.length()-2),1); + String sUnit=sLength.substring(sLength.length()-2); + return Float.toString(fPercent*fLength/100)+sUnit; + } + + public static final String add(String sLength1, String sLength2){ + if (sLength1.equals("0")) { return sLength2; } + if (sLength2.equals("0")) { return sLength1; } + float fLength1=getFloat(sLength1.substring(0,sLength1.length()-2),1); + String sUnit1=sLength1.substring(sLength1.length()-2); + float fLength2=getFloat(sLength2.substring(0,sLength2.length()-2),1); + String sUnit2=sLength2.substring(sLength2.length()-2); + // Use unit from sLength1: + return Float.toString(fLength1+getUpi(sUnit1)/getUpi(sUnit2)*fLength2)+sUnit1; + } + + public static final String sub(String sLength1, String sLength2){ + return add(sLength1,multiply("-100%",sLength2)); + } + + public static boolean isLessThan(String sThis, String sThat) { + return sub(sThis,sThat).startsWith("-"); + } + + public static String abs(String sLength) { + return sLength.startsWith("-") ? sLength.substring(1) : sLength; + } + + /* + * Utility method to make sure the document name is stripped of any file + * extensions before use. + * (this is copied verbatim from PocketWord.java in xmerge) + */ + public static final String trimDocumentName(String name,String extension) { + String temp = name.toLowerCase(); + + if (temp.endsWith(extension)) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - extension.length(); + name = name.substring(0,endIndex); + } + + return name; + } + + public static final String removeExtension(String sName) { + int n = sName.lastIndexOf("."); + if (n<0) { return sName; } + return sName.substring(0,n); + } + + /* + * Utility method to retrieve a Node attribute or null + */ + public static final String getAttribute (Node node, String attribute) { + NamedNodeMap attrNodes = node.getAttributes(); + + if (attrNodes != null) { + Node attr = attrNodes.getNamedItem(attribute); + if (attr != null) { + return attr.getNodeValue(); + } + } + + return null; + } + + public static final boolean isElement(Node node) { + return node.getNodeType()==Node.ELEMENT_NODE; + } + + /* Utility method to determine if a Node is a specific Element + */ + public static final boolean isElement(Node node, String sTagName) { + return node.getNodeType()==Node.ELEMENT_NODE + && node.getNodeName().equals(sTagName); + } + + public static final boolean isText(Node node) { + return node.getNodeType()==Node.TEXT_NODE; + } + + /* + * Utility method to retrieve an element attribute or null + */ + public static final String getAttribute (Element node, String attribute) { + if (node.hasAttribute(attribute)) { return node.getAttribute(attribute); } + else { return null; } + } + + /* utility method to get the first child with a given tagname */ + public static final Element getChildByTagName(Node node, String sTagName){ + if (node.hasChildNodes()){ + NodeList nl=node.getChildNodes(); + int nLen=nl.getLength(); + for (int i=0; i<nLen; i++){ + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE && + child.getNodeName().equals(sTagName)){ + return (Element) child; + } + } + } + return null; + } + + /* utility method to get the first <em>element</em> child of a node*/ + public static final Element getFirstChildElement(Node node) { + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + return (Element) child; + } + child = child.getNextSibling(); + } + return null; + } + + /* utility method that collects PCDATA content of an element */ + public static String getPCDATA(Node node) { + StringBuffer buf = new StringBuffer(); + if (node.hasChildNodes()) { + NodeList nl = node.getChildNodes(); + int nLen = nl.getLength(); + for (int i=0; i<nLen; i++) { + if (nl.item(i).getNodeType()==Node.TEXT_NODE) { + buf.append(nl.item(i).getNodeValue()); + } + } + } + return buf.toString(); + } + + /* Utility method that url encodes a string */ + public static String urlEncode(String s) { + try { + return URLEncoder.encode(s,"UTF-8"); + } + catch (UnsupportedEncodingException e) { + return ""; + } + } + + /* Utility method that url decodes a string */ + public static String urlDecode(String s) { + try { + return URLDecoder.decode(s,"UTF-8"); + } + catch (UnsupportedEncodingException e) { + return ""; + } + } + + /* utility method to make a file name valid for a href attribute + (ie. replace spaces with %20 etc.) + */ + public static String makeHref(String s) { + try { + java.net.URI uri = new java.net.URI(null, null, s, null); + return uri.toString(); + } + catch (Exception e) { + e.printStackTrace(); + } + return "error"; + } + + /** <p>Read an <code>InputStream</code> into a <code>byte</code>array</p> + * @param is the <code>InputStream</code> to read + * @return a byte array with the contents read from the stream + * @throws IOException in case of any I/O errors. + */ + public static byte[] inputStreamToByteArray(InputStream is) throws IOException { + if (is==null) { + throw new IOException ("No input stream to read"); + } + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + int nLen = 0; + byte buffer[] = new byte[BUFFERSIZE]; + while ((nLen = is.read(buffer)) > 0) { + baos.write(buffer, 0, nLen); + } + return baos.toByteArray(); + } + + + +} diff --git a/source/java/writer2latex/util/Package.html b/source/java/writer2latex/util/Package.html new file mode 100644 index 0000000..3536243 --- /dev/null +++ b/source/java/writer2latex/util/Package.html @@ -0,0 +1,11 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.util</title> +</head> + +<body> +<p>Some general utility classes.</p> +</body> +</html> diff --git a/source/java/writer2latex/util/SimpleInputBuffer.java b/source/java/writer2latex/util/SimpleInputBuffer.java new file mode 100644 index 0000000..a511577 --- /dev/null +++ b/source/java/writer2latex/util/SimpleInputBuffer.java @@ -0,0 +1,106 @@ +/************************************************************************ + * + * SimpleInputBuffer.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2007-11-22) + * + */ + +package writer2latex.util; + +/** This class provides a simple string input buffer; it can be used as the + * basis of a tokenizer. + */ +public class SimpleInputBuffer { + + private String sContent; + private int nIndex, nLen; + + /*private static boolean isEndOrLineEnd(char cChar) { + switch (cChar){ + case '\0': + case '\n': + case '\r': + return true; + default: + return false; + } + }*/ + + private static boolean isDigitOrDot(char cChar) { + return (cChar>='0' && cChar<='9') || cChar=='.'; + } + + private static boolean isDigitOrDotOrComma(char cChar) { + return isDigitOrDot(cChar) || cChar==','; + } + + public SimpleInputBuffer(String sContent) { + this.sContent=sContent; + nLen=sContent.length(); + nIndex=0; + } + + public int getIndex() { return nIndex; } + + public boolean atEnd() { + return nIndex>=nLen; + } + + public char peekChar() { + return nIndex<nLen ? sContent.charAt(nIndex) : '\0'; + } + + public char peekFollowingChar() { + return nIndex+1<nLen ? sContent.charAt(nIndex+1) : '\0'; + } + + public char getChar() { + return nIndex<nLen ? sContent.charAt(nIndex++) : '\0'; + } + + public String getIdentifier() { + int nStart=nIndex; + while (nIndex<nLen && (Character.isLetter(sContent.charAt(nIndex)) || + isDigitOrDot(sContent.charAt(nIndex)))) + nIndex++; + return sContent.substring(nStart,nIndex); + } + + public String getNumber() { + int nStart=nIndex; + while (nIndex<nLen && isDigitOrDotOrComma(sContent.charAt(nIndex))) + nIndex++; + return sContent.substring(nStart,nIndex); + } + + public String getInteger() { + int nStart=nIndex; + while (nIndex<nLen && sContent.charAt(nIndex)>='0' && sContent.charAt(nIndex)<='9'){ + nIndex++; + } + return sContent.substring(nStart,nIndex); + } + + public void skipSpaces() { + while (nIndex<nLen && sContent.charAt(nIndex)==' ') { nIndex++; } + } +} diff --git a/source/java/writer2latex/xhtml/BatchConverterImpl.java b/source/java/writer2latex/xhtml/BatchConverterImpl.java new file mode 100644 index 0000000..4cbe639 --- /dev/null +++ b/source/java/writer2latex/xhtml/BatchConverterImpl.java @@ -0,0 +1,231 @@ +/************************************************************************ + * + * BatchConverterImpl.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-08) + * + */ + +package writer2latex.xhtml; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Locale; +import java.text.Collator; +import org.w3c.dom.Element; + +import writer2latex.api.IndexPageEntry; +import writer2latex.api.OutputFile; +import writer2latex.base.BatchConverterBase; + +/** + * Implementation of <code>writer2latex.api.BatchConverter</code> for + * xhtml 1.0 strict + */ +public class BatchConverterImpl extends BatchConverterBase { + + private XhtmlConfig config; + private XhtmlDocument template; + + private String sDefaultLang; + private String sDefaultCountry; + private L10n l10n; + + public BatchConverterImpl() { + super(); + config = new XhtmlConfig(); + template = null; + + l10n = new L10n(); + sDefaultLang = System.getProperty("user.language"); + sDefaultCountry = System.getProperty("user.country"); + l10n.setLocale(sDefaultLang, sDefaultCountry); + } + + // Implementation of the remaining (xhtml specific) parts of the interface + + public writer2latex.api.Config getConfig() { + return config; + } + + public void readTemplate(InputStream is) throws IOException { + template = new XhtmlDocument("Template",XhtmlDocument.XHTML10); + try { + template.read(is); + } + catch (IOException e) { + template = null; + throw e; + } + } + + public void readTemplate(File file) throws IOException { + readTemplate(new FileInputStream(file)); + } + + protected String getIndexFileName() { + return "index.html"; + } + + public OutputFile createIndexFile(String sHeading, IndexPageEntry[] entries) { + // Create the index page (with header/footer or from template) + XhtmlDocument htmlDoc = new XhtmlDocument("index",XhtmlDocument.XHTML10); + htmlDoc.setEncoding(config.xhtmlEncoding()); + htmlDoc.setNoDoctype(config.xhtmlNoDoctype()); + htmlDoc.setAddBOM(config.xhtmlAddBOM()); + htmlDoc.setUseNamedEntities(config.useNamedEntities()); + if (template!=null) { htmlDoc.readFromTemplate(template); } + else { htmlDoc.createHeaderFooter(); } + + org.w3c.dom.Document htmlDOM = htmlDoc.getContentDOM(); + + // Declare charset (we need this for xhtml because we have no <?xml ... ?>) + Element meta = htmlDOM.createElement("meta"); + meta.setAttribute("http-equiv","Content-Type"); + meta.setAttribute("content","text/html; charset="+htmlDoc.getEncoding().toLowerCase()); + htmlDoc.getHeadNode().appendChild(meta); + + // Add link to stylesheet + if (config.xhtmlCustomStylesheet().length()>0) { + Element htmlStyle = htmlDOM.createElement("link"); + htmlStyle.setAttribute("rel","stylesheet"); + htmlStyle.setAttribute("type","text/css"); + htmlStyle.setAttribute("media","all"); + htmlStyle.setAttribute("href",config.xhtmlCustomStylesheet()); + htmlDoc.getHeadNode().appendChild(htmlStyle); + } + + // Add uplink to header and footer + Element header = htmlDoc.getHeaderNode(); + if (header!=null) { + if (config.getXhtmlUplink().length()>0) { + Element a = htmlDOM.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(htmlDOM.createTextNode(l10n.get(L10n.UP))); + header.appendChild(a); + } + else { + header.appendChild(htmlDOM.createTextNode(l10n.get(L10n.UP))); + } + } + + Element footer = htmlDoc.getFooterNode(); + if (footer!=null) { + if (config.getXhtmlUplink().length()>0) { + Element a = htmlDOM.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(htmlDOM.createTextNode(l10n.get(L10n.UP))); + footer.appendChild(a); + } + else { + footer.appendChild(htmlDOM.createTextNode(l10n.get(L10n.UP))); + } + } + + // Add title and heading + htmlDoc.getTitleNode().appendChild(htmlDOM.createTextNode(sHeading)); + Element h1 = htmlDOM.createElement("h1"); + htmlDoc.getContentNode().appendChild(h1); + h1.appendChild(htmlDOM.createTextNode(sHeading)); + + // Sort the entries + int nLen = entries.length; + Collator collator = Collator.getInstance(new Locale(sDefaultLang,sDefaultCountry)); + for (int i = 0; i<nLen; i++) { + if (entries[i]!=null) { + for (int j = i+1; j<nLen ; j++) { + if (entries[j]!=null) { + IndexPageEntry entryi = entries[i]; + IndexPageEntry entryj = entries[j]; + if (collator.compare(entryi.getDisplayName(), entryj.getDisplayName()) > 0) { + entries[i] = entryj; + entries[j] = entryi; + } + } + } + } + } + + // Insert directory entries + boolean bUseIcon = config.getXhtmlDirectoryIcon().length()>0; + for (int i=0; i<nLen; i++) { + if (entries[i]!=null && entries[i].isDirectory()) { + Element p = htmlDOM.createElement("p"); + htmlDoc.getContentNode().appendChild(p); + if (bUseIcon) { + Element img = htmlDOM.createElement("img"); + p.appendChild(img); + img.setAttribute("src",config.getXhtmlDirectoryIcon()); + img.setAttribute("alt",l10n.get(L10n.DIRECTORY)); + p.appendChild(htmlDOM.createTextNode(" ")); + } + Element a = htmlDOM.createElement("a"); + p.appendChild(a); + a.setAttribute("href",entries[i].getFile()); + a.appendChild(htmlDOM.createTextNode(entries[i].getDisplayName())); + } + } + + // Insert document entries + bUseIcon = config.getXhtmlDocumentIcon().length()>0; + for (int i=0; i<nLen; i++) { + if (entries[i]!=null && !entries[i].isDirectory()) { + Element p = htmlDOM.createElement("p"); + htmlDoc.getContentNode().appendChild(p); + if (bUseIcon) { + Element img = htmlDOM.createElement("img"); + p.appendChild(img); + img.setAttribute("src",config.getXhtmlDocumentIcon()); + img.setAttribute("alt",l10n.get(L10n.DOCUMENT)); + p.appendChild(htmlDOM.createTextNode(" ")); + } + // Add link to html file + if (entries[i].getFile()!=null) { + Element a = htmlDOM.createElement("a"); + p.appendChild(a); + a.setAttribute("href",entries[i].getFile()); + a.appendChild(htmlDOM.createTextNode(entries[i].getDisplayName())); + } + else { + p.appendChild(htmlDOM.createTextNode(entries[i].getDisplayName())); + } + // Add link to pdf file + if (entries[i].getPdfFile()!=null) { + p.appendChild(htmlDOM.createTextNode(" ")); + Element pdfa = htmlDOM.createElement("a"); + p.appendChild(pdfa); + pdfa.setAttribute("href",entries[i].getPdfFile()); + pdfa.appendChild(htmlDOM.createTextNode("pdf")); + } + // TODO: Add link to original file if defined + // Add description if available + if (entries[i].getDescription()!=null) { + p.appendChild(htmlDOM.createTextNode(": "+entries[i].getDescription())); + } + } + } + + return htmlDoc; + } + +} diff --git a/source/java/writer2latex/xhtml/CellStyleConverter.java b/source/java/writer2latex/xhtml/CellStyleConverter.java new file mode 100644 index 0000000..def4d90 --- /dev/null +++ b/source/java/writer2latex/xhtml/CellStyleConverter.java @@ -0,0 +1,104 @@ +/************************************************************************ + * + * CellStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; + +/** + * This class converts OpenDocument cell styles to CSS2 styles. + * Cells are formatted using box properties and alignment. + */ +public class CellStyleConverter extends StyleWithPropertiesConverterHelper { + + /** Create a new <code>CellStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public CellStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + // Style maps for Cells are currently not supported. + // (In OOo, cell styles are only supported by Calc) + this.styleMap = new XhtmlStyleMap(); + this.bConvertStyles = config.xhtmlTableFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlTableFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlTableFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlTableFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + /** Get the family of cell styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getCellStyles(); + } + + /** Create default tag name to represent a Cell object + * @param style to use + * @return the tag name. + */ + public String getDefaultTagName(StyleWithProperties style) { + return "td"; + } + + /** Convert formatting properties for a specific Cell style. + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit) { + // Apply "inner" box properties (no margins) + getFrameSc().cssBorder(style,props,bInherit); + getFrameSc().cssPadding(style,props,bInherit); + getFrameSc().cssBackground(style,props,bInherit); + // only relevant for spreadsheets + getParSc().cssPar(style,props,bInherit); + getTextSc().cssTextCommon(style,props,bInherit); + // Cell-specific properties (vertical alignment) + cssCell(style,props,bInherit); + } + + private void cssCell(StyleWithProperties style, CSVList props, boolean bInherit){ + // Vertical align: Some values fit with css + String s = ofr.isOpenDocument() ? + style.getProperty(XMLString.STYLE_VERTICAL_ALIGN,bInherit) : + style.getProperty(XMLString.FO_VERTICAL_ALIGN,bInherit); + if ("middle".equals(s)) { props.addValue("vertical-align","middle"); } + else if ("bottom".equals(s)) { props.addValue("vertical-align","bottom"); } + else if ("top".equals(s)) { props.addValue("vertical-align","top"); } + else { + // No value or "automatic" means, according to the spec, + //"The application decide how to align the text." + // We treat this case like OOo does: + props.addValue("vertical-align", ofr.isSpreadsheet() ? "bottom" : "top"); + } + } + +} diff --git a/source/java/writer2latex/xhtml/Converter.java b/source/java/writer2latex/xhtml/Converter.java new file mode 100644 index 0000000..9bcc643 --- /dev/null +++ b/source/java/writer2latex/xhtml/Converter.java @@ -0,0 +1,628 @@ +/************************************************************************ + * + * Converter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-16) + * + */ + +package writer2latex.xhtml; + +import java.io.File; +import java.io.FileInputStream; +import java.util.ListIterator; +import java.util.LinkedList; +import java.util.Vector; +import java.util.Hashtable; +import java.util.Iterator; + +import java.io.InputStream; +import java.io.IOException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Text; + +import writer2latex.api.Config; +import writer2latex.api.ConverterFactory; +//import writer2latex.api.ConverterResult; +import writer2latex.base.ConverterBase; +//import writer2latex.latex.LaTeXDocumentPortion; +//import writer2latex.latex.util.Context; +import writer2latex.office.MIMETypes; +import writer2latex.office.OfficeReader; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.ExportNameCollection; +import writer2latex.util.Misc; + +/** + * <p>This class converts an OpenDocument file to an XHTML(+MathML) document<.</p> + * + */ +public class Converter extends ConverterBase { + // Config + private XhtmlConfig config; + + public Config getConfig() { return config; } + + // The locale + private L10n l10n; + + // The helpers + private StyleConverter styleCv; + private TextConverter textCv; + private TableConverter tableCv; + private DrawConverter drawCv; + private MathConverter mathCv; + + // The template + private XhtmlDocument template = null; + + // The xhtml output file(s) + protected int nType = XhtmlDocument.XHTML10; // the doctype + Vector outFiles; + private int nOutFileIndex; + private XhtmlDocument htmlDoc; // current outfile + private Document htmlDOM; // current DOM, usually within htmlDoc + private boolean bNeedHeaderFooter = false; + + // Hyperlinks + Hashtable targets = new Hashtable(); + LinkedList links = new LinkedList(); + // Strip illegal characters from internal hyperlink targets + private ExportNameCollection targetNames = new ExportNameCollection(true); + + // Constructor setting the DOCTYPE + public Converter(int nType) { + super(); + config = new XhtmlConfig(); + this.nType = nType; + } + + // override + public void readTemplate(InputStream is) throws IOException { + template = new XhtmlDocument("Template",nType); + template.read(is); + } + + public void readTemplate(File file) throws IOException { + readTemplate(new FileInputStream(file)); + } + + protected StyleConverter getStyleCv() { return styleCv; } + + protected TextConverter getTextCv() { return textCv; } + + protected TableConverter getTableCv() { return tableCv; } + + protected DrawConverter getDrawCv() { return drawCv; } + + protected MathConverter getMathCv() { return mathCv; } + + protected int getType() { return nType; } + + protected int getOutFileIndex() { return nOutFileIndex; } + + protected Element createElement(String s) { return htmlDOM.createElement(s); } + + protected Text createTextNode(String s) { return htmlDOM.createTextNode(s); } + + protected Node importNode(Node node, boolean bDeep) { return htmlDOM.importNode(node,bDeep); } + + protected L10n getL10n() { return l10n; } + + // override + public void convertInner() throws IOException { + sTargetFileName = Misc.trimDocumentName(sTargetFileName,XhtmlDocument.getExtension(nType)); + + outFiles = new Vector(); + nOutFileIndex = -1; + + bNeedHeaderFooter = ofr.isSpreadsheet() || ofr.isPresentation() || config.getXhtmlSplitLevel()>0 || config.getXhtmlUplink().length()>0; + + l10n = new L10n(); + + imageLoader.setUseSubdir(config.saveImagesInSubdir()); + + imageLoader.setDefaultFormat(MIMETypes.PNG); + imageLoader.addAcceptedFormat(MIMETypes.JPEG); + imageLoader.addAcceptedFormat(MIMETypes.GIF); + + styleCv = new StyleConverter(ofr,config,this,nType); + textCv = new TextConverter(ofr,config,this); + tableCv = new TableConverter(ofr,config,this); + drawCv = new DrawConverter(ofr,config,this); + mathCv = new MathConverter(ofr,config,this,nType!=XhtmlDocument.XHTML10); + + // Set locale to document language + StyleWithProperties style = ofr.isSpreadsheet() ? ofr.getDefaultCellStyle() : ofr.getDefaultParStyle(); + if (style!=null) { + String sLang = style.getProperty(XMLString.FO_LANGUAGE); + String sCountry = style.getProperty(XMLString.FO_COUNTRY); + if (sLang!=null) { + if (sCountry==null) { l10n.setLocale(sLang); } + else { l10n.setLocale(sLang+"-"+sCountry); } + } + } + + //NodeList list; + // Traverse the body + Element body = ofr.getContent(); + if (ofr.isSpreadsheet()) { tableCv.convertTableContent(body); } + else if (ofr.isPresentation()) { drawCv.convertDrawContent(body); } + else { textCv.convertTextContent(body); } + + // Add footnotes and endnotes + textCv.insertFootnotes(htmlDoc.getContentNode()); + textCv.insertEndnotes(htmlDoc.getContentNode()); + + // Resolve links + ListIterator iter = links.listIterator(); + while (iter.hasNext()) { + LinkDescriptor ld = (LinkDescriptor) iter.next(); + Integer targetIndex = (Integer) targets.get(ld.sId); + if (targetIndex!=null) { + int nTargetIndex = targetIndex.intValue(); + if (nTargetIndex == ld.nIndex) { // same file + ld.element.setAttribute("href","#"+targetNames.getExportName(ld.sId)); + } + else { + ld.element.setAttribute("href",getOutFileName(nTargetIndex,true) + +"#"+targetNames.getExportName(ld.sId)); + } + } + } + + // Export styles (temp.) + for (int i=0; i<=nOutFileIndex; i++) { + Document dom = ((XhtmlDocument) outFiles.get(i)).getContentDOM(); + NodeList hlist = dom.getElementsByTagName("head"); + Node styles = styleCv.exportStyles(dom); + if (styles!=null) { + hlist.item(0).appendChild(styles); + } + } + + // Create headers & footers (if nodes are available) + if (ofr.isSpreadsheet()) { + for (int i=0; i<=nOutFileIndex; i++) { + + XhtmlDocument doc = (XhtmlDocument) outFiles.get(i); + Document dom = doc.getContentDOM(); + Element header = doc.getHeaderNode(); + Element footer = doc.getFooterNode(); + Element headerPar = dom.createElement("p"); + Element footerPar = dom.createElement("p"); + footerPar.setAttribute("style","clear:both"); // no floats may pass! + + // Add uplink + if (config.getXhtmlUplink().length()>0) { + Element a = dom.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(dom.createTextNode(l10n.get(L10n.UP))); + headerPar.appendChild(a); + headerPar.appendChild(dom.createTextNode(" ")); + a = dom.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(dom.createTextNode(l10n.get(L10n.UP))); + footerPar.appendChild(a); + footerPar.appendChild(dom.createTextNode(" ")); + } + // Add links to all sheets: + int nSheets = tableCv.sheetNames.size(); + for (int j=0; j<nSheets; j++) { + if (config.xhtmlCalcSplit()) { + addNavigationLink(dom,headerPar,(String) tableCv.sheetNames.get(j),j); + addNavigationLink(dom,footerPar,(String) tableCv.sheetNames.get(j),j); + } + else { + addInternalNavigationLink(dom,headerPar,(String) tableCv.sheetNames.get(j),"tableheading"+j); + addInternalNavigationLink(dom,footerPar,(String) tableCv.sheetNames.get(j),"tableheading"+j); + } + } + + if (header!=null) { header.appendChild(headerPar); } + if (footer!=null) { footer.appendChild(footerPar); } + } + } + else if (ofr.isPresentation() || config.getXhtmlSplitLevel()>0) { + for (int i=0; i<=nOutFileIndex; i++) { + XhtmlDocument doc = (XhtmlDocument) outFiles.get(i); + Document dom = doc.getContentDOM(); + //Element content = doc.getContentNode(); + + // Header links + Element header = doc.getHeaderNode(); + if (header!=null) { + if (ofr.isPresentation()) { + // Absolute placement in presentations (quick and dirty solution) + header.setAttribute("style","position:absolute;top:0;left:0"); + } + if (config.getXhtmlUplink().length()>0) { + Element a = dom.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(dom.createTextNode(l10n.get(L10n.UP))); + header.appendChild(a); + header.appendChild(dom.createTextNode(" ")); + } + addNavigationLink(dom,header,l10n.get(L10n.FIRST),0); + addNavigationLink(dom,header,l10n.get(L10n.PREVIOUS),i-1); + addNavigationLink(dom,header,l10n.get(L10n.NEXT),i+1); + addNavigationLink(dom,header,l10n.get(L10n.LAST),nOutFileIndex); + if (textCv.getTocIndex()>=0) { + addNavigationLink(dom,header,l10n.get(L10n.CONTENTS),textCv.getTocIndex()); + } + if (textCv.getAlphabeticalIndex()>=0) { + addNavigationLink(dom,header,l10n.get(L10n.INDEX),textCv.getAlphabeticalIndex()); + } + } + + // Footer links + Element footer = doc.getFooterNode(); + if (footer!=null && !ofr.isPresentation()) { + // No footer in presentations + if (config.getXhtmlUplink().length()>0) { + Element a = dom.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(dom.createTextNode(l10n.get(L10n.UP))); + footer.appendChild(a); + footer.appendChild(dom.createTextNode(" ")); + } + addNavigationLink(dom,footer,l10n.get(L10n.FIRST),0); + addNavigationLink(dom,footer,l10n.get(L10n.PREVIOUS),i-1); + addNavigationLink(dom,footer,l10n.get(L10n.NEXT),i+1); + addNavigationLink(dom,footer,l10n.get(L10n.LAST),nOutFileIndex); + if (textCv.getTocIndex()>=0) { + addNavigationLink(dom,footer,l10n.get(L10n.CONTENTS),textCv.getTocIndex()); + } + if (textCv.getAlphabeticalIndex()>=0) { + addNavigationLink(dom,footer,l10n.get(L10n.INDEX),textCv.getAlphabeticalIndex()); + } + } + } + } + else if (config.getXhtmlUplink().length()>0) { + for (int i=0; i<=nOutFileIndex; i++) { + XhtmlDocument doc = (XhtmlDocument) outFiles.get(i); + Document dom = doc.getContentDOM(); + //Element content = doc.getContentNode(); + + Element header = doc.getHeaderNode(); + if (header!=null) { + Element a = dom.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(dom.createTextNode(l10n.get(L10n.UP))); + header.appendChild(a); + header.appendChild(dom.createTextNode(" ")); + } + + Element footer = doc.getFooterNode(); + if (footer!=null) { + Element a = dom.createElement("a"); + a.setAttribute("href",config.getXhtmlUplink()); + a.appendChild(dom.createTextNode(l10n.get(L10n.UP))); + footer.appendChild(a); + footer.appendChild(dom.createTextNode(" ")); + } + } + } + + } + + private void addNavigationLink(Document dom, Node node, String s, int nIndex) { + if (nIndex>=0 && nIndex<=nOutFileIndex) { + Element a = dom.createElement("a"); + a.setAttribute("href",Misc.makeHref(getOutFileName(nIndex,true))); + a.appendChild(dom.createTextNode(s)); + //node.appendChild(dom.createTextNode("[")); + node.appendChild(a); + node.appendChild(dom.createTextNode(" ")); + //node.appendChild(dom.createTextNode("] ")); + } + else { + Element span = dom.createElement("span"); + span.setAttribute("class","nolink"); + node.appendChild(span); + span.appendChild(dom.createTextNode(s)); + node.appendChild(dom.createTextNode(" ")); + //node.appendChild(dom.createTextNode("["+s+"] ")); + } + } + + private void addInternalNavigationLink(Document dom, Node node, String s, String sLink) { + Element a = dom.createElement("a"); + a.setAttribute("href","#"+sLink); + a.appendChild(dom.createTextNode(s)); + //node.appendChild(dom.createTextNode("[")); + node.appendChild(a); + node.appendChild(dom.createTextNode(" ")); + //node.appendChild(dom.createTextNode("] ")); + } + + /* get inline text, ignoring any draw objects, footnotes, formatting and hyperlinks */ + private String getPlainInlineText(Node node) { + StringBuffer buf = new StringBuffer(); + Node child = node.getFirstChild(); + while (child!=null) { + short nodeType = child.getNodeType(); + + switch (nodeType) { + case Node.TEXT_NODE: + buf.append(child.getNodeValue()); + break; + + case Node.ELEMENT_NODE: + String sName = child.getNodeName(); + if (sName.equals(XMLString.TEXT_S)) { + buf.append(" "); + } + else if (sName.equals(XMLString.TEXT_TAB_STOP) || sName.equals(XMLString.TEXT_TAB)) { // text:tab in oasis + buf.append(" "); + } + else if (OfficeReader.isNoteElement(child)) { + // ignore + } + else if (OfficeReader.isTextElement(child)) { + buf.append(getPlainInlineText(child)); + } + break; + default: + // Do nothing + } + child = child.getNextSibling(); + } + return buf.toString(); + } + + + public void handleOfficeAnnotation(Node onode, Node hnode) { + if (config.xhtmlNotes()) { + // Extract the text from the paragraphs, seperate paragraphs with newline + StringBuffer buf = new StringBuffer(); + Node child = onode.getFirstChild(); + while (child!=null) { + if (Misc.isElement(child, XMLString.TEXT_P)) { + if (buf.length()>0) { buf.append('\n'); } + buf.append(getPlainInlineText(child)); + } + child = child.getNextSibling(); + } + Node commentNode = htmlDOM.createComment(buf.toString()); + hnode.appendChild(commentNode); + } + } + + ///////////////////////////////////////////////////////////////////////// + // UTILITY METHODS + + // Create output file name (docname.html, docname1.html, docname2.html etc.) + public String getOutFileName(int nIndex, boolean bWithExt) { + return sTargetFileName + (nIndex>0 ? Integer.toString(nIndex) : "") + + (bWithExt ? htmlDoc.getFileExtension() : ""); + } + + // Return true if the current outfile has a non-empty body + public boolean outFileHasContent() { + return htmlDoc.getContentNode().hasChildNodes(); + } + + // Use another document. TODO: This is very ugly; clean it up!!! + public void changeOutFile(int nIndex) { + nOutFileIndex = nIndex; + htmlDoc = (XhtmlDocument) outFiles.get(nIndex); + htmlDOM = htmlDoc.getContentDOM(); + } + + public Element getPanelNode() { + return htmlDoc.getPanelNode(); + } + + // Prepare next output file + public Element nextOutFile() { + if (nOutFileIndex>=0) { textCv.insertFootnotes(htmlDoc.getContentNode()); } + htmlDoc = new XhtmlDocument(getOutFileName(++nOutFileIndex,false),nType); + htmlDoc.setEncoding(config.xhtmlEncoding()); + htmlDoc.setNoDoctype(config.xhtmlNoDoctype()); + htmlDoc.setAddBOM(config.xhtmlAddBOM()); + htmlDoc.setUseNamedEntities(config.useNamedEntities()); + htmlDoc.setXsltPath(config.getXsltPath()); + if (template!=null) { htmlDoc.readFromTemplate(template); } + else if (bNeedHeaderFooter) { htmlDoc.createHeaderFooter(); } + outFiles.add(nOutFileIndex,htmlDoc); + convertData.addDocument(htmlDoc); + + // Create head + body + htmlDOM = htmlDoc.getContentDOM(); + Element rootElement = htmlDOM.getDocumentElement(); + styleCv.applyDefaultLanguage(rootElement); + rootElement.insertBefore(htmlDOM.createComment( + "This file was converted to xhtml by " + + (ofr.isText() ? "Writer" : (ofr.isSpreadsheet() ? "Calc" : "Impress")) + + "2xhtml ver. " + ConverterFactory.getVersion() + + ". See http://www.hj-gym.dk/~hj/writer2latex for more info."), + rootElement.getFirstChild()); + + // Apply page formatting (using first master page) + if (ofr.getFirstMasterPage()!=null && !ofr.isPresentation()) { + StyleInfo pageInfo = new StyleInfo(); + styleCv.getPageSc().applyStyle(ofr.getFirstMasterPage().getName(),pageInfo); + styleCv.getPageSc().applyStyle(pageInfo,htmlDoc.getContentNode()); + } + + // Add title (required by xhtml) + String sTitle = metaData.getTitle(); + if (sTitle==null) { // use filename as fallback + sTitle = htmlDoc.getFileName(); + } + htmlDoc.getTitleNode().appendChild( htmlDOM.createTextNode(sTitle) ); + + // Declare charset (we need this for xhtml because we have no <?xml ... ?>) + if (nType==XhtmlDocument.XHTML10) { + Element meta = htmlDOM.createElement("meta"); + meta.setAttribute("http-equiv","Content-Type"); + meta.setAttribute("content","text/html; charset="+htmlDoc.getEncoding().toLowerCase()); + htmlDoc.getHeadNode().appendChild(meta); + } + + // "Traditional" meta data + //createMeta("generator","Writer2LaTeX "+Misc.VERSION); + createMeta("description",metaData.getDescription()); + createMeta("keywords",metaData.getKeywords()); + + // Dublin core meta data (optional) + // Format as recommended on dublincore.org + // Declare meta data profile + if (config.xhtmlUseDublinCore()) { + htmlDoc.getHeadNode().setAttribute("profile","http://dublincore.org/documents/dcq-html/"); + // Add link to declare namespace + Element dclink = htmlDOM.createElement("link"); + dclink.setAttribute("rel","schema.DC"); + dclink.setAttribute("href","http://purl.org/dc/elements/1.1/"); + htmlDoc.getHeadNode().appendChild(dclink); + // Insert the actual meta data + createMeta("DC.title",metaData.getTitle()); + // DC.subject actually contains subject+keywords, so we merge them + String sDCSubject = metaData.getSubject(); + if (metaData.getSubject().length()>0 && metaData.getKeywords().length()>0) { + sDCSubject+=", "; + } + sDCSubject+=metaData.getKeywords(); + createMeta("DC.subject",sDCSubject); + createMeta("DC.description",metaData.getDescription()); + createMeta("DC.creator",metaData.getCreator()); + createMeta("DC.date",metaData.getDate()); + createMeta("DC.language",metaData.getLanguage()); + } + + // Add link to stylesheet + if (config.xhtmlCustomStylesheet().length()>0) { + Element htmlStyle = htmlDOM.createElement("link"); + htmlStyle.setAttribute("rel","stylesheet"); + htmlStyle.setAttribute("type","text/css"); + htmlStyle.setAttribute("media","all"); + htmlStyle.setAttribute("href",config.xhtmlCustomStylesheet()); + htmlDoc.getHeadNode().appendChild(htmlStyle); + } + /* later.... + if (nSplit>0 && !config.xhtmlIgnoreStyles()) { + Element htmlStyle = htmlDOM.createElement("link"); + htmlStyle.setAttribute("rel","stylesheet"); + htmlStyle.setAttribute("type","text/css"); + htmlStyle.setAttribute("media","all"); + htmlStyle.setAttribute("href",oooDoc.getName()+"-styles.css"); + htmlHead.appendChild(htmlStyle); + }*/ + // Note: For single output file, styles are exported to the doc at the end. + + // Recreate nested sections, if any + if (!textCv.sections.isEmpty()) { + Iterator iter = textCv.sections.iterator(); + while (iter.hasNext()) { + Element section = (Element) iter.next(); + String sStyleName = Misc.getAttribute(section,XMLString.TEXT_STYLE_NAME); + Element div = htmlDOM.createElement("div"); + htmlDoc.getContentNode().appendChild(div); + htmlDoc.setContentNode(div); + StyleInfo sectionInfo = new StyleInfo(); + styleCv.getSectionSc().applyStyle(sStyleName,sectionInfo); + styleCv.getSectionSc().applyStyle(sectionInfo,div); + } + } + + return htmlDoc.getContentNode(); + } + + // create a target + public Element createTarget(String sId) { + Element a = htmlDOM.createElement("a"); + a.setAttribute("id",targetNames.getExportName(sId)); + targets.put(sId, new Integer(nOutFileIndex)); + return a; + } + + // put a target id on an existing element + public void addTarget(Element node,String sId) { + node.setAttribute("id",targetNames.getExportName(sId)); + targets.put(sId, new Integer(nOutFileIndex)); + } + + // create an internal link + public Element createLink(String sId) { + Element a = htmlDOM.createElement("a"); + LinkDescriptor ld = new LinkDescriptor(); + ld.element = a; ld.sId = sId; ld.nIndex = nOutFileIndex; + links.add(ld); + return a; + } + + // create a link + public Element createLink(Element onode) { + // First create the anchor + String sHref = onode.getAttribute(XMLString.XLINK_HREF); + Element anchor; + if (sHref.startsWith("#")) { // internal link + anchor = createLink(sHref.substring(1)); + } + else { // external link + anchor = htmlDOM.createElement("a"); + + // Workaround for an OOo problem: + if (sHref.indexOf("?")==-1) { // No question mark + int n3F=sHref.indexOf("%3F"); + if (n3F>0) { // encoded question mark + sHref = sHref.substring(0,n3F)+"?"+sHref.substring(n3F+3); + } + } + anchor.setAttribute("href",sHref); + String sName = Misc.getAttribute(onode,XMLString.OFFICE_NAME); + if (sName!=null) { + anchor.setAttribute("name",sName); + anchor.setAttribute("title",sName); // OOo does not have title... + } + // TODO: target has been deprecated in xhtml 1.0 strict + String sTarget = Misc.getAttribute(onode,XMLString.OFFICE_TARGET_FRAME_NAME); + if (sTarget!=null) { anchor.setAttribute("target",sTarget); } + } + + // Then style it + String sStyleName = onode.getAttribute(XMLString.TEXT_STYLE_NAME); + String sVisitedStyleName = onode.getAttribute(XMLString.TEXT_VISITED_STYLE_NAME); + StyleInfo anchorInfo = new StyleInfo(); + styleCv.getTextSc().applyAnchorStyle(sStyleName,sVisitedStyleName,anchorInfo); + styleCv.getTextSc().applyStyle(anchorInfo,anchor); + + return anchor; + } + + + private void createMeta(String sName, String sValue) { + if (sValue==null) { return; } + Element meta = htmlDOM.createElement("meta"); + meta.setAttribute("name",sName); + meta.setAttribute("content",sValue); + htmlDoc.getHeadNode().appendChild(meta); + } + + +} \ No newline at end of file diff --git a/source/java/writer2latex/xhtml/ConverterHelper.java b/source/java/writer2latex/xhtml/ConverterHelper.java new file mode 100644 index 0000000..149cfae --- /dev/null +++ b/source/java/writer2latex/xhtml/ConverterHelper.java @@ -0,0 +1,92 @@ +/************************************************************************ + * + * ConverterHelper.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import org.w3c.dom.Element; + +import writer2latex.office.OfficeReader; + +public class ConverterHelper { + protected OfficeReader ofr; + protected XhtmlConfig config; + protected Converter converter; + + protected StyleConverter getStyleCv() { return converter.getStyleCv(); } + + protected TextStyleConverter getTextSc() { return converter.getStyleCv().getTextSc(); } + + protected ParStyleConverter getParSc() { return converter.getStyleCv().getParSc(); } + + protected ListStyleConverter getListSc() { return converter.getStyleCv().getListSc(); } + + protected SectionStyleConverter getSectionSc() { return converter.getStyleCv().getSectionSc(); } + + protected TableStyleConverter getTableSc() { return converter.getStyleCv().getTableSc(); } + + protected RowStyleConverter getRowSc() { return converter.getStyleCv().getRowSc(); } + + protected CellStyleConverter getCellSc() { return converter.getStyleCv().getCellSc(); } + + protected FrameStyleConverter getFrameSc() { return converter.getStyleCv().getFrameSc(); } + + protected PresentationStyleConverter getPresentationSc() { return converter.getStyleCv().getPresentationSc(); } + + protected PageStyleConverter getPageSc() { return converter.getStyleCv().getPageSc(); } + + protected TextConverter getTextCv() { return converter.getTextCv(); } + + protected TableConverter getTableCv() { return converter.getTableCv(); } + + protected DrawConverter getDrawCv() { return converter.getDrawCv(); } + + protected MathConverter getMathCv() { return converter.getMathCv(); } + + // TODO: Move to StyleInfo! + protected void applyStyle(StyleInfo info, Element hnode) { + if (info.sClass!=null) { + hnode.setAttribute("class",info.sClass); + } + if (!info.props.isEmpty()) { + hnode.setAttribute("style",info.props.toString()); + } + if (info.sLang!=null) { + hnode.setAttribute("xml:lang",info.sLang); + if (converter.getType()==XhtmlDocument.XHTML10) { + hnode.setAttribute("lang",info.sLang); // HTML4 compatibility + } + } + if (info.sDir!=null) { + hnode.setAttribute("dir",info.sDir); + } + } + + public ConverterHelper(OfficeReader ofr, XhtmlConfig config, Converter converter) { + this.ofr = ofr; + this.config = config; + this.converter = converter; + } +} diff --git a/source/java/writer2latex/xhtml/DrawConverter.java b/source/java/writer2latex/xhtml/DrawConverter.java new file mode 100644 index 0000000..db29009 --- /dev/null +++ b/source/java/writer2latex/xhtml/DrawConverter.java @@ -0,0 +1,939 @@ +/************************************************************************ + * + * DrawConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-12-15) + * + */ + + /* TODO (impress2xhtml) + * Support master page content! + * New option: xhtml_draw_scaling: scale all draw objects this percentage + * (applies to applySize in this class + page size in PageStyleConverter) + * Certain options should have a fixed value for impress2xhtml: + * original_image_size: always false + * xhtml_formatting: always "convert_all" + * xhtml_frame_formatting: always "convert_all" + * xhtml_use_list_hack: always "true" (until list merge is fixed..) + * apply hard draw page background (see below) + * apply z-order for draw objects (frames) + * export notes (part of draw-page) + * export list-style-image for image bullets! + */ + +package writer2latex.xhtml; + +import java.util.Iterator; +import java.util.Vector; + +import java.io.IOException; + +import org.xml.sax.SAXException; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Element; + +import writer2latex.xmerge.EmbeddedObject; +//import writer2latex.xmerge.EmbeddedBinaryObject; +import writer2latex.xmerge.EmbeddedXMLObject; + +import writer2latex.util.Misc; +import writer2latex.util.CSVList; +import writer2latex.xmerge.BinaryGraphicsDocument; +import writer2latex.office.XMLString; +import writer2latex.office.MIMETypes; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.FormReader; +import writer2latex.office.ControlReader; +//import writer2latex.office.MasterPage; +//import writer2latex.office.PageLayout; +import writer2latex.office.OfficeReader; +//import writer2latex.xhtml.XhtmlStyleMap; + +public class DrawConverter extends ConverterHelper { + + /** Identifies objects that should be displayed inline. + */ + public static final int INLINE = 0; + + /** Identifies objects that should be displayed as floats, either alone + * or with text wrap (using the css attribute float:left or float:right) + */ + public static final int FLOATING = 1; + + /** Identifies objects that should be positioned absolute (using the css + * attribute postion:absolute) + */ + public static final int ABSOLUTE = 2; + + /** Identifies objects that should be placed centered */ + public static final int CENTERED = 3; + + private FormReader form = null; + private String sScale; + private boolean bConvertToPx; + private boolean bOriginalImageSize; + + // Frames in spreadsheet documents are collected here + private Vector frames = new Vector(); + // This flag determines wether to collect frames or insert them immediately + private boolean bCollectFrames = false; + + public DrawConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter); + // We can only handle one form; pick an arbitrary one. + // Also we cannot split a form over several files. + Iterator formsIterator = ofr.getForms().getFormsIterator(); + if (formsIterator.hasNext() && config.getXhtmlSplitLevel()==0) { + form = (FormReader) formsIterator.next(); + } + bCollectFrames = ofr.isSpreadsheet(); + sScale = config.getXhtmlScaling(); + bConvertToPx = config.xhtmlConvertToPx(); + bOriginalImageSize = config.originalImageSize(); + } + + /////////////////////////////////////////////////////////////////////// + // Complete Draw documents/presentations + + public void convertDrawContent(Element onode) { + if (!onode.hasChildNodes()) { return; } + NodeList nList = onode.getChildNodes(); + int nLen = nList.getLength(); + for (int i=0; i<nLen; i++) { + Node child = nList.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sNodeName = child.getNodeName(); + if (sNodeName.equals(XMLString.DRAW_PAGE)) { + handleDrawPage((Element)child,converter.nextOutFile()); + } + } + } + } + + private void handleDrawPage(Element onode, Element hnode) { + Element div = converter.createElement("div"); + hnode.appendChild(div); + + // Style it (TODO: Apply hard drawing-page (background) style) + StyleInfo info = new StyleInfo(); + getPageSc().applyStyle(onode.getAttribute(XMLString.DRAW_MASTER_PAGE_NAME),info); + info.props.addValue("top","40px"); // Somewhat arbitrary + info.props.addValue("left","0"); + info.props.addValue("position","absolute"); + applyStyle(info,div); + + // Traverse the draw:page + if (!onode.hasChildNodes()) { return; } + NodeList nList = onode.getChildNodes(); + int nLen = nList.getLength(); + for (int i=0; i<nLen; i++) { + Node child = nList.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + handleDrawElement((Element)child,div,div,ABSOLUTE); + } + } + } + + ///////////////////////////////////////////////////////////////////////// + // Form document + + /** <p>Create form, if there is a form in this document</p> + * @return the form element, or null if there are no forms in the document + */ + public Element createForm() { + if (form==null) return null; + Element htmlForm = converter.createElement("form"); + htmlForm.setAttribute("name", form.getAttribute(XMLString.FORM_NAME)); + htmlForm.setAttribute("action", form.getAttribute(XMLString.XLINK_HREF)); + String sMethod = form.getAttribute(XMLString.FORM_METHOD); + htmlForm.setAttribute("method", sMethod!=null ? sMethod : "get"); + return htmlForm; + } + + ///////////////////////////////////////////////////////////////////////// + // DRAW ELEMENTS + + /** <p> A draw element with a hyperlink is represented as two elements, + * eg. <code><draw:a><draw:image/></draw:a></code>. + * We thus need methods to switch between the two elements.</p> + * <p> This method takes a <code>draw</code>-element. + * If this element is a hyperlink, the child element is returned. + * Otherwise the argument is returned unchanged.</p> + * @param onode the <code>draw:a</code> element + * @return the corresponding element + */ + public Element getRealDrawElement(Element onode) { + if (XMLString.DRAW_A.equals(onode.getTagName())) { + Node child = onode.getFirstChild(); + while (child!=null) { + if (OfficeReader.isDrawElement(child)) { return (Element) child; } + child = child.getNextSibling(); + } + return null; // empty anchor + } + return onode; + } + + /** <p> A draw element with a hyperlink is represented as two elements, + * eg. <code><draw:a><draw:image/></draw:a></code>. + * We thus need methods to switch between the two elements.</p> + * <p> This method takes a <code>draw</code>-element. + * If this element is contained in a hyperlink, the hyperlink is returned. + * Otherwise null is returned.</p> + * @param onode the <code>draw:a</code> element + * @return the hyperlink element, if any + */ + public Element getDrawAnchor(Element onode) { + Element parent = (Element) onode.getParentNode(); + // in oasis format, we need to skip the frame as well + if (XMLString.DRAW_FRAME.equals(parent.getTagName())) { + parent = (Element) parent.getParentNode(); + } + if (XMLString.DRAW_A.equals(parent.getTagName())) { return parent; } + return null; + } + + private Element getFrame(Element onode) { + if (ofr.isOpenDocument()) return (Element) onode.getParentNode(); + else return onode; + } + + public void flushFrames(Element hnode) { + bCollectFrames = false; + int nCount = frames.size(); + for (int i=0; i<nCount; i++) { + handleDrawElement((Element) frames.get(i),hnode,null,CENTERED); + } + frames.clear(); + bCollectFrames = true; + } + + /** <p>Convert a draw element to xhtml. The presentation depends on the + * parameter <code>nMode</code>:</p> + * <ul><li><code>DrawConverter.INLINE</code>: Presented inline. The hnode + * must accept inline content. An inline container <em>must</em> be + * provided.</li> + * <li><code>DrawConverter.FLOAT</code>: Presented as a float. The hnode + * must accept block/flow content. A block container <em>must</em> be + * provided.</li> + * <li><code>DrawConverter.ABSOLUTE</code>: Presented at an absolute + * position. A block container <em>must</em> be provided.</li> + * </ul> + * <p>Containers for block and inline elements should be supplied. + * The containers may be identical (flow container).</p> + * <p>Note: A draw:text-box will be ignored in inline mode.</p> + * @param onode the draw element + * @param hnodeBlock the xhtml element to attach the converted element to if it's a block element + * @param hnodeInline the xhtml element to attach the converted element to if it's an inline element + * @param nMode identifies how the element should be presented + */ + public void handleDrawElement(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) { + if (bCollectFrames) { + frames.add(onode); + return; + } + String sName = onode.getNodeName(); + if (sName.equals(XMLString.DRAW_OBJECT)) { + handleDrawObject(onode,hnodeBlock,hnodeInline,nMode); + } + else if (sName.equals(XMLString.DRAW_OBJECT_OLE)) { + handleDrawObject(onode,hnodeBlock,hnodeInline,nMode); + } + else if (sName.equals(XMLString.DRAW_IMAGE)) { + handleDrawImage(onode,hnodeBlock,hnodeInline,nMode); + } + else if (sName.equals(XMLString.DRAW_TEXT_BOX)) { + handleDrawTextBox(onode,hnodeBlock,hnodeInline,nMode); + } + else if (sName.equals(XMLString.DRAW_A)) { + Element elm = getRealDrawElement(onode); + if (elm!=null) { + handleDrawElement(elm,hnodeBlock,hnodeInline,nMode); + } + } + else if (sName.equals(XMLString.DRAW_FRAME)) { + // OpenDocument embeds the draw element in a frame element + handleDrawElement(Misc.getFirstChildElement(onode),hnodeBlock,hnodeInline,nMode); + } + else if (sName.equals(XMLString.DRAW_G)) { + handleDrawGroup(onode,hnodeBlock,hnodeInline,nMode); + } + else if (sName.equals(XMLString.DRAW_CONTROL)) { + handleDrawControl(onode,hnodeBlock,hnodeInline,nMode); + } + } + + private void handleDrawObject(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) { + // TODO: Placement if not inline + // If possible, add the object inline. In pure block context, add a div. + Element hnode; + if (hnodeInline!=null) { + hnode = hnodeInline; + } + else { + Element div = converter.createElement("div"); + hnodeBlock.appendChild(div); + hnode = div; + } + + String sHref = Misc.getAttribute(onode, XMLString.XLINK_HREF); + if (sHref!=null) { // Embedded object in package or linked object + if (ofr.isInPackage(sHref)) { // Embedded object in package + if (sHref.startsWith("#")) { sHref=sHref.substring(1); } + if (sHref.startsWith("./")) { sHref=sHref.substring(2); } + EmbeddedObject object = converter.getEmbeddedObject(sHref); + if (MIMETypes.MATH.equals(object.getType()) || MIMETypes.ODF.equals(object.getType())) { // Formula! + EmbeddedXMLObject xmlObject = (EmbeddedXMLObject) object; + // Document settings = object.getSettingsDOM(); + try { + hnode.appendChild(converter.createTextNode(" ")); + getMathCv().convert(xmlObject.getContentDOM().getDocumentElement(),hnode); + hnode.appendChild(converter.createTextNode(" ")); + } + catch (SAXException e) { + e.printStackTrace(); + } + catch (IOException e) { + e.printStackTrace(); + } + } + else { // unsupported object + boolean bIgnore = true; + if (ofr.isOpenDocument()) { // look for replacement image + Element replacementImage = Misc.getChildByTagName(getFrame(onode),XMLString.DRAW_IMAGE); + if (replacementImage!=null) { + handleDrawImage(replacementImage,hnodeBlock,hnodeInline,nMode); + bIgnore = false; + } + } + if (bIgnore) { + hnode.appendChild( converter.createTextNode("[Warning: object ignored]")); + } + } + } + else { // TODO: Linked object + hnode.appendChild( converter.createTextNode("[Warning: Linked object ignored]")); + } + } + else { // flat xml format + Node formula = Misc.getChildByTagName(onode,XMLString.MATH_MATH); + if (formula != null) { + hnode.appendChild(converter.createTextNode(" ")); + getMathCv().convert(formula,hnode); + hnode.appendChild(converter.createTextNode(" ")); + } + else { // unsupported object + boolean bIgnore = true; + if (ofr.isOpenDocument()) { // look for replacement image + Element replacementImage = Misc.getChildByTagName(getFrame(onode),XMLString.DRAW_IMAGE); + if (replacementImage!=null) { + handleDrawImage(replacementImage,hnodeBlock,hnodeInline,nMode); + bIgnore = false; + } + } + if (bIgnore) { + hnode.appendChild( converter.createTextNode("[Warning: object ignored]")); + } + } + } + } + + private void handleDrawImage(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) { + // Get the image from the ImageLoader + String sFileName = null; + String sHref = Misc.getAttribute(onode,XMLString.XLINK_HREF); + if (sHref!=null && sHref.length()>0 && !ofr.isInPackage(sHref)) { + // Linked image is not yet handled by ImageLoader. This is a temp. + // solution (will go away when ImageLoader is finished) + sFileName = sHref; + // In OpenDocument *package* format ../ means "leave the package" + if (ofr.isOpenDocument() && ofr.isPackageFormat() && sFileName.startsWith("../")) { + sFileName=sFileName.substring(3); + } + //String sExt = sHref.substring(sHref.lastIndexOf(".")).toLowerCase(); + } + else { // embedded or base64 encoded image + BinaryGraphicsDocument bgd = converter.getImageLoader().getImage(onode); + if (bgd!=null) { + converter.addDocument(bgd); + sFileName = bgd.getFileName(); + } + } + + if (sFileName==null) { return; } // TODO: Add warning? + + // Create the image (sFileName contains the file name) + Element image = converter.createElement("img"); + String sName = Misc.getAttribute(getFrame(onode),XMLString.DRAW_NAME); + converter.addTarget(image,sName+"|graphic"); + image.setAttribute("src",sFileName); + + // Add alternative text, using either alt.text, name or file name + Element frame = getFrame(onode); + Element desc = Misc.getChildByTagName(frame,XMLString.SVG_DESC); + String sAltText = desc!=null ? Misc.getPCDATA(desc) : (sName!=null ? sName : sFileName); + image.setAttribute("alt",sAltText); + + // Now style it + StyleInfo info = new StyleInfo(); + String sStyleName = Misc.getAttribute(frame, XMLString.DRAW_STYLE_NAME); + getFrameSc().applyStyle(sStyleName,info); + if (!bOriginalImageSize) { applySize(frame,info.props,false); } + + // Apply placement + applyPlacement(frame, hnodeBlock, hnodeInline, nMode, image, info); + + applyStyle(info,image); + addLink(onode,image); + } + + private void handleDrawTextBox(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) { + // Create the div with id=name + Element textbox = converter.createElement("div"); + if (hnodeBlock!=null) { + hnodeBlock.appendChild(textbox); + } + else { // cannot include the div inline, ignore + return; + } + // Add name, if defined + String sName = Misc.getAttribute(getFrame(onode),XMLString.DRAW_NAME); + if (sName!=null) { converter.addTarget(textbox,sName+"|frame"); } + + // Now style it + Element frame = getFrame(onode); + StyleInfo info = new StyleInfo(); + // Draw frame style + String sStyleName = Misc.getAttribute(frame, XMLString.DRAW_STYLE_NAME); + if (sStyleName!=null) { + getFrameSc().applyStyle(sStyleName,info); + } + // Presentation frame style + sStyleName = Misc.getAttribute(frame, XMLString.PRESENTATION_STYLE_NAME); + if (sStyleName!=null) { + if ("outline".equals(Misc.getAttribute(frame, XMLString.PRESENTATION_CLASS))) { + getPresentationSc().enterOutline(sStyleName); + } + getPresentationSc().applyStyle(sStyleName,info); + } + // Additional text formatting + sStyleName = Misc.getAttribute(frame, XMLString.DRAW_TEXT_STYLE_NAME); + if (sStyleName!=null) { + //getStyleCv().applyParStyle(sStyleName,info); + } + + // Apply placement + switch (nMode) { + case INLINE: + break; + case ABSOLUTE: + applySize(frame,info.props,false); + applyPosition(frame,info.props); + break; + case CENTERED: + info.props.addValue("maring-top","2px"); + info.props.addValue("maring-bottom","2px"); + info.props.addValue("margin-left","auto"); + info.props.addValue("margin-right","auto"); + applySize(frame,info.props,true); + break; + case FLOATING: + applySize(frame,info.props,true); + StyleWithProperties style = ofr.getFrameStyle(sStyleName); + if (style!=null) { + String sPos = style.getProperty(XMLString.STYLE_HORIZONTAL_POS); + String sWrap = style.getProperty(XMLString.STYLE_WRAP); + if (isLeft(sPos) && mayWrapRight(sWrap)) { + info.props.addValue("float","left"); + } + else if (isRight(sPos) && mayWrapLeft(sWrap)) { + info.props.addValue("float","right"); + } + else if (isFromLeft(sPos)) { + if (mayWrapRight(sWrap)) { + info.props.addValue("float","left"); + } + String sX = frame.getAttribute(XMLString.SVG_X); + if (sX!=null && sX.length()>0) { + info.props.addValue("margin-left",scale(sX)); + } + } + } + } + + //Finish + applyStyle(info,textbox); + getTextCv().traverseBlockText(onode,textbox); + getPresentationSc().exitOutline(); + } + + private void handleDrawGroup(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) { + // TODO: style-name and z-index should be transferred to children + Node child = onode.getFirstChild(); + while (child!=null) { + if (OfficeReader.isDrawElement(child)) { + handleDrawElement((Element) child, hnodeBlock, hnodeInline, nMode); + } + child = child.getNextSibling(); + } + } + + ////////////////////////////////////////////////////////////////////////// + // Forms + + private void handleDrawControl(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) { + // Get the control, if possible + if (form==null) { return; } + ControlReader control = ofr.isOpenDocument() ? + ofr.getForms().getControl(Misc.getAttribute(onode,XMLString.DRAW_CONTROL)) : + ofr.getForms().getControl(Misc.getAttribute(onode,XMLString.FORM_ID)); + if (control==null || control.getOwnerForm()!=form) { return; } + + // Create the control element + Element hcontrol = null; + String sType = control.getControlType(); + + if (XMLString.FORM_TEXT.equals(sType)) { + hcontrol = createInputText(control,false); + } + else if (XMLString.FORM_PASSWORD.equals(sType)) { + hcontrol = createInputText(control,true); + } + else if (XMLString.FORM_FILE.equals(sType)) { + hcontrol = createInputFile(control); + } + else if (XMLString.FORM_IMAGE.equals(sType)) { + hcontrol = createInput(control,"image"); + } + else if (XMLString.FORM_HIDDEN.equals(sType)) { + hcontrol = createInput(control,"hidden"); + } + else if (XMLString.FORM_CHECKBOX.equals(sType)) { + hcontrol = createInputCheck(control,false); + } + else if (XMLString.FORM_RADIO.equals(sType)) { + hcontrol = createInputCheck(control,true); + } + else if (XMLString.FORM_BUTTON.equals(sType)) { + hcontrol = createInputButton(control); + } + else if (XMLString.FORM_FIXED_TEXT.equals(sType)) { + hcontrol = createLabel(control); + } + else if (XMLString.FORM_TEXTAREA.equals(sType)) { + hcontrol = createTextarea(control); + } + else if (XMLString.FORM_LISTBOX.equals(sType)) { + hcontrol = createSelect(control); + } + // ignore other controls + + if (hcontrol!=null) { + Element frame = onode; // controls are *not* contained in a draw:frame! + StyleInfo info = new StyleInfo(); + getFrameSc().applyStyle(frame.getAttribute(XMLString.DRAW_STYLE_NAME),info); + applySize(frame,info.props,false); + applyPlacement(frame,hnodeBlock,hnodeInline,nMode,hcontrol,info); + applyStyle(info,hcontrol); + } + + } + + private Element createInput(ControlReader control, String sType) { + // Create the element + Element input = converter.createElement("input"); + input.setAttribute("type",sType); + return input; + } + + private Element createInputFile(ControlReader control) { + Element input = converter.createElement("input"); + input.setAttribute("type","file"); + setCommonAttributes(control,input); + setDisabled(control,input); + setReadonly(control,input); + setValue(control,input); + return input; + } + + private Element createInputText(ControlReader control, boolean bPassword) { + Element input = converter.createElement("input"); + input.setAttribute("type",bPassword ? "password" : "text"); + setCommonAttributes(control,input); + setName(control,input,true); + setValue(control,input); + setMaxLength(control,input); + setDisabled(control,input); + setReadonly(control,input); + return input; + } + + private Element createInputCheck(ControlReader control, boolean bRadio) { + Element input = converter.createElement("input"); + input.setAttribute("type",bRadio ? "radio" : "checkbox"); + setCommonAttributes(control,input); + setName(control,input,true); + setValue(control,input); + setChecked(control,input); + setDisabled(control,input); + setReadonly(control,input); + // Add a label for the check/radio + Element label = converter.createElement("label"); + setFor(control,label); + label.appendChild(input); + label.appendChild(converter.createTextNode(control.getTypeAttribute(XMLString.FORM_LABEL))); + return label; + } + + private Element createInputButton(ControlReader control) { + Element input = converter.createElement("input"); + String sButtonType = control.getTypeAttribute(XMLString.FORM_BUTTON_TYPE); + if ("submit".equals(sButtonType)) { + input.setAttribute("type","submit"); + } + else if ("reset".equals(sButtonType)) { + input.setAttribute("type","reset"); + } + else { // TODO: url button (using javascript) + input.setAttribute("type","button"); + } + setCommonAttributes(control,input); + setName(control,input,true); + input.setAttribute("value",control.getTypeAttribute(XMLString.FORM_LABEL)); + setDisabled(control,input); + return input; + } + + private Element createLabel(ControlReader control) { + Element label = converter.createElement("label"); + setCommonAttributes(control,label); + setFor(control,label); + label.setAttribute("value",control.getTypeAttribute(XMLString.FORM_LABEL)); + label.appendChild(converter.createTextNode(control.getTypeAttribute(XMLString.FORM_LABEL))); + return label; + } + + private Element createTextarea(ControlReader control) { + Element textarea = converter.createElement("textarea"); + setCommonAttributes(control,textarea); + setName(control,textarea,true); + setDisabled(control,textarea); + setReadonly(control,textarea); + // rows & cols are required - but css will override them! + textarea.setAttribute("rows","10"); + textarea.setAttribute("cols","5"); + // The value attribute should be used as content + String s = control.getTypeAttribute(XMLString.FORM_VALUE); + if (s!=null) { + textarea.appendChild(converter.createTextNode(s)); + } + return textarea; + } + + private Element createSelect(ControlReader control) { + Element select = converter.createElement("select"); + setCommonAttributes(control,select); + setName(control,select,false); + setSize(control,select); + setMultiple(control,select); + setDisabled(control,select); + // Add options + int nCount = control.getItemCount(); + for (int i=0; i<nCount; i++) { + String sLabel = control.getItemAttribute(i,XMLString.FORM_LABEL); + boolean bSelected = "true".equals(control.getItemAttribute(i,XMLString.FORM_SELECTED)); + Element option = converter.createElement("option"); + select.appendChild(option); + if (bSelected) { option.setAttribute("selected","selected"); } + option.appendChild(converter.createTextNode(sLabel)); + } + return select; + } + + // form helpers + + private void setCommonAttributes(ControlReader control, Element hnode) { + setId(control,hnode); + setTitle(control,hnode); + setTabIndex(control,hnode); + } + + private void setId(ControlReader control, Element hnode) { + String s = control.getId(); + if (s!=null) { hnode.setAttribute("id",s); } + } + + private void setName(ControlReader control, Element hnode, boolean bRequired) { + String s = control.getAttribute(XMLString.FORM_NAME); + if (s!=null) { hnode.setAttribute("name",s); } + else if (bRequired) { hnode.setAttribute("name","unknown"); } + } + + private void setValue(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_VALUE); // TODO: Correct?? + if (s!=null) { hnode.setAttribute("value",s); } + } + + private void setTitle(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_TITLE); + if (s!=null) { hnode.setAttribute("title",s); } + } + + private void setTabIndex(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_TAB_INDEX); + if (s!=null) { hnode.setAttribute("tabindex",s); } + } + + private void setMaxLength(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_MAX_LENGTH); + if (s!=null) { hnode.setAttribute("maxlength",s); } + } + + private void setSize(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_SIZE); + if (s!=null) { hnode.setAttribute("size",s); } + } + + private void setChecked(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_SELECTED); + if ("true".equals(s)) { hnode.setAttribute("checked","checked"); } + } + + private void setMultiple(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_MULTIPLE); + if ("true".equals(s)) { hnode.setAttribute("multiple","multiple"); } + } + + private void setDisabled(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_DISABLED); + if ("true".equals(s)) { hnode.setAttribute("disabled","disabled"); } + } + + private void setReadonly(ControlReader control, Element hnode) { + String s = control.getTypeAttribute(XMLString.FORM_READONLY); + if ("true".equals(s)) { hnode.setAttribute("readonly","readonly"); } + } + + private void setFor(ControlReader control, Element hnode) { + hnode.setAttribute("for",control.getId()); + } + + /////////////////////////////////////////////////////////////////////////// + // Utility methods + + // Add link to a draw element + private void addLink(Element onode, Element hnode) { + Element oanchor = getDrawAnchor(onode); + if (oanchor!=null) { + // If xlink:href is empty, there's no point in creating the anchor: + String sHref = oanchor.getAttribute(XMLString.XLINK_HREF); + if (sHref!=null && sHref.length()>0) { + Element hanchor = converter.createLink(oanchor); + hnode.getParentNode().replaceChild(hanchor,hnode); + hanchor.appendChild(hnode); + } + } + } + + private void applySize(Element node, CSVList props, boolean bOnlyWidth) { + // The width attribute in css refers to the content width, excluding borders and padding + // We thus have to subtract the borders and padding to get the correct width + StyleWithProperties style = ofr.getFrameStyle(node.getAttribute(XMLString.DRAW_STYLE_NAME)); + + String sWidth = Misc.getAttribute(node,XMLString.SVG_WIDTH); + if (sWidth.length()>0) { + if (style!=null) { + // Subtract padding + String s = style.getProperty(XMLString.FO_PADDING_LEFT); + if (s!=null) sWidth = Misc.sub(sWidth, s); + s = style.getProperty(XMLString.FO_PADDING_RIGHT); + if (s!=null) sWidth = Misc.sub(sWidth, s); + s = style.getProperty(XMLString.FO_PADDING); + if (s!=null) sWidth = Misc.sub(sWidth, Misc.multiply("200%", s)); + // Subtract border + s = style.getProperty(XMLString.FO_BORDER_LEFT); + if (s!=null) sWidth = Misc.sub(sWidth, getTableCv().borderWidth(s)); + s = style.getProperty(XMLString.FO_BORDER_RIGHT); + if (s!=null) sWidth = Misc.sub(sWidth, getTableCv().borderWidth(s)); + s = style.getProperty(XMLString.FO_BORDER); + if (s!=null) sWidth = Misc.sub(sWidth, Misc.multiply("200%", getTableCv().borderWidth(s))); + } + + props.addValue("width",scale(sWidth)); + } + + String sHeight = Misc.getAttribute(node,XMLString.SVG_HEIGHT); + if (sHeight.length()>0 && !bOnlyWidth) { + if (style!=null) { + // Subtract padding + String s = style.getProperty(XMLString.FO_PADDING_TOP); + if (s!=null) sHeight = Misc.sub(sHeight, s); + s = style.getProperty(XMLString.FO_PADDING_BOTTOM); + if (s!=null) sHeight = Misc.sub(sHeight, s); + s = style.getProperty(XMLString.FO_PADDING); + if (s!=null) sHeight = Misc.sub(sHeight, Misc.multiply("200%", s)); + // Subtract border + s = style.getProperty(XMLString.FO_BORDER_TOP); + if (s!=null) sHeight = Misc.sub(sHeight, getTableCv().borderWidth(s)); + s = style.getProperty(XMLString.FO_BORDER_BOTTOM); + if (s!=null) sHeight = Misc.sub(sHeight, getTableCv().borderWidth(s)); + s = style.getProperty(XMLString.FO_BORDER); + if (s!=null) sHeight = Misc.sub(sHeight, Misc.multiply("200%", getTableCv().borderWidth(s))); + } + + props.addValue("height",scale(sHeight)); + } + } + + private void applyPosition(Element node, CSVList props) { + // The left and top attributes in css refers to the entire box, including margins + // We thus have to subtract the margins to get correct placement + String sX = node.getAttribute(XMLString.SVG_X); + if (sX.length()==0) sX="0"; + String sY = node.getAttribute(XMLString.SVG_Y); + if (sY.length()==0) sY="0"; + StyleWithProperties style = ofr.getFrameStyle(node.getAttribute(XMLString.DRAW_STYLE_NAME)); + if (style!=null) { + String s = style.getProperty(XMLString.FO_MARGIN_TOP); + if (s!=null) sX=Misc.sub(sX,s); + s = style.getProperty(XMLString.FO_MARGIN_LEFT); + if (s!=null) sY=Misc.sub(sY,s); + } + + props.addValue("position","absolute"); + if (sX!=null && sX.length()>0) { props.addValue("left",scale(sX)); } + if (sY!=null && sY.length()>0) { props.addValue("top",scale(sY)); } + + } + + private void applyPlacement(Element onode, Element hnodeBlock, + Element hnodeInline, int nMode, Element object, StyleInfo info) { + switch (nMode) { + case INLINE : + hnodeInline.appendChild(object); + break; + case ABSOLUTE: + applyPosition(onode,info.props); + if (hnodeInline!=null) { + hnodeInline.appendChild(object); + } + else { + Element div = converter.createElement("div"); + hnodeBlock.appendChild(div); + div.appendChild(object); + } + break; + case CENTERED: + Element centerdiv = converter.createElement("div"); + centerdiv.setAttribute("style","margin:2px 0px 2px 0px"); + hnodeBlock.appendChild(centerdiv); + centerdiv.appendChild(object); + break; + case FLOATING: + boolean bWrap = false; + String sAlign = "center"; + String sX = null; + String sStyleName = Misc.getAttribute(onode, XMLString.DRAW_STYLE_NAME); + StyleWithProperties style = ofr.getFrameStyle(sStyleName); + if (style!=null) { + String sPos = style.getProperty(XMLString.STYLE_HORIZONTAL_POS); + String sWrap = style.getProperty(XMLString.STYLE_WRAP); + if (isLeft(sPos)) { + bWrap = mayWrapRight(sWrap); + sAlign = "left"; + } + else if (isRight(sPos)) { + bWrap = mayWrapLeft(sWrap); + sAlign = "right"; + } + else if (isFromLeft(sPos)) { + bWrap = mayWrapRight(sWrap); + sAlign = "left"; + sX = onode.getAttribute(XMLString.SVG_X); + } + } + + if (bWrap) { + info.props.addValue("float",sAlign); + if (sX!=null && sX.length()>0) { info.props.addValue("margin-left",sX); } + if (hnodeInline!=null) { + hnodeInline.appendChild(object); + } + else { + Element div = converter.createElement("div"); + hnodeBlock.appendChild(div); + div.appendChild(object); + } + } + else { + Element div = converter.createElement("div"); + hnodeBlock.appendChild(div); + div.appendChild(object); + CSVList props = new CSVList(";"); + props.addValue("text-align",sAlign); + if (sX!=null && sX.length()>0) { props.addValue("margin-left",sX); } + div.setAttribute("style",props.toString()); + } + } + + } + + private boolean isLeft(String sHPos) { + return "left".equals(sHPos) || "inside".equals(sHPos); + } + + /*private boolean isCenter(String sHPos) { + return "center".equals(sHPos); + }*/ + + private boolean isRight(String sHPos) { + return "right".equals(sHPos) || "outside".equals(sHPos); + } + + private boolean isFromLeft(String sHPos) { + return "from-left".equals(sHPos) || "from-inside".equals(sHPos); + } + + private boolean mayWrapLeft(String sWrap) { + return "left".equals(sWrap) || "parallel".equals(sWrap) || + "dynamic".equals(sWrap) || "run-through".equals(sWrap); + } + + private boolean mayWrapRight(String sWrap) { + return "right".equals(sWrap) || "parallel".equals(sWrap) || + "dynamic".equals(sWrap) || "run-through".equals(sWrap); + } + + // TODO: Move to ConverterHelper.java + private String scale(String s) { + if (bConvertToPx) { + return Misc.length2px(Misc.multiply(sScale,Misc.truncateLength(s))); + } + else { + return Misc.multiply(sScale,Misc.truncateLength(s)); + } + } + +} + + diff --git a/source/java/writer2latex/xhtml/FrameStyleConverter.java b/source/java/writer2latex/xhtml/FrameStyleConverter.java new file mode 100644 index 0000000..249cafd --- /dev/null +++ b/source/java/writer2latex/xhtml/FrameStyleConverter.java @@ -0,0 +1,299 @@ +/************************************************************************ + * + * FrameStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +import java.util.Enumeration; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; +//import writer2latex.util.Misc; +import writer2latex.util.SimpleInputBuffer; + +/** + * This class converts OpenDocument graphic (frame) styles to CSS2 styles. + * This includes conversion of frame properties in other styles (paragraph, + * cell, section, page and presentation styles). + */ +public class FrameStyleConverter extends StyleWithPropertiesConverterHelper { + + /** Create a new <code>FrameStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public FrameStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + this.styleMap = config.getXFrameStyleMap(); + this.bConvertStyles = config.xhtmlFrameFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFrameFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlFrameFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFrameFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + /** Convert style information for used styles + * @param sIndent a String of spaces to add before each line + */ + public String getStyleDeclarations(String sIndent) { + if (bConvertStyles) { + StringBuffer buf = new StringBuffer(); + buf.append(super.getStyleDeclarations(sIndent)); + Enumeration names = styleNames.keys(); + while (names.hasMoreElements()) { + String sDisplayName = (String) names.nextElement(); + StyleWithProperties style = (StyleWithProperties) + getStyles().getStyleByDisplayName(sDisplayName); + if (!style.isAutomatic()) { + // Apply style to paragraphs contained in this frame + CSVList props = new CSVList(";"); + getFrameSc().cssMargins(style,props,true); + getParSc().cssPar(style,props,true); + getTextSc().cssTextCommon(style,props,true); + if (!props.isEmpty()) { + buf.append(sIndent) + .append(getDefaultTagName(null)) + .append(".").append(getClassNamePrefix()) + .append(styleNames.getExportName(sDisplayName)) + .append(" p {").append(props.toString()).append("}\n"); + } + } + } + return buf.toString(); + } + else { + return ""; + } + } + + /** Return a prefix to be used in generated css class names + * @return the prefix + */ + public String getClassNamePrefix() { return "frame"; } + + /** Get the family of frame styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getFrameStyles(); + } + + /** Create default tag name to represent a frame + * @param style to use + * @return the tag name. + */ + public String getDefaultTagName(StyleWithProperties style) { + return ""; + } + + /** Convert formatting properties for a specific frame style. + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit) { + cssBox(style,props,bInherit); + getTextSc().cssTextCommon(style,props,bInherit); // only in presentations + } + + //////////////////////////////////////////////////////////////////////////// + // OpenDocument frame properties + + public void cssBox(StyleWithProperties style, CSVList props, boolean bInherit){ + // translates "box" style properties. + // these can be applied to paragraph styles, frame styles and page styles. + // The following properties are not supported by CSS2: + // style:border-line-width and style:border-line-width-* + // TODO: What about shadow? + cssMargins(style,props,bInherit); + cssBorder(style,props,bInherit); + cssPadding(style,props,bInherit); + cssBackground(style,props,bInherit); + } + + public void cssMargins(StyleWithProperties style, CSVList props, boolean bInherit){ + // *Absolute* values fit with css + String s; + if (bInherit || style.getProperty(XMLString.FO_MARGIN_LEFT,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_MARGIN_LEFT); + if (s!=null) { props.addValue("margin-left",scale(s)); } + else { props.addValue("margin-left","0"); } + } + if (bInherit || style.getProperty(XMLString.FO_MARGIN_RIGHT,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_MARGIN_RIGHT); + if (s!=null) { props.addValue("margin-right",scale(s)); } + else { props.addValue("margin-right","0"); } + } + if (bInherit || style.getProperty(XMLString.FO_MARGIN_TOP,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_MARGIN_TOP); + if (s!=null) { props.addValue("margin-top",scale(s)); } + else { props.addValue("margin-top","0"); } + } + if (bInherit || style.getProperty(XMLString.FO_MARGIN_BOTTOM,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_MARGIN_BOTTOM); + if (s!=null) { props.addValue("margin-bottom",scale(s)); } + else { props.addValue("margin-bottom","0"); } + } + } + + public void cssBorder(StyleWithProperties style, CSVList props, boolean bInherit){ + // Same as in css + boolean bHasBorder = false; + String s=null; + if (bInherit || style.getProperty(XMLString.FO_BORDER,false)!=null) { + s = style.getProperty(XMLString.FO_BORDER); + } + if (s!=null) { + props.addValue("border",borderScale(s)); bHasBorder = true; + } + else { // apply individual borders + if (bInherit || style.getProperty(XMLString.FO_BORDER_TOP,false)!=null) { + s = style.getProperty(XMLString.FO_BORDER_TOP); + if (s!=null) { props.addValue("border-top",borderScale(s)); bHasBorder=true; } + } + if (bInherit || style.getProperty(XMLString.FO_BORDER_BOTTOM,false)!=null) { + s = style.getProperty(XMLString.FO_BORDER_BOTTOM); + if (s!=null) { props.addValue("border-bottom",borderScale(s)); bHasBorder=true; } + } + if (bInherit || style.getProperty(XMLString.FO_BORDER_LEFT,false)!=null) { + s = style.getProperty(XMLString.FO_BORDER_LEFT); + if (s!=null) { props.addValue("border-left",borderScale(s)); bHasBorder=true; } + } + if (bInherit || style.getProperty(XMLString.FO_BORDER_RIGHT,false)!=null) { + s = style.getProperty(XMLString.FO_BORDER_RIGHT); + if (s!=null) { props.addValue("border-right",borderScale(s)); bHasBorder=true; } + } + } + // Default to no border: + if (bInherit && !bHasBorder) { props.addValue("border","none"); } + } + + public void cssPadding(StyleWithProperties style, CSVList props, boolean bInherit){ + // *Absolute* values fit with css + String s=null; + if (bInherit || style.getProperty(XMLString.FO_PADDING,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_PADDING); + } + if (s!=null) { + props.addValue("padding",scale(s)); + } + else { // apply individual paddings + boolean bTop = false; + boolean bBottom = false; + boolean bLeft = false; + boolean bRight = false; + if (bInherit || style.getProperty(XMLString.FO_PADDING_TOP,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_PADDING_TOP); + if (s!=null) { props.addValue("padding-top",scale(s)); bTop=true; } + } + if (bInherit || style.getProperty(XMLString.FO_PADDING_BOTTOM,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_PADDING_BOTTOM); + if (s!=null) { props.addValue("padding-bottom",scale(s)); bBottom=true; } + } + if (bInherit || style.getProperty(XMLString.FO_PADDING_LEFT,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_PADDING_LEFT); + if (s!=null) { props.addValue("padding-left",scale(s)); bLeft=true; } + } + if (bInherit || style.getProperty(XMLString.FO_PADDING_RIGHT,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_PADDING_RIGHT); + if (s!=null) { props.addValue("padding-right",scale(s)); bRight=true; } + } + if (bInherit) { // must specify padding + if (!bTop && !bBottom && !bLeft && !bRight) { + props.addValue("padding","0"); + } + else { + if (!bTop) { props.addValue("padding-top","0"); } + if (!bBottom) { props.addValue("padding-bottom","0"); } + if (!bLeft) { props.addValue("padding-left","0"); } + if (!bRight) { props.addValue("padding-right","0"); } + } + } + } + } + + // parapgrah styles need this for special treatment of background color + public void cssBackgroundCommon(StyleWithProperties style, CSVList props, boolean bInherit){ + // Background image: + String sUrl = style.getBackgroundImageProperty(XMLString.XLINK_HREF); + if (sUrl!=null) { // currently only support for linked image + props.addValue("background-image","url("+escapeUrl(sUrl)+")"); + + String sRepeat = style.getBackgroundImageProperty(XMLString.STYLE_REPEAT); + if ("no-repeat".equals(sRepeat) || "stretch".equals(sRepeat)) { + props.addValue("background-repeat","no-repeat"); + } + else { + props.addValue("background-repeat","repeat"); + } + + String sPosition = style.getBackgroundImageProperty(XMLString.STYLE_POSITION); + if (sPosition!=null) { props.addValue("background-position",sPosition); } + } + } + + public void cssBackground(StyleWithProperties style, CSVList props, boolean bInherit){ + // Background color: Same as in css + String s = style.getProperty(XMLString.FO_BACKGROUND_COLOR,bInherit); + if (s!=null) { props.addValue("background-color",s); } + cssBackgroundCommon(style,props,bInherit); + } + + // Scale the border with while preserving the rest of the attribute + public String borderScale(String sBorder) { + SimpleInputBuffer in = new SimpleInputBuffer(sBorder); + StringBuffer out = new StringBuffer(); + while (in.peekChar()!='\0') { + // Skip spaces + while(in.peekChar()==' ') { out.append(" "); in.getChar(); } + // If it's a number it must be a unit -> convert it + if ('0'<=in.peekChar() && in.peekChar()<='9') { + out.append(scale(in.getNumber()+in.getIdentifier())); + } + // skip other characters + while (in.peekChar()!=' ' && in.peekChar()!='\0') { + out.append(in.getChar()); + } + } + return out.toString(); + } + + // Must escape certain characters in the url property + private String escapeUrl(String sUrl) { + StringBuffer buf = new StringBuffer(); + int nLen = sUrl.length(); + for (int i=0; i<nLen; i++) { + char c = sUrl.charAt(i); + if (c=='\'' || c=='"' || c=='(' || c==')' || c==',' || c==' ') { + buf.append("\\"); + } + buf.append(c); + } + return buf.toString(); + } + +} diff --git a/source/java/writer2latex/xhtml/L10n.java b/source/java/writer2latex/xhtml/L10n.java new file mode 100644 index 0000000..964c157 --- /dev/null +++ b/source/java/writer2latex/xhtml/L10n.java @@ -0,0 +1,154 @@ +/************************************************************************ + * + * L10n.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-12-16) + * + */ + +package writer2latex.xhtml; + +// This class handles localized strings (used for navigation) +public class L10n { + public final static int UP = 0; + public final static int FIRST = 1; + public final static int PREVIOUS = 2; + public final static int NEXT = 3; + public final static int LAST = 4; + public final static int CONTENTS = 5; + public final static int INDEX = 6; + public final static int HOME = 7; + public final static int DIRECTORY = 8; + public final static int DOCUMENT = 9; + + private String sLocale="en-US"; + + public void setLocale(String sLocale) { + if (sLocale!=null) { this.sLocale = sLocale;} + } + + public void setLocale(String sLanguage, String sCountry) { + if (sLanguage!=null) { + if (sCountry!=null) { sLocale = sLanguage + "-" + sCountry; } + else { sLocale = sLanguage; } + } + } + + public String get(int nString) { + if (sLocale.startsWith("de")) { // german + switch (nString) { + case UP: return "Nach oben"; + case FIRST : return "Anfang"; + case PREVIOUS : return "Vorheriges"; + case NEXT : return "N\u00ccchstes"; + case LAST : return "Ende"; + case CONTENTS : return "Inhalte"; + case INDEX : return "Index"; + case HOME : return "Home"; + case DIRECTORY: return "Verzeichnis"; + case DOCUMENT: return "Dokument"; + } + } + if (sLocale.startsWith("fr")) { // french + switch (nString) { + case UP: return "Haut"; + case FIRST : return "D\u00e9but"; + case PREVIOUS : return "Pr\u00e9c\u00e9dent"; + case NEXT : return "Suivant"; + case LAST : return "Dernier"; + case CONTENTS : return "Contenus"; + case INDEX : return "Index"; + case HOME : return "Documents Personnels"; + case DIRECTORY: return "R\u00e9pertoire"; + case DOCUMENT: return "Document"; + } + } + if (sLocale.startsWith("da")) { // danish + switch (nString) { + case UP: return "Op"; + case FIRST : return "F\u00F8rste"; + case PREVIOUS : return "Forrige"; + case NEXT : return "N\u00E6ste"; + case LAST : return "Sidste"; + case CONTENTS : return "Indhold"; + case INDEX : return "Stikord"; + case HOME : return "Hjem"; + case DIRECTORY: return "Mappe"; + case DOCUMENT: return "Dokument"; + } + } + if (sLocale.startsWith("ru")) { // russian + switch (nString) { + case UP: return "\u0412\u0432\u0435\u0440\u0445"; + case FIRST : return "\u041f\u0435\u0440\u0432\u0430\u044f"; + case PREVIOUS : return "\u041f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0430\u044f"; + case NEXT : return "\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0430\u044f"; + case LAST : return "\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f"; + case CONTENTS : return "\u0421\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435"; + case INDEX : return "\u0421\u043f\u0438\u0441\u043e\u043a"; + case HOME : return "\u0414\u043e\u043c\u043e\u0439"; + case DIRECTORY: return "\u0414\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044f"; + case DOCUMENT: return "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442"; + } + } + if (sLocale.startsWith("uk")) { // ukrainian + switch (nString) { + case UP: return "\u041d\u0430\u0433\u043e\u0440\u0443"; + case FIRST : return "\u041f\u0435\u0440\u0448\u0430"; + case PREVIOUS : return "\u041f\u043e\u043f\u0435\u0440\u0435\u0434\u043d\u044f"; + case NEXT : return "\u041d\u0430\u0441\u0442\u0443\u043f\u043d\u0430"; + case LAST : return "\u041e\u0441\u0442\u0430\u043d\u043d\u044f"; + case CONTENTS : return "\u0417\u043c\u0456\u0441\u0442"; + case INDEX : return "\u0421\u043f\u0438\u0441\u043e\u043a"; + case HOME : return "\u0414\u043e\u0434\u043e\u043c\u0443"; + case DIRECTORY: return "\u0422\u0435\u043a\u0430"; + case DOCUMENT: return "\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442"; + } + } + if (sLocale.startsWith("hr")) { // croatian + switch (nString) { + case UP: return "Up"; + case FIRST : return "Prvi"; + case PREVIOUS : return "Prethodan"; + case NEXT : return "slijede\u0107i"; + case LAST : return "Zadnji"; + case CONTENTS : return "Sadr\u017Eaj"; + case INDEX : return "Indeks"; + case DIRECTORY: return "Directory"; + case DOCUMENT: return "Document"; + } + } + // english - default + switch (nString) { + case UP: return "Up"; + case FIRST : return "First"; + case PREVIOUS : return "Previous"; + case NEXT : return "Next"; + case LAST: return "Last"; + case CONTENTS : return "Contents"; + case INDEX : return "Index"; + case HOME : return "Home"; + case DIRECTORY: return "Directory"; + case DOCUMENT: return "Document"; + } + return "???"; + } +} diff --git a/source/java/writer2latex/xhtml/LinkDescriptor.java b/source/java/writer2latex/xhtml/LinkDescriptor.java new file mode 100644 index 0000000..26e3ffc --- /dev/null +++ b/source/java/writer2latex/xhtml/LinkDescriptor.java @@ -0,0 +1,40 @@ +/************************************************************************ + * + * LinkDescriptor.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-07-27) + * + */ + +package writer2latex.xhtml; + +import org.w3c.dom.Element; + +/** + * Helper class (a struct) to contain information about a Link (used to manage + * links to be resolved later) + */ +final class LinkDescriptor { + Element element; // the a-element + String sId; // the id to link to + int nIndex; // the index of *this* file +} + diff --git a/source/java/writer2latex/xhtml/ListStyleConverter.java b/source/java/writer2latex/xhtml/ListStyleConverter.java new file mode 100644 index 0000000..ff40aee --- /dev/null +++ b/source/java/writer2latex/xhtml/ListStyleConverter.java @@ -0,0 +1,157 @@ +/************************************************************************ + * + * ListStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +import java.util.Enumeration; +//import java.util.Hashtable; + +import writer2latex.office.ListStyle; +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; + +/** + * This class converts OpenDocument list styles to + * CSS2 styles (currently, actually CSS1). + */ +public class ListStyleConverter extends StyleConverterHelper { + + /** Create a new <code>ListStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public ListStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + this.styleMap = config.getXListStyleMap(); + this.bConvertStyles = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + public void applyStyle(int nLevel, String sStyleName, StyleInfo info) { + ListStyle style = ofr.getListStyle(sStyleName); + if (style!=null) { + if (style.isAutomatic()) { + applyStyle(nLevel,style.getParentName(),info); + if (bConvertHard) { cssList(style,nLevel,info.props); } + } + else { + String sDisplayName = style.getDisplayName(); + if (styleMap.contains(sDisplayName)) { + info.sTagName = styleMap.getElement(sDisplayName); + if (!"(none)".equals(styleMap.getCss(sDisplayName))) { + info.sClass = styleMap.getCss(sDisplayName); + } + } + else { + info.sClass = "listlevel"+Integer.toString(nLevel) + +styleNames.getExportName(sDisplayName); + } + } + } + } + + + + /** <p>Convert style information for used styles</p> + * @param sIndent a String of spaces to add before each line + */ + public String getStyleDeclarations(String sIndent) { + if (bConvertStyles) { + StringBuffer buf = new StringBuffer(); + Enumeration names = styleNames.keys(); + while (names.hasMoreElements()) { + String sDisplayName = (String) names.nextElement(); + ListStyle style = (ListStyle) + getStyles().getStyleByDisplayName(sDisplayName); + if (!style.isAutomatic()) { + for (int nLevel=1; nLevel<10; nLevel++) { + CSVList props = new CSVList(";"); + cssList(style,nLevel,props); + buf.append(sIndent); + buf.append(".listlevel"); + buf.append(nLevel); + buf.append(styleNames.getExportName(sDisplayName)); + buf.append(" {"); + buf.append(props.toString()); + buf.append("}\n"); + } + } + } + return buf.toString(); + } + else { + return ""; + } + } + + /** Get the family of list styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getListStyles(); + } + + private void cssList(ListStyle style, int nLevel, CSVList props){ + // translates "list" style properties for a particular level + // Mozilla does not seem to support the "marker" mechanism of CSS2 + // so we will stick with the simpler CSS1-like list style properties + props.addValue("margin-top","0"); + props.addValue("margin-bottom","0"); + String sLevelType = style.getLevelType(nLevel); + if (XMLString.TEXT_LIST_LEVEL_STYLE_NUMBER.equals(sLevelType)) { + // Numbering style, get number format + String sNumFormat = style.getLevelProperty(nLevel,XMLString.STYLE_NUM_FORMAT); + if ("1".equals(sNumFormat)) { props.addValue("list-style-type","decimal"); } + else if ("i".equals(sNumFormat)) { props.addValue("list-style-type","lower-roman"); } + else if ("I".equals(sNumFormat)) { props.addValue("list-style-type","upper-roman"); } + else if ("a".equals(sNumFormat)) { props.addValue("list-style-type","lower-alpha"); } + else if ("A".equals(sNumFormat)) { props.addValue("list-style-type","upper-alpha"); } + } + else if (XMLString.TEXT_LIST_LEVEL_STYLE_BULLET.equals(sLevelType)) { + // Bullet. We can only choose from disc, bullet and square + switch (nLevel % 3) { + case 1: props.addValue("list-style-type","disc"); break; + case 2: props.addValue("list-style-type","bullet"); break; + case 0: props.addValue("list-style-type","square"); break; + } + } + else if (XMLString.TEXT_LIST_LEVEL_STYLE_IMAGE.equals(sLevelType)) { + // Image. TODO: Handle embedded images + String sHref = style.getLevelProperty(nLevel,XMLString.XLINK_HREF); + if (sHref!=null) { props.addValue("list-style-image","url('"+sHref+"')"); } + } + // We don't want floats to pass a list to the left (Mozilla and IE both + //handles this terribly!) + props.addValue("clear:left"); + } + + + +} diff --git a/source/java/writer2latex/xhtml/MathConverter.java b/source/java/writer2latex/xhtml/MathConverter.java new file mode 100644 index 0000000..6c5f29a --- /dev/null +++ b/source/java/writer2latex/xhtml/MathConverter.java @@ -0,0 +1,238 @@ +/************************************************************************ + * + * MathConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-19) + * + */ + +package writer2latex.xhtml; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; + +import writer2latex.office.*; + +public class MathConverter extends ConverterHelper { + + private boolean bSupportMathML; + + public MathConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, + boolean bSupportMathML) { + + super(ofr,config,converter); + this.bSupportMathML = bSupportMathML; + } + + public void convert(Node onode, Node hnode) { + if (bSupportMathML) { + convertNode(onode,hnode); + } + else { + Document htmlDOM = hnode.getOwnerDocument(); + NodeList annotationList + = ((Element) onode).getElementsByTagName(XMLString.MATH_ANNOTATION); + if (annotationList.getLength()>0 && annotationList.item(0).hasChildNodes()) { + // Insert the StarMath annotation as a kbd element + Element kbd = htmlDOM.createElement("kbd"); + hnode.appendChild(kbd); + NodeList list = annotationList.item(0).getChildNodes(); + int nLen = list.getLength(); + for (int i=0; i<nLen; i++) { + Node child = list.item(i); + if (child.getNodeType()==Node.TEXT_NODE) { + kbd.appendChild(htmlDOM.createTextNode(child.getNodeValue())); + } + } + } + else { + hnode.appendChild(htmlDOM.createTextNode("[Warning: formula ignored]")); + } + } + } + + public void convertNode(Node onode, Node hnode) { + if (onode.getNodeType()==Node.ELEMENT_NODE) { + if (onode.getNodeName().equals(XMLString.MATH_SEMANTICS)) { + // ignore this construction + convertNodeList(onode.getChildNodes(),hnode); + } + else if (onode.getNodeName().equals(XMLString.MATH_ANNOTATION)) { + // ignore the annotation (StarMath) completely + // (mozilla renders it for some reason) + } + else { + String sElementName = stripNamespace(onode.getNodeName()); + Element newNode = hnode.getOwnerDocument().createElement(sElementName); + hnode.appendChild(newNode); + if (onode.hasAttributes()) { + NamedNodeMap attr = onode.getAttributes(); + int nLen = attr.getLength(); + for (int i=0; i<nLen; i++) { + String sName = attr.item(i).getNodeName(); + if (sName.equals("xmlns:math")) { sName="xmlns"; } + else { sName = stripNamespace(sName); } + String sValue = attr.item(i).getNodeValue(); + newNode.setAttribute(sName,replacePrivateChars(sValue)); + } + } + convertNodeList(onode.getChildNodes(),newNode); + } + } + else if (onode.getNodeType()==Node.TEXT_NODE) { + String s = replacePrivateChars(onode.getNodeValue()); + hnode.appendChild(hnode.getOwnerDocument().createTextNode(s)); + } + } + + private void convertNodeList(NodeList list, Node hnode) { + if (list==null) { return; } + int nLen = list.getLength(); + for (int i=0; i<nLen; i++) { + convertNode(list.item(i),hnode); + } + } + + private String stripNamespace(String s) { + int nPos = s.indexOf(':'); + return s.substring(nPos+1); + } + + // OOo exports some characters (from the OpenSymbol/StarSymbol font) + // in the private use area of unicode. These should be replaced + // with real unicode positions. + private String replacePrivateChars(String s) { + int nLen = s.length(); + StringBuffer buf = new StringBuffer(nLen); + for (int i=0; i<nLen; i++) { + buf.append(replacePrivateChar(s.charAt(i))); + } + return buf.toString(); + } + + // This method maps {Open|Star}Symbol private use area to real unicode + // positions. This is the same table as in w2l/latex/style/symbols.xml. + // The named entities list is contributed by Bruno Mascret + private char replacePrivateChar(char c) { + switch (c) { + case '\uE002': return '\u2666'; + case '\uE003': return '\u25C6'; + case '\uE005': return '\u274D'; + case '\uE006': return '\u2794'; + case '\uE007': return '\u2713'; + case '\uE008': return '\u25CF'; + case '\uE009': return '\u274D'; + case '\uE00A': return '\u25FC'; + case '\uE00B': return '\u2752'; + case '\uE00D': return '\u2756'; + case '\uE013': return '\u2742'; + case '\uE01B': return '\u270D'; + case '\uE01E': return '\u2022'; + case '\uE021': return '\u00A9'; + case '\uE024': return '\u00AE'; + case '\uE025': return '\u21E8'; + case '\uE026': return '\u21E9'; + case '\uE027': return '\u21E6'; + case '\uE028': return '\u21E7'; + case '\uE02B': return '\u279E'; + case '\uE032': return '\u2741'; + case '\uE036': return '\u0028'; + case '\uE037': return '\u0029'; + case '\uE03A': return '\u20AC'; + case '\uE080': return '\u2030'; + case '\uE081': return '\uFE38'; // underbrace + case '\uE082': return '\uFE37'; // overbrace + case '\uE083': return '\u002B'; + case '\uE084': return '\u003C'; + case '\uE085': return '\u003E'; + case '\uE086': return '\u2264'; + case '\uE087': return '\u2265'; + case '\uE089': return '\u2208'; + case '\uE08B': return '\u2026'; + case '\uE08C': return '\u2192'; + case '\uE090': return '\u2225'; + case '\uE091': return '\u005E'; + case '\uE092': return '\u02C7'; + case '\uE093': return '\u02D8'; + case '\uE094': return '\u00B4'; + case '\uE095': return '\u0060'; + case '\uE096': return '\u02DC'; // or 007E + case '\uE097': return '\u00AF'; + case '\uE098': return '\u2192'; // or 21E1 + case '\uE09B': return '\u20DB'; // triple dot, neither MathPlayer nor Mozilla understands this glyph + case '\uE09E': return '\u0028'; + case '\uE09F': return '\u0029'; + case '\uE0A0': return '\u2221'; + case '\uE0AA': return '\u2751'; + case '\uE0AC': return '\u0393'; + case '\uE0AD': return '\u0394'; + case '\uE0AE': return '\u0398'; + case '\uE0AF': return '\u039B'; + case '\uE0B0': return '\u039E'; + case '\uE0B1': return '\u03A0'; + case '\uE0B2': return '\u03A3'; + case '\uE0B3': return '\u03A5'; + case '\uE0B4': return '\u03A6'; + case '\uE0B5': return '\u03A8'; + case '\uE0B6': return '\u03A9'; + case '\uE0B7': return '\u03B1'; + case '\uE0B8': return '\u03B2'; + case '\uE0B9': return '\u03B3'; + case '\uE0BA': return '\u03B4'; + case '\uE0BB': return '\u03F5'; + case '\uE0BC': return '\u03B6'; + case '\uE0BD': return '\u03B7'; + case '\uE0BE': return '\u03B8'; + case '\uE0BF': return '\u03B9'; + case '\uE0C0': return '\u03BA'; + case '\uE0C1': return '\u03BB'; + case '\uE0C2': return '\u03BC'; + case '\uE0C3': return '\u03BD'; + case '\uE0C4': return '\u03BE'; + case '\uE0C5': return '\u03BF'; + case '\uE0C6': return '\u03C0'; + case '\uE0C7': return '\u03C1'; + case '\uE0C8': return '\u03C3'; + case '\uE0C9': return '\u03C4'; + case '\uE0CA': return '\u03C5'; + case '\uE0CB': return '\u03D5'; + case '\uE0CC': return '\u03C7'; + case '\uE0CD': return '\u03C8'; + case '\uE0CE': return '\u03C9'; + case '\uE0CF': return '\u03B5'; + case '\uE0D0': return '\u03D1'; + case '\uE0D1': return '\u03D6'; + case '\uE0D3': return '\u03C2'; + case '\uE0D4': return '\u03C6'; + case '\uE0D5': return '\u2202'; + case '\uE0D9': return '\u22A4'; + case '\uE0DB': return '\u2190'; + case '\uE0DC': return '\u2191'; + case '\uE0DD': return '\u2193'; + default: + return c; + } + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/xhtml/Package.html b/source/java/writer2latex/xhtml/Package.html new file mode 100644 index 0000000..6235375 --- /dev/null +++ b/source/java/writer2latex/xhtml/Package.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.xhtml</title> +</head> + +<body> +<p>This package contains xhtml specific code.</p> +<p>It contains a <code>writerlatex.api.Converter</code> implementation for +conversion into xhtml and xhtml+MathML.</p> +<p>Since version 1.0 you can build Writer2LaTeX without this package if you +don't need xhtml support.</p> +</body> +</html> diff --git a/source/java/writer2latex/xhtml/PageStyleConverter.java b/source/java/writer2latex/xhtml/PageStyleConverter.java new file mode 100644 index 0000000..67adc28 --- /dev/null +++ b/source/java/writer2latex/xhtml/PageStyleConverter.java @@ -0,0 +1,137 @@ +/************************************************************************ + * + * PageStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import java.util.Enumeration; + +import writer2latex.office.MasterPage; +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.PageLayout; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; + +/** + * This class converts OpenDocument page styles to CSS2 styles. + * A page style in a presentation is represented through the master page, + * which links to a page layout defining the geometry and optionally a drawing + * page defining the drawing background. + * In a presentation document we export the full page style, in a text + * document we only export the background. + */ +public class PageStyleConverter extends StyleConverterHelper { + + /** Create a new <code>PageStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public PageStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + } + + public void applyStyle(String sStyleName, StyleInfo info) { + MasterPage masterPage = ofr.getMasterPage(sStyleName); + String sDisplayName = masterPage.getDisplayName(); + if (masterPage!=null) { + if (ofr.isPresentation()) { + // Always generates class name + info.sClass="masterpage"+styleNames.getExportName(sDisplayName); + } + else { + // For text documents only writing direction and background + String sPageLayout = masterPage.getPageLayoutName(); + PageLayout pageLayout = ofr.getPageLayout(sPageLayout); + if (pageLayout!=null) { + applyDirection(pageLayout,info); + if (bConvertStyles) { + getFrameSc().cssBackground(pageLayout,info.props,true); + } + } + } + } + } + + /** Convert style information for used styles + * @param sIndent a String of spaces to add before each line + */ + public String getStyleDeclarations(String sIndent) { + StringBuffer buf = new StringBuffer(); + Enumeration names = styleNames.keys(); + while (names.hasMoreElements()) { + // This will be master pages for presentations only + String sDisplayName = (String) names.nextElement(); + MasterPage style = (MasterPage) + getStyles().getStyleByDisplayName(sDisplayName); + StyleInfo info = new StyleInfo(); + // First apply page layout (size) + PageLayout pageLayout = ofr.getPageLayout(style.getPageLayoutName()); + if (pageLayout!=null) { + applyDirection(pageLayout,info); + cssPageSize(pageLayout,info.props); + getFrameSc().cssBackground(pageLayout,info.props,true); + } + // Next apply drawing-page style (draw background) + StyleWithProperties drawingPage = ofr.getDrawingPageStyle(style.getProperty(XMLString.DRAW_STYLE_NAME)); + if (drawingPage!=null) { + cssDrawBackground(drawingPage,info.props,true); + } + // The export the results + buf.append(sIndent) + .append(".masterpage").append(styleNames.getExportName(sDisplayName)) + .append(" {").append(info.props.toString()).append("}\n"); + } + return buf.toString(); + } + + /** Get the family of page styles (master pages) + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getMasterPages(); + } + + // Background properties in draw: Color, gradient, hatching or bitmap + private void cssDrawBackground(StyleWithProperties style, CSVList props, boolean bInherit){ + // Fill color: Same as in css + String s = style.getProperty(XMLString.DRAW_FILL_COLOR,bInherit); + if (s!=null) { props.addValue("background-color",s); } + } + + + private void cssPageSize(PageLayout style, CSVList props) { + String sWidth = style.getProperty(XMLString.FO_PAGE_WIDTH); + if (sWidth!=null) { props.addValue("width",scale(sWidth)); } + String sHeight = style.getProperty(XMLString.FO_PAGE_HEIGHT); + if (sHeight!=null) { props.addValue("height",scale(sHeight)); } + } + + + +} diff --git a/source/java/writer2latex/xhtml/ParStyleConverter.java b/source/java/writer2latex/xhtml/ParStyleConverter.java new file mode 100644 index 0000000..267f2b7 --- /dev/null +++ b/source/java/writer2latex/xhtml/ParStyleConverter.java @@ -0,0 +1,195 @@ +/************************************************************************ + * + * ParStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; + + /* + TODO: drop caps (contained in a child of the style:properties element) + The CSS attributes should be applied to the :first-letter + pseudo-element or to an additional inline element. + */ + + +/** + * This class converts OpenDocument paragraph styles to CSS2 styles for + * use in paragraphs and headings. + * This also includes conversion of paragraph properties in other styles + * (cell styles). + */ +public class ParStyleConverter extends StyleWithPropertiesConverterHelper { + + // Some bookkeeping for headings + private String[] sHeadingStyles = new String[7]; + + /** Create a new <code>ParStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public ParStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + this.styleMap = config.getXParStyleMap(); + this.bConvertStyles = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + // TODO: Remove me, OfficeReader takes care of this + public void setHeadingStyle(int nLevel, String sStyleName) { + if (sHeadingStyles[nLevel]==null) { + sHeadingStyles[nLevel] = sStyleName; + } + } + + /** Convert style information for used styles + * @param sIndent a String of spaces to add before each line + */ + public String getStyleDeclarations(String sIndent) { + StringBuffer buf = new StringBuffer(); + buf.append(super.getStyleDeclarations(sIndent)); + if (bConvertStyles) { + // Styles for headings + for (int i=1; i<=6; i++) { + if (sHeadingStyles[i]!=null) { + StyleWithProperties style = ofr.getParStyle(sHeadingStyles[i]); + if (style!=null) { + CSVList props = new CSVList(";"); + applyProperties(style,props,true); + props.addValue("clear","left"); + buf.append(sIndent).append("h").append(i) + .append(" {").append(props.toString()).append("}\n"); + } + } + } + } + return buf.toString(); + } + + /** Get the family of paragraph styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getParStyles(); + } + + /** Create default tag name to represent a paragraph + * @param style to use + * @return the tag name. + */ + public String getDefaultTagName(StyleWithProperties style) { + return "p"; + } + + /** Convert formatting properties for a specific Par style. + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit) { + getFrameSc().cssMargins(style,props,bInherit); + getFrameSc().cssBorder(style,props,bInherit); + getFrameSc().cssPadding(style,props,bInherit); + getFrameSc().cssBackgroundCommon(style,props,bInherit); + cssPar(style,props,bInherit); + getTextSc().cssTextCommon(style,props,bInherit); + } + + public String getTextBackground(String sStyleName) { + CSVList props = new CSVList(";"); + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style!=null) { + getTextSc().cssTextBackground(style,props,true); + } + return props.toString(); + } + + // TODO: get rid of this + public String getRealParStyleName(String sStyleName) { + if (sStyleName==null) { return sStyleName; } + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style==null || !style.isAutomatic()) { return sStyleName; } + return style.getParentName(); + } + + public void cssPar(StyleWithProperties style, CSVList props, boolean bInherit){ + String s; + + // translates paragraph style properties. + // The following properties are not supported by CSS2: + // style:justify-single-word and style:text-align-last + +/* problem: 120% times normal makes no sense... + s = style.getProperty(XMLString.FO_LINE_HEIGHT); + if (s!=null && s.equals("normal")) { + props.addValue("line-height:normal;"; + } + else { // length or percentage + s = style.getAbsoluteProperty(XMLString.FO_LINE_HEIGHT); + if (s!=null) { props.addValue("line-height",s); } + } + */ + // TODO: style:line-height-at-least and stype:line-spacing + + // Background color fits with css (note: Paragraph property!) + s = style.getParProperty(XMLString.FO_BACKGROUND_COLOR,bInherit); + if (s!=null) { props.addValue("background-color",s); } + + // Indentation: Absolute values of this property fit with css... + if (bInherit || style.getProperty(XMLString.FO_TEXT_INDENT,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_TEXT_INDENT); + if (s!=null) { + props.addValue("text-indent",scale(s)); + } + else { // ... but css doesn't have this one + s = style.getProperty(XMLString.STYLE_AUTO_TEXT_INDENT); + if ("true".equals(s)) { props.addValue("text-indent","2em"); } + } + } + + // Alignment: This property fit with css, but two values have different names + s = style.getProperty(XMLString.FO_TEXT_ALIGN,bInherit); + if (s!=null) { // rename two property values: + if (s.equals("start")) { s="left"; } + else if (s.equals("end")) { s="right"; } + props.addValue("text-align",s); + } + + // Wrap (only in table cells, only in spreadsheets): + if (ofr.isSpreadsheet()) { + s = style.getProperty(XMLString.FO_WRAP_OPTION,bInherit); + if ("no-wrap".equals(s)) props.addValue("white-space","nowrap"); + else if ("wrap".equals(s)) props.addValue("white-space","normal"); + } + } + + +} diff --git a/source/java/writer2latex/xhtml/PresentationStyleConverter.java b/source/java/writer2latex/xhtml/PresentationStyleConverter.java new file mode 100644 index 0000000..e2312a4 --- /dev/null +++ b/source/java/writer2latex/xhtml/PresentationStyleConverter.java @@ -0,0 +1,157 @@ +/************************************************************************ + * + * PresentationStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +import java.util.Enumeration; +import java.util.Hashtable; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +//import writer2latex.office.XMLString; +import writer2latex.util.CSVList; +import writer2latex.util.ExportNameCollection; + +/** + * This class converts OpenDocument presentation styles to CSS2 styles. + * Presentation styles are special frame styles, used to style the standard + * elements in a presentation (title, subtitle and textbox) + */ +public class PresentationStyleConverter extends FrameStyleConverter { + + // Data about outline styles + String sCurrentOutlineStyle = null; + Hashtable outlineStyles = new Hashtable(); + ExportNameCollection outlineStyleNames = new ExportNameCollection(true); + + /** Create a new <code>PresentationStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public PresentationStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + // style maps for presentations are currently not supported: + this.styleMap = new XhtmlStyleMap(); + } + + /** Return a prefix to be used in generated css class names + * @return the prefix + */ + public String getClassNamePrefix() { return "prsnt"; } + + /** Get the family of presentation styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getPresentationStyles(); + } + + /** Create default tag name to represent a presentation object + * @param style to use + * @return the tag name. + */ + public String getDefaultTagName(StyleWithProperties style) { + return "div"; + } + + /** <p>Convert style information for used styles</p> + * @param sIndent a String of spaces to add before each line + */ + public String getStyleDeclarations(String sIndent) { + if (bConvertStyles) { + StringBuffer buf = new StringBuffer(); + buf.append(super.getStyleDeclarations(sIndent)); + Enumeration names = outlineStyleNames.keys(); + while (names.hasMoreElements()) { + String sDisplayName = (String) names.nextElement(); + StyleWithProperties style = (StyleWithProperties) + getStyles().getStyleByDisplayName(sDisplayName); + if (!style.isAutomatic()) { + // Apply style to paragraphs within a list item with this class + CSVList props = new CSVList(";"); + getFrameSc().cssMargins(style,props,true); + getParSc().cssPar(style,props,true); + getTextSc().cssTextCommon(style,props,true); + if (!props.isEmpty()) { + buf.append(sIndent) + .append("li.outline") + .append(styleNames.getExportName(sDisplayName)) + .append(" p {").append(props.toString()).append("}\n"); + } + } + } + return buf.toString(); + } + else { + return ""; + } + } + + + public void enterOutline(String sStyleName) { + sCurrentOutlineStyle = sStyleName; + if (!outlineStyles.containsKey(sCurrentOutlineStyle)) { + String[] sNames = new String[10]; + outlineStyles.put(sCurrentOutlineStyle,sNames); + StyleWithProperties style1 = ofr.getPresentationStyle(sCurrentOutlineStyle); + if (style1!=null) { + String sCurrentOutlineStyle1 = sCurrentOutlineStyle; + if (style1.isAutomatic()) { sCurrentOutlineStyle1 = style1.getParentName(); } + sNames[1] = sCurrentOutlineStyle1; + String sBaseName = sCurrentOutlineStyle1.substring(0,sCurrentOutlineStyle1.length()-1); + for (int i=2; i<10; i++) { + String sName = sBaseName + Integer.toString(i); + StyleWithProperties style = ofr.getPresentationStyle(sName); + if (style!=null && style.getParentName().equals(sNames[i-1])) { + sNames[i] = sName; + } + else { + break; + } + } + sNames[1] = null; + } + } + } + + public void exitOutline() { + sCurrentOutlineStyle = null; + } + + public void applyOutlineStyle(int nLevel, StyleInfo info) { + if (2<=nLevel && nLevel<=9 && sCurrentOutlineStyle!=null) { + if (outlineStyles.containsKey(sCurrentOutlineStyle)) { + info.sClass = "outline"+outlineStyleNames.getExportName(((String[]) outlineStyles.get(sCurrentOutlineStyle))[nLevel]); + } + } + } + + + +} diff --git a/source/java/writer2latex/xhtml/RowStyleConverter.java b/source/java/writer2latex/xhtml/RowStyleConverter.java new file mode 100644 index 0000000..af20959 --- /dev/null +++ b/source/java/writer2latex/xhtml/RowStyleConverter.java @@ -0,0 +1,80 @@ +/************************************************************************ + * + * RowStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +//import writer2latex.office.XMLString; +import writer2latex.util.CSVList; + +/** + * This class converts OpenDocument row styles to CSS2 styles. + * Rows formatting includes <em>background</em>, and also <em>height</em>, + * which is considered elsewhere. + */ +public class RowStyleConverter extends StyleWithPropertiesConverterHelper { + + /** Create a new <code>RowStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public RowStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + // Style maps for rows are currently not supported. + this.styleMap = new XhtmlStyleMap(); + this.bConvertStyles = config.xhtmlTableFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlTableFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlTableFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlTableFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + /** Get the family of row styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getRowStyles(); + } + + /** Create default tag name to represent a row object + * @param style to use + * @return the tag name. + */ + public String getDefaultTagName(StyleWithProperties style) { + return "tr"; + } + + /** Convert formatting properties for a specific Row style. + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit) { + getFrameSc().cssBackground(style,props,bInherit); + } + +} diff --git a/source/java/writer2latex/xhtml/SectionStyleConverter.java b/source/java/writer2latex/xhtml/SectionStyleConverter.java new file mode 100644 index 0000000..919cd26 --- /dev/null +++ b/source/java/writer2latex/xhtml/SectionStyleConverter.java @@ -0,0 +1,84 @@ +/************************************************************************ + * + * SectionStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +//import writer2latex.office.XMLString; +import writer2latex.util.CSVList; + +/** + * This class converts OpenDocument section styles to CSS2 styles. + * Sections are formatted using (a subset of) box properties and with columns. + * The latter would require css3 to be converted (column-count property) + */ +public class SectionStyleConverter extends StyleWithPropertiesConverterHelper { + + /** Create a new <code>SectionStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public SectionStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + // Style maps for sections are currently not supported. + // (Section styles are not supported by OOo yet) + this.styleMap = new XhtmlStyleMap(); + this.bConvertStyles = config.xhtmlSectionFormatting()==XhtmlConfig.CONVERT_ALL + || config.xhtmlSectionFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlSectionFormatting()==XhtmlConfig.CONVERT_ALL + || config.xhtmlSectionFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + /** Get the family of section styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getSectionStyles(); + } + + /** <p>Create default tag name to represent a section object</p> + * @param style to use + * @return the tag name. If the style is null, a default result should be + * returned. + */ + public String getDefaultTagName(StyleWithProperties style) { + return "div"; + } + + /** <p>Convert formatting properties for a specific section style.</p> + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit) { + getFrameSc().cssBox(style,props,bInherit); + } + +} diff --git a/source/java/writer2latex/xhtml/StyleConverter.java b/source/java/writer2latex/xhtml/StyleConverter.java new file mode 100644 index 0000000..3ff1660 --- /dev/null +++ b/source/java/writer2latex/xhtml/StyleConverter.java @@ -0,0 +1,175 @@ +/************************************************************************ + * + * StyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +//import java.util.Enumeration; +//import java.util.Hashtable; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import writer2latex.office.*; +import writer2latex.util.*; + +/** + * <p>This class converts OpenDocument styles to CSS2 styles.</p> + * <p>Note that some elements in OpenDocument has attributes that also maps + * to CSS2 properties. Example: the width of a text box.</p> + * <p>Also note, that some OpenDocument style properties cannot be mapped to + * CSS2 without creating an additional inline element.</p> + * <p>The class uses one helper class per OpenDocument style family + * (paragraph, frame etc.)</p> + */ +class StyleConverter extends ConverterHelper { + + // Helpers for text styles + private TextStyleConverter textSc; + private ParStyleConverter parSc; + private ListStyleConverter listSc; + private SectionStyleConverter sectionSc; + + // Helpers for table styles + private TableStyleConverter tableSc; + private RowStyleConverter rowSc; + private CellStyleConverter cellSc; + + // Helpers for drawing styles + private FrameStyleConverter frameSc; + private PresentationStyleConverter presentationSc; + + // Helper for page styles + private PageStyleConverter pageSc; + + /** <p>Create a new <code>StyleConverter</code></p> + */ + public StyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter); + // Create the helpers + textSc = new TextStyleConverter(ofr,config,converter,nType); + parSc = new ParStyleConverter(ofr,config,converter,nType); + listSc = new ListStyleConverter(ofr,config,converter,nType); + sectionSc = new SectionStyleConverter(ofr,config,converter,nType); + tableSc = new TableStyleConverter(ofr,config,converter,nType); + rowSc = new RowStyleConverter(ofr,config,converter,nType); + cellSc = new CellStyleConverter(ofr,config,converter,nType); + frameSc = new FrameStyleConverter(ofr,config,converter,nType); + presentationSc = new PresentationStyleConverter(ofr,config,converter,nType); + pageSc = new PageStyleConverter(ofr,config,converter,nType); + } + + // Accessor methods for helpers + protected TextStyleConverter getTextSc() { return textSc; } + + protected ParStyleConverter getParSc() { return parSc; } + + protected ListStyleConverter getListSc() { return listSc; } + + protected SectionStyleConverter getSectionSc() { return sectionSc; } + + protected TableStyleConverter getTableSc() { return tableSc; } + + protected RowStyleConverter getRowSc() { return rowSc; } + + protected CellStyleConverter getCellSc() { return cellSc; } + + protected FrameStyleConverter getFrameSc() { return frameSc; } + + protected PresentationStyleConverter getPresentationSc() { return presentationSc; } + + protected PageStyleConverter getPageSc() { return pageSc; } + + private StyleWithProperties getDefaultStyle() { + if (ofr.isSpreadsheet()) return ofr.getDefaultCellStyle(); + else if (ofr.isPresentation()) return ofr.getDefaultFrameStyle(); + else return ofr.getDefaultParStyle(); + } + + // Apply the default language + public void applyDefaultLanguage(Element node) { + StyleWithProperties style = getDefaultStyle(); + if (style!=null) { + StyleInfo info = new StyleInfo(); + StyleConverterHelper.applyLang(style,info); + applyStyle(info,node); + } + } + + // Export used styles to CSS + public Node exportStyles(Document htmlDOM) { + String sIndent = " "; + + StringBuffer buf = new StringBuffer(); + + // Export default style + if (config.xhtmlCustomStylesheet().length()==0 && + (config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || + config.xhtmlFormatting()==XhtmlConfig.IGNORE_HARD)) { + // Default paragraph/cell/frame style is applied to the body element + StyleWithProperties defaultStyle = getDefaultStyle(); + if (defaultStyle!=null) { + CSVList props = new CSVList(";"); + // text properties only! + getTextSc().cssTextCommon(defaultStyle,props,true); + buf.append(sIndent) + .append("body {").append(props.toString()).append("}\n"); + } + + } + + // Export declarations from helpers + // For OpenDocument documents created with OOo only some will generate content: + // Text documents: text, par, list, frame + // Spreadsheet documents: cell + // Presentation documents: frame, presentation, page + buf.append(getTextSc().getStyleDeclarations(sIndent)); + buf.append(getParSc().getStyleDeclarations(sIndent)); + buf.append(getListSc().getStyleDeclarations(sIndent)); + buf.append(getSectionSc().getStyleDeclarations(sIndent)); + buf.append(getCellSc().getStyleDeclarations(sIndent)); + buf.append(getTableSc().getStyleDeclarations(sIndent)); + buf.append(getRowSc().getStyleDeclarations(sIndent)); + buf.append(getFrameSc().getStyleDeclarations(sIndent)); + buf.append(getPresentationSc().getStyleDeclarations(sIndent)); + buf.append(getPageSc().getStyleDeclarations(sIndent)); + + // Create node + if (buf.length()>0) { + Element htmlStyle = htmlDOM.createElement("style"); + htmlStyle.setAttribute("media","all"); + htmlStyle.setAttribute("type","text/css"); + htmlStyle.appendChild(htmlDOM.createTextNode("\n")); + htmlStyle.appendChild(htmlDOM.createTextNode(buf.toString())); + htmlStyle.appendChild(htmlDOM.createTextNode(" ")); + return htmlStyle; + } + else { + return null; + } + + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/xhtml/StyleConverterHelper.java b/source/java/writer2latex/xhtml/StyleConverterHelper.java new file mode 100644 index 0000000..4f1ce4f --- /dev/null +++ b/source/java/writer2latex/xhtml/StyleConverterHelper.java @@ -0,0 +1,121 @@ +/************************************************************************ + * + * StyleConverterHelper.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.ExportNameCollection; +import writer2latex.util.Misc; + +/** + * <p>This is an abstract base class to convert an OpenDocument style family to + * CSS2 styles.</p> + */ +public abstract class StyleConverterHelper extends ConverterHelper { + + // Translation of OpenDocument style names to CSS class names + protected ExportNameCollection styleNames = new ExportNameCollection(true); + + // Style map to use + protected XhtmlStyleMap styleMap; + + // Should we convert styles resp. hard formatting? + protected boolean bConvertStyles = true; + protected boolean bConvertHard = true; + + // The type of xhtml document + protected int nType; + + // Scaling and unit transformation to use + private String sScale; + private String sColScale; + private boolean bConvertToPx; + + /** Create a new <code>StyleConverterHelper</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public StyleConverterHelper(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter); + this.nType = nType; + sScale = config.getXhtmlScaling(); + sColScale = config.getXhtmlColumnScaling(); + bConvertToPx = config.xhtmlConvertToPx(); + } + + protected String scale(String s) { + if (bConvertToPx) { + return Misc.length2px(Misc.multiply(sScale,s)); + } + else { + return Misc.multiply(sScale,s); + } + } + + protected String colScale(String s) { + return scale(Misc.multiply(sColScale,s)); + } + + /** Apply the writing direction (ltr or rtl) attribute from a style + * @param style the OpenDocument style to use + * @param info the <code>StyleInfo</code> object to add information to + */ + protected static void applyDirection(StyleWithProperties style, StyleInfo info) { + String sDir = style.getProperty(XMLString.STYLE_WRITING_MODE); + if ("lr-tb".equals(sDir)) { info.sDir="ltr"; } + else if ("rl-tb".equals(sDir)) { info.sDir="rtl"; } + } + + /** Apply language+country from a style + * @param style the OpenDocument style to use + * @param info the <code>StyleInfo</code> object to add information to + */ + protected static void applyLang(StyleWithProperties style, StyleInfo info) { + String sLang = style.getProperty(XMLString.FO_LANGUAGE); + String sCountry = style.getProperty(XMLString.FO_COUNTRY); + if (sLang!=null) { + if (sCountry==null) { info.sLang = sLang; } + else { info.sLang = sLang+"-"+sCountry; } + } + } + + /** Get the OpenDocument style family associated with this + * StyleConverterHelper + * @return the style family + */ + public abstract OfficeStyleFamily getStyles(); + + /** <p>Convert style information for used styles</p> + * @param sIndent a String of spaces to add before each line + */ + public abstract String getStyleDeclarations(String sIndent); + +} diff --git a/source/java/writer2latex/xhtml/StyleInfo.java b/source/java/writer2latex/xhtml/StyleInfo.java new file mode 100644 index 0000000..bf5d911 --- /dev/null +++ b/source/java/writer2latex/xhtml/StyleInfo.java @@ -0,0 +1,41 @@ +/************************************************************************ + * + * StyleInfo.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2007 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2007-02-27) + * + */ + +package writer2latex.xhtml; + +import writer2latex.util.CSVList; + +public class StyleInfo { + public String sTagName = null; + public String sClass = null; + public CSVList props = new CSVList(";"); + public String sLang = null; + public String sDir = null; + + public boolean hasAttributes() { + return !props.isEmpty() || sClass!=null || sLang!=null || sDir!=null; + } +} diff --git a/source/java/writer2latex/xhtml/StyleWithPropertiesConverterHelper.java b/source/java/writer2latex/xhtml/StyleWithPropertiesConverterHelper.java new file mode 100644 index 0000000..133ddfd --- /dev/null +++ b/source/java/writer2latex/xhtml/StyleWithPropertiesConverterHelper.java @@ -0,0 +1,144 @@ +/************************************************************************ + * + * StyleWithPropertiesConverterHelper.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +import java.util.Enumeration; +//import java.util.Hashtable; + +//import writer2latex.latex.util.Info; +import writer2latex.office.OfficeReader; +import writer2latex.office.StyleWithProperties; +import writer2latex.util.CSVList; +//import writer2latex.util.ExportNameCollection; + +/** + * <p>This is an abstract class to convert an OpenDocument style family + * represented by <code>StyleWithProperties</code> to CSS2 styles.</p> + */ +public abstract class StyleWithPropertiesConverterHelper + extends StyleConverterHelper { + + /** Create a new <code>StyleWithPropertiesConverterHelper</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public StyleWithPropertiesConverterHelper(OfficeReader ofr, XhtmlConfig config, + Converter converter, int nType) { + super(ofr,config,converter,nType); + } + + /** Apply a style, either by converting the style or by applying the + * style map from the configuarion + * @param sStyleName name of the OpenDocument style + * @param info the <code>StyleInfo</code> object to add information to + */ + public void applyStyle(String sStyleName, StyleInfo info) { + StyleWithProperties style = (StyleWithProperties) getStyles().getStyle(sStyleName); + info.sTagName = getDefaultTagName(style); + if (style!=null) { + applyLang(style,info); + applyDirection(style,info); + if (style.isAutomatic()) { + // Apply parent style + hard formatting + applyStyle(style.getParentName(),info); + if (bConvertHard) { applyProperties(style,info.props,false); } + } + else { + String sDisplayName = style.getDisplayName(); + if (styleMap.contains(sDisplayName)) { + // Apply attributes as specified in style map from user + info.sTagName = styleMap.getElement(sDisplayName); + if (!"(none)".equals(styleMap.getCss(sDisplayName))) { + info.sClass = styleMap.getCss(sDisplayName); + } + } + else { + // Generate class name from display name + info.sClass = getClassNamePrefix() + + styleNames.getExportName(sDisplayName); + } + } + } + } + + /** Convert style information for used styles + * @param sIndent a String of spaces to add before each line + */ + public String getStyleDeclarations(String sIndent) { + if (bConvertStyles) { + StringBuffer buf = new StringBuffer(); + Enumeration names = styleNames.keys(); + while (names.hasMoreElements()) { + String sDisplayName = (String) names.nextElement(); + StyleWithProperties style = (StyleWithProperties) + getStyles().getStyleByDisplayName(sDisplayName); + if (!style.isAutomatic()) { + CSVList props = new CSVList(";"); + applyProperties(style,props,true); + buf.append(sIndent); + buf.append(getDefaultTagName(null)); + buf.append("."); + buf.append(getClassNamePrefix()); + buf.append(styleNames.getExportName(sDisplayName)); + buf.append(" {"); + buf.append(props.toString()); + buf.append("}\n"); + // TODO: Create a method "getStyleDeclarationsInner" + // to be used by eg. FrameStyleConverter + } + } + return buf.toString(); + } + else { + return ""; + } + } + + /** Return a prefix to be used in generated css class names + * @return the prefix + */ + public String getClassNamePrefix() { return ""; } + + /** Create default tag name to represent a specific style, e.g. + * <code>span</code> (text style) or <code>ul</code> (unordered list) + * @param style to use + * @return the tag name. If the style is null, a default result should be + * returned. + */ + public abstract String getDefaultTagName(StyleWithProperties style); + + /** Convert formatting properties for a specific style. + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public abstract void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit); + + +} diff --git a/source/java/writer2latex/xhtml/TableConverter.java b/source/java/writer2latex/xhtml/TableConverter.java new file mode 100644 index 0000000..474efea --- /dev/null +++ b/source/java/writer2latex/xhtml/TableConverter.java @@ -0,0 +1,453 @@ +/************************************************************************ + * + * TableConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-12-15) + * + */ + +package writer2latex.xhtml; + +import java.util.Vector; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Element; + +import writer2latex.util.Misc; +import writer2latex.util.SimpleInputBuffer; +import writer2latex.office.XMLString; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.OfficeReader; +//import writer2latex.office.TableLine; +import writer2latex.office.TableRange; +import writer2latex.office.TableReader; +import writer2latex.office.TableView; + +public class TableConverter extends ConverterHelper { + + // The collection of all table names + // TODO: Navigation should be handled here rather than in Converter.java + protected Vector sheetNames = new Vector(); + + public TableConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter); + } + + /** Converts an office node as a complete table (spreadsheet) document + * + * @param onode the Office node containing the content to convert + */ + public void convertTableContent(Element onode) { + Element hnode = null; + if (!onode.hasChildNodes()) { return; } + if (!config.xhtmlCalcSplit()) { hnode = nextOutFile(); } + NodeList nList = onode.getChildNodes(); + int nLen = nList.getLength(); + for (int i=0; i<nLen; i++) { + Node child = nList.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sNodeName = child.getNodeName(); + if (sNodeName.equals(XMLString.TABLE_TABLE)) { + StyleWithProperties style = ofr.getTableStyle( + Misc.getAttribute(child,XMLString.TABLE_STYLE_NAME)); + if ((config.xhtmlDisplayHiddenSheets() || style==null + || !"false".equals(style.getProperty(XMLString.TABLE_DISPLAY))) + && (!config.applyPrintRanges() || ofr.getTableReader((Element)child).getPrintRangeCount()>0)) { + if (config.xhtmlCalcSplit()) { hnode = nextOutFile(); } + // Collect name + String sName = Misc.getAttribute(child,XMLString.TABLE_NAME); + sheetNames.add(sName); + + // Add sheet name as heading, if required + if (config.xhtmlUseSheetNamesAsHeadings()) { + Element heading = converter.createElement("h2"); + hnode.appendChild(heading); + heading.setAttribute("id","tableheading"+(sheetNames.size()-1)); + heading.appendChild(converter.createTextNode(sName)); + } + + // Handle the table + handleTable(child,hnode); + + // Add frames belonging to this table + Element div = converter.createElement("div"); + Element shapes = Misc.getChildByTagName(child,XMLString.TABLE_SHAPES); + if (shapes!=null) { + Node shape = shapes.getFirstChild(); + while (shape!=null) { + if (OfficeReader.isDrawElement(shape)) { + // Actually only the first parameter is used + getDrawCv().handleDrawElement((Element)shape,div,null,DrawConverter.CENTERED); + } + shape = shape.getNextSibling(); + } + } + getDrawCv().flushFrames(div); + if (div.hasChildNodes()) { hnode.appendChild(div); } + } + } + } + } + if (converter.getOutFileIndex()<0) { + // No files, add an empty one (This may happen if apply_print_ranges=true + // and the document does not contain any print ranges) + nextOutFile(); + } + } + + private Element nextOutFile() { + Element hnode = converter.nextOutFile(); + // Add title, if required by config + if (config.xhtmlUseTitleAsHeading()) { + String sTitle = converter.getMetaData().getTitle(); + if (sTitle!=null) { + Element title = converter.createElement("h1"); + hnode.appendChild(title); + title.appendChild(converter.createTextNode(sTitle)); + } + } + return hnode; + } + + /** Process a table:table tag + * + * @param onode the Office node containing the table element + * @param hnode the XHTML node to which the table should be attached + */ + public void handleTable(Node onode, Node hnode) { + TableReader tblr = ofr.getTableReader((Element)onode); + if (config.applyPrintRanges()) { + if (tblr.getPrintRangeCount()>0) { + Element div = converter.createElement("div"); + if (!tblr.isSubTable()) { + converter.addTarget(div,tblr.getTableName()+"|table"); + } + hnode.appendChild(div); + int nCount = tblr.getPrintRangeCount(); + for (int nRange=0; nRange<nCount; nRange++) { + Element table = createTable(tblr); + div.appendChild(table); + TableRange range = tblr.getPrintRange(nRange); + range.setIncludeHidden(config.displayHiddenRowsCols()); + range.setIncludeFiltered(config.displayFilteredRowsCols()); + traverseTable(range.createTableView(),table); + } + } + } + else { + // Create table + Element table = createTable(tblr); + if (!tblr.isSubTable()) { + converter.addTarget(table,tblr.getTableName()+"|table"); + } + hnode.appendChild(table); + + // Create view (full table) + TableRange range = new TableRange(tblr); + if (ofr.isSpreadsheet()) { + // skip trailing empty rows and columns + range.setLastRow(tblr.getMaxRowCount()-1); + range.setLastCol(tblr.getMaxColCount()-1); + } + range.setIncludeHidden(config.displayHiddenRowsCols()); + range.setIncludeFiltered(config.displayFilteredRowsCols()); + traverseTable(range.createTableView(),table); + } + } + + private Element createTable(TableReader tblr) { + Element table = converter.createElement("table"); + // Apply table style + // IE needs the cellspacing attribute, as it doesn't understand the css border-spacing attribute + table.setAttribute("cellspacing","0"); + applyTableStyle(tblr.getTableStyleName(), table, tblr.isSubTable()); + return table; + } + + private void traverseTable(TableView view, Element hnode) { + int nRowCount = view.getRowCount(); + int nColCount = view.getColCount(); + // Check to see, if the first row contains any colspan + boolean bFirstRowColSpan = false; + for (int nCol=0; nCol<nColCount; nCol++) { + Node cell = view.getCell(0,nCol); + if (cell!=null && XMLString.TABLE_TABLE_CELL.equals(cell.getNodeName())) { + String sColSpan = Misc.getAttribute(cell,XMLString.TABLE_NUMBER_COLUMNS_SPANNED); + if (Misc.getPosInteger(sColSpan,1)>1) { + bFirstRowColSpan = true; + } + } + } + + // Create columns; only for tables with relative width + // Otherwise we set the cell width. Reason: IE and Mozilla does not + // interpret column width the same way. IE excludes padding and border, + // Mozilla (like OOo) includes them. + // If the first row contains colspan we have to add <col> anyway + if (!config.xhtmlIgnoreTableDimensions()) { + if (view.getRelTableWidth()!=null) { + for (int nCol=0; nCol<nColCount; nCol++) { + Element col = converter.createElement("col"); + hnode.appendChild(col); + col.setAttribute("style","width:"+view.getRelColumnWidth(nCol)); + } + } + else if (bFirstRowColSpan) { + for (int nCol=0; nCol<nColCount; nCol++) { + Element col = converter.createElement("col"); + hnode.appendChild(col); + col.setAttribute("style","width:"+getTableSc().colScale(view.getColumnWidth(nCol))); + } + } + } + + // Indentify head + int nBodyStart = 0; + while (nBodyStart<nRowCount && view.getRow(nBodyStart).isHeader()) { + nBodyStart++; + } + if (nBodyStart==0 || nBodyStart==nRowCount) { + // all body or all head + traverseRows(view,0,nRowCount,hnode); + } + else { + // Create thead + Element thead = converter.createElement("thead"); + hnode.appendChild(thead); + traverseRows(view,0,nBodyStart,thead); + // Create tbody + Element tbody = converter.createElement("tbody"); + hnode.appendChild(tbody); + traverseRows(view,nBodyStart,nRowCount,tbody); + } + + } + + private void traverseRows(TableView view, int nFirstRow, int nLastRow, Element hnode) { + for (int nRow=nFirstRow; nRow<nLastRow; nRow++) { + // Create row and apply row style + Element tr = converter.createElement("tr"); + hnode.appendChild(tr); + applyRowStyle(view.getRow(nRow).getStyleName(),tr); + + for (int nCol=0; nCol<view.getColCount(); nCol++) { + Node cell = view.getCell(nRow,nCol); + if (cell!=null && XMLString.TABLE_TABLE_CELL.equals(cell.getNodeName())) { + // Create cell + Element td = converter.createElement("td"); + tr.appendChild(td); + int nRowSpan = view.getRowSpan(nRow,nCol); + if (nRowSpan>1) { + td.setAttribute("rowspan",Integer.toString(nRowSpan)); + } + int nColSpan = view.getColSpan(nRow,nCol); + if (nColSpan>1) { + td.setAttribute("colspan",Integer.toString(nColSpan)); + } + + // Handle content + if (!isEmptyCell(cell)) { + getTextCv().traverseBlockText(cell,td); + } + else { + // Hack to display empty cells even in msie... + Element par = converter.createElement("p"); + td.appendChild(par); + par.setAttribute("style","margin:0;font-size:1px"); + par.appendChild(converter.createTextNode("\u00A0")); + } + + // Is this a subtable? + Node subTable = Misc.getChildByTagName(cell,XMLString.TABLE_SUB_TABLE); + String sTotalWidth=null; + if (nColSpan==1) { + sTotalWidth = view.getCellWidth(nRow,nCol); + } + String sValueType = ofr.isOpenDocument() ? + Misc.getAttribute(cell,XMLString.OFFICE_VALUE_TYPE) : + Misc.getAttribute(cell,XMLString.TABLE_VALUE_TYPE); + applyCellStyle(view.getCellStyleName(nRow,nCol), sTotalWidth, sValueType, td, subTable!=null); + } + else if (XMLString.TABLE_COVERED_TABLE_CELL.equals(cell.getNodeName())) { + // covered table cells are not part of xhtml table model + } + } + } + } + + private boolean isEmptyCell(Node cell) { + if (!cell.hasChildNodes()) { + return true; + } + else if (OfficeReader.isSingleParagraph(cell)) { + Element par = Misc.getChildByTagName(cell,XMLString.TEXT_P); + return par==null || !par.hasChildNodes(); + } + return false; + } + + private void applyTableStyle(String sStyleName, Element table, boolean bIsSubTable) { + StyleInfo info = new StyleInfo(); + getTableSc().applyStyle(sStyleName,info); + + if (!config.xhtmlIgnoreTableDimensions()) { + StyleWithProperties style = ofr.getTableStyle(sStyleName); + if (style!=null) { + // Set table width + String sWidth = style.getProperty(XMLString.STYLE_REL_WIDTH); + if (sWidth!=null) { + info.props.addValue("width",sWidth); + } + else { + sWidth = style.getProperty(XMLString.STYLE_WIDTH); + if (sWidth!=null) { + info.props.addValue("width",getTableSc().colScale(sWidth)); + } + } + } + } + + // Writer uses a separating border model, Calc a collapsing: + // props.addValue("border-collapse", bCalc ? "collapse" : "separate"); + // For now always use separating model: + info.props.addValue("border-collapse", "separate"); + info.props.addValue("border-spacing", "0"); + + info.props.addValue("table-layout","fixed"); + + //info.props.addValue("empty-cells","show"); use instead... + + if (ofr.isSpreadsheet()) { info.props.addValue("white-space","nowrap"); } + + if (bIsSubTable) { + // Should try to fill the cell; hence: + info.props.addValue("width","100%"); + info.props.addValue("margin","0"); + } + applyStyle(info,table); + } + + private void applyRowStyle(String sStyleName, Element row) { + StyleInfo info = new StyleInfo(); + getRowSc().applyStyle(sStyleName,info); + + if (!config.xhtmlIgnoreTableDimensions()) { + StyleWithProperties style = ofr.getRowStyle(sStyleName); + if (style!=null) { + // Translates row style properties + // OOo offers style:row-height and style:min-row-height + // In css row heights are always minimal, so both are exported as height + // If neither is specified, the tallest cell rules; this fits with css. + String s = style.getAbsoluteProperty(XMLString.STYLE_ROW_HEIGHT); + // Do not export minimal row height; causes trouble with ie + //if (s==null) { s = style.getAbsoluteProperty(XMLString.STYLE_MIN_ROW_HEIGHT); } + if (s!=null) { info.props.addValue("height",getRowSc().scale(s)); } + } + } + + applyStyle(info,row); + } + + private void applyCellStyle(String sStyleName, String sTotalWidth, String sValueType, Element cell, boolean bIsSubTable) { + StyleInfo info = new StyleInfo(); + getCellSc().applyStyle(sStyleName,info); + + StyleWithProperties style = ofr.getCellStyle(sStyleName); + if (style!=null) { + if (!config.xhtmlIgnoreTableDimensions()) { + String sEdge = "0"; + + // Set the cell width. This is calculated as + // "total cell width" - "border" - "padding" + String s = style.getProperty(XMLString.FO_PADDING_LEFT); + if (s!=null) { + sEdge=Misc.add(sEdge,getTableSc().colScale(s)); + } + s = style.getProperty(XMLString.FO_PADDING_RIGHT); + if (s!=null) { + sEdge=Misc.add(sEdge,getTableSc().colScale(s)); + } + s = style.getProperty(XMLString.FO_PADDING); + if (s!=null) { + sEdge=Misc.add(sEdge,Misc.multiply("200%",getTableSc().colScale(s))); + } + s = style.getProperty(XMLString.FO_BORDER_LEFT); + if (s!=null) { + sEdge=Misc.add(sEdge,getTableSc().colScale(borderWidth(s))); + } + s = style.getProperty(XMLString.FO_BORDER_RIGHT); + if (s!=null) { + sEdge=Misc.add(sEdge,getTableSc().colScale(borderWidth(s))); + } + s = style.getProperty(XMLString.FO_BORDER); + if (s!=null) { + sEdge=Misc.add(sEdge,Misc.multiply("200%",getTableSc().colScale(borderWidth(s)))); + } + + if (sTotalWidth!=null) { + info.props.addValue("width",Misc.sub(getTableSc().colScale(sTotalWidth),sEdge)); + } + } + + // Automatic horizontal alignment (calc only) + if (ofr.isSpreadsheet() && !"fix".equals(style.getProperty(XMLString.STYLE_TEXT_ALIGN_SOURCE))) { + // Strings go left, other types (float, time, date, percentage, currency, boolean) go right + // The default is string + info.props.addValue("text-align", sValueType==null || "string".equals(sValueType) ? "left" : "right"); + } + } + + if (!cell.hasChildNodes()) { // hack to handle empty cells even in msie + // info.props.addValue("line-height","1px"); TODO: Reenable this... + cell.appendChild( converter.createTextNode("\u00A0") ); + } + + if (bIsSubTable) { + // Cannot set height of a subtable, if the subtable does not fill + // the entire cell it is placed at the top + info.props.addValue("vertical-align","top"); + // Don't add padding if there's a subtable in the cell! + info.props.addValue("padding","0"); + } + + applyStyle(info,cell); + } + + // TODO: Move me to a more logical place! + public String borderWidth(String sBorder) { + if (sBorder==null || sBorder.equals("none")) { return "0"; } + SimpleInputBuffer in = new SimpleInputBuffer(sBorder); + while (in.peekChar()!='\0') { + // Skip spaces + while(in.peekChar()==' ') { in.getChar(); } + // If it's a number it must be a unit -> get it + if ('0'<=in.peekChar() && in.peekChar()<='9') { + return in.getNumber()+in.getIdentifier(); + } + // skip other characters + while (in.peekChar()!=' ' && in.peekChar()!='\0') { } + } + return "0"; + } + + +} diff --git a/source/java/writer2latex/xhtml/TableStyleConverter.java b/source/java/writer2latex/xhtml/TableStyleConverter.java new file mode 100644 index 0000000..fc68ad8 --- /dev/null +++ b/source/java/writer2latex/xhtml/TableStyleConverter.java @@ -0,0 +1,114 @@ +/************************************************************************ + * + * TableStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; + +/** + * This class converts OpenDocument table styles to CSS2 styles. + * Table formatting includes <em>background</em>, <em>alignment</em>, + * <em>margins</em>, and also <em>width</em>, which is considered elsewhere. + */ +public class TableStyleConverter extends StyleWithPropertiesConverterHelper { + + /** Create a new <code>TableStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public TableStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + // Style maps for tables are currently not supported. + this.styleMap = new XhtmlStyleMap(); + this.bConvertStyles = config.xhtmlTableFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlTableFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlTableFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlTableFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + /** Get the family of table styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getTableStyles(); + } + + /** Create default tag name to represent a table object + * @param style to use + * @return the tag name + */ + public String getDefaultTagName(StyleWithProperties style) { + return "table"; + } + + /** Convert formatting properties for a specific table style. + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit) { + // Apply background + getFrameSc().cssBackground(style,props,bInherit); + // Table-specific properties + cssTable(style,props,bInherit); + } + + private void cssTable(StyleWithProperties style, CSVList props, boolean bInherit){ + // Top and bottom margins + String sMarginTop = style.getAbsoluteProperty(XMLString.FO_MARGIN_TOP); + if (sMarginTop!=null) { props.addValue("margin-top",scale(sMarginTop)); } + else { props.addValue("margin-top","0"); } + + String sMarginBottom = style.getAbsoluteProperty(XMLString.FO_MARGIN_BOTTOM); + if (sMarginBottom!=null) { props.addValue("margin-bottom",scale(sMarginBottom)); } + else { props.addValue("margin-bottom","0"); } + + // Left and right margins and horizontal alignment + String sAlign = style.getProperty(XMLString.TABLE_ALIGN); + String sMarginLeft = style.getAbsoluteProperty(XMLString.FO_MARGIN_LEFT); + if (sMarginLeft!=null) { sMarginLeft = scale(sMarginLeft); } + String sMarginRight = style.getAbsoluteProperty(XMLString.FO_MARGIN_RIGHT); + if (sMarginRight!=null) { sMarginRight = scale(sMarginRight); } + + if ("center".equals(sAlign)) { + sMarginLeft = "auto"; sMarginRight = "auto"; + } + else if ("right".equals(sAlign)) { + sMarginLeft = "auto"; + } + else if ("left".equals(sAlign)) { + sMarginRight = "auto"; + } + + if (sMarginLeft!=null) { props.addValue("margin-left",sMarginLeft); } + if (sMarginRight!=null) { props.addValue("margin-right",sMarginRight); } + } + +} diff --git a/source/java/writer2latex/xhtml/TextConverter.java b/source/java/writer2latex/xhtml/TextConverter.java new file mode 100644 index 0000000..636a896 --- /dev/null +++ b/source/java/writer2latex/xhtml/TextConverter.java @@ -0,0 +1,1811 @@ +/************************************************************************ + * + * TextConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-22) + * + */ + +package writer2latex.xhtml; + +import java.util.Hashtable; +import java.util.Stack; +import java.util.Vector; +import java.util.LinkedList; +import java.util.Locale; + +import java.text.Collator; + +//import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.Element; + +import writer2latex.util.Misc; +import writer2latex.office.XMLString; +import writer2latex.office.IndexMark; +import writer2latex.office.ListCounter; +import writer2latex.office.ListStyle; +import writer2latex.office.PropertySet; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.OfficeReader; +import writer2latex.office.TocReader; + +// Helper class (a struct) to contain information about an alphabetical +// index entry. +final class AlphabeticalEntry { + String sWord; // the word for the index + int nIndex; // the original index of this entry +} + +// Helper class (a struct) to contain information about a toc entry +// (ie. a heading, other paragraph or toc-mark) +final class TocEntry { + Element onode; // the original node + String sLabel = null; // generated label for the entry + int nFileIndex; // the file index for the generated content + int nOutlineLevel; // the outline level for this heading + int[] nOutlineNumber; // the natural outline number for this heading +} + +// Helper class (a struct) to point back to indexes that should be processed +final class IndexData { + int nOutFileIndex; // the index of the out file containing the index + Element onode; // the original node + Element chapter; // the chapter containing this toc + Element hnode; // a div node where the index should be added +} + +public class TextConverter extends ConverterHelper { + + // Data used to handle splitting over several files + // TODO: Accessor methods for sections + int nSplit = 0; // The outline level at which to split files (0=no split) + int nRepeatLevels = 5; // The number of levels to repeat when splitting (0=no repeat) + private int nLastSplitLevel = 1; // The outline level at which the last split occured + private int nDontSplitLevel = 0; // if > 0 splitting is forbidden + boolean bAfterHeading=false; // last element was a top level heading + protected Stack sections = new Stack(); // stack of nested sections + Element[] currentHeading = new Element[7]; // Last headings (repeated when splitting) + + // Counters for generated numbers + private ListCounter outlineNumbering; + private Hashtable listCounters = new Hashtable(); + private String sCurrentListLabel = null; + + // Mode used to handle floats (depends on source doc type and config) + private int nFloatMode; + + // Data used for index bookkeeping + private Vector indexes = new Vector(); + + // Data used to handle Alphabetical Index + Vector index = new Vector(); // All words for the index + private int nIndexIndex = -1; // Current index used for id's (of form idxN) + private int nAlphabeticalIndex = -1; // File containing alphabetical index + + // Data used to handle Table of Contents + private Vector tocEntries = new Vector(); // All potential(!) toc items + private int nTocFileIndex = -1; // file index for main toc + private Element currentChapter = null; // Node for the current chapter (level 1) heading + private int nTocIndex = -1; // Current index for id's (of form tocN) + private ListCounter naturalOutline = new ListCounter(); // Current "natural" outline number + + // Style names for foot- and endnotes + private String sFntCitBodyStyle = null; + private String sFntCitStyle = null; + private String sEntCitBodyStyle = null; + private String sEntCitStyle = null; + + // Gather the footnotes and endnotes + private LinkedList footnotes = new LinkedList(); + private LinkedList endnotes = new LinkedList(); + + // Sometimes we have to create an inlinenode in a block context + // (labels for footnotes and endnotes) + // We put it here and insert it in the first paragraph/heading to come: + private Node asapNode = null; + + // When generating toc, a few things should be done differently + private boolean bInToc = false; + + public TextConverter(OfficeReader ofr, XhtmlConfig config, Converter converter) { + super(ofr,config,converter); + nSplit = config.getXhtmlSplitLevel(); + nRepeatLevels = config.getXhtmlRepeatLevels(); + nFloatMode = ofr.isText() && config.xhtmlFloatObjects() ? + DrawConverter.FLOATING : DrawConverter.ABSOLUTE; + outlineNumbering = new ListCounter(ofr.getOutlineStyle()); + // Styles for footnotes and endnotes + PropertySet notes = ofr.getFootnotesConfiguration(); + if (notes!=null) { + sFntCitBodyStyle = notes.getProperty(XMLString.TEXT_CITATION_BODY_STYLE_NAME); + sFntCitStyle = notes.getProperty(XMLString.TEXT_CITATION_STYLE_NAME); + } + notes = ofr.getEndnotesConfiguration(); + if (notes!=null) { + sEntCitBodyStyle = notes.getProperty(XMLString.TEXT_CITATION_BODY_STYLE_NAME); + sEntCitStyle = notes.getProperty(XMLString.TEXT_CITATION_STYLE_NAME); + } + } + + /** Converts an office node as a complete text document + * + * @param onode the Office node containing the content to convert + */ + public void convertTextContent(Element onode) { + Element hnode = converter.nextOutFile(); + + // Create form + if (nSplit==0) { + Element form = getDrawCv().createForm(); + if (form!=null) { + hnode.appendChild(form); + hnode = form; + } + } + + // Convert content + traverseBlockText(onode,hnode); + + // Generate all indexes + int nIndexCount = indexes.size(); + for (int i=0; i<nIndexCount; i++) { + generateToc((IndexData) indexes.get(i)); + } + + // Generate navigation links + generateHeaders(); + generateFooters(); + generatePanels(); + } + + protected int getTocIndex() { return nTocFileIndex; } + + protected int getAlphabeticalIndex() { return nAlphabeticalIndex; } + + //////////////////////////////////////////////////////////////////////// + // NAVIGATION (fill header, footer and panel with navigation links) + //////////////////////////////////////////////////////////////////////// + + // The header is populated with prev/next navigation + private void generateHeaders() { } + + // The footer is populated with prev/next navigation + private void generateFooters() { } + + // The panel is populated with a minitoc + // TODO: Include link to toc and index in appropriate places.. + private void generatePanels() { + int nLastIndex = converter.getOutFileIndex(); + + bInToc = true; + + boolean bHasFrontMatter = false; + + TocEntry fakeEntry = new TocEntry(); + fakeEntry.nOutlineLevel = 0; + fakeEntry.nOutlineNumber = new int[11]; + + int nLen = tocEntries.size(); + + for (int nIndex=0; nIndex<=nLastIndex; nIndex++) { + converter.changeOutFile(nIndex); + Element panel = converter.getPanelNode(); + if (panel!=null) { + // Get the last heading of level <= split level for this file + TocEntry entryCurrent = null; + for (int i=nLen-1; i>=0; i--) { + TocEntry entry = (TocEntry) tocEntries.get(i); + if (XMLString.TEXT_H.equals(entry.onode.getTagName()) && entry.nFileIndex==nIndex && entry.nOutlineLevel<=nSplit) { + entryCurrent = entry; break; + } + } + + if (entryCurrent==null) { + entryCurrent = fakeEntry; + if (nIndex==0) { bHasFrontMatter=true; } + } + + // Determine the maximum outline level to include + int nMaxLevel = entryCurrent.nOutlineLevel; + if (nMaxLevel<nSplit) { nMaxLevel++; } + + // Create minitoc with relevant entries + if (bHasFrontMatter) { + Element inline = createPanelLink(panel, nIndex, 0, 1); + inline.appendChild(converter.createTextNode(converter.getL10n().get(L10n.HOME))); + } + + int nPrevFileIndex = 0; + for (int i=0; i<nLen; i++) { + TocEntry entry = (TocEntry) tocEntries.get(i); + + if (entry.nFileIndex>nPrevFileIndex+1) { + // Skipping a file index means we have passed an index + for (int k=nPrevFileIndex+1; k<entry.nFileIndex; k++) { + createIndexLink(panel,nIndex,k); + } + } + nPrevFileIndex = entry.nFileIndex; + + String sNodeName = entry.onode.getTagName(); + if (XMLString.TEXT_H.equals(sNodeName)) { + + // Determine wether or not to include this heading + // Note that this condition misses the case where + // a heading of level n is followed by a heading of + // level n+2. This is considered a bug in the document! + boolean bInclude = entry.nOutlineLevel<=nMaxLevel; + if (bInclude) { + // Check that this heading matches the current + int nCompareLevels = entry.nOutlineLevel; + for (int j=1; j<nCompareLevels; j++) { + if (entry.nOutlineNumber[j]!=entryCurrent.nOutlineNumber[j]) { + bInclude = false; + } + } + } + + if (bInclude) { + Element inline = createPanelLink(panel, nIndex, entry.nFileIndex, entry.nOutlineLevel); + + // Add content of heading + if (entry.sLabel!=null && entry.sLabel.length()>0) { + inline.appendChild(converter.createTextNode(entry.sLabel)); + if (!entry.sLabel.endsWith(" ")) { + inline.appendChild(converter.createTextNode(" ")); + } + } + traverseInlineText(entry.onode,inline); + } + } + } + if (nPrevFileIndex<nLastIndex) { + // Trailing index + for (int k=nPrevFileIndex+1; k<=nLastIndex; k++) { + createIndexLink(panel,nIndex,k); + } + } + } + } + + bInToc = false; + + converter.changeOutFile(nLastIndex); + + } + + private void createIndexLink(Element panel, int nIndex, int nFileIndex) { + if (nFileIndex==nTocFileIndex) { + Element inline = createPanelLink(panel, nIndex, nTocFileIndex, 1); + inline.appendChild(converter.createTextNode(converter.getL10n().get(L10n.CONTENTS))); + } + else if (nFileIndex==nAlphabeticalIndex) { + Element inline = createPanelLink(panel, nIndex, nAlphabeticalIndex, 1); + inline.appendChild(converter.createTextNode(converter.getL10n().get(L10n.INDEX))); + } + } + + private Element createPanelLink(Element panel, int nCurrentFile, int nLinkFile, int nOutlineLevel) { + // Create a link + Element p = converter.createElement("p"); + p.setAttribute("class","level"+nOutlineLevel); + panel.appendChild(p); + Element inline; + if (nCurrentFile!=nLinkFile) { + inline = converter.createElement("a"); + inline.setAttribute("href",converter.getOutFileName(nLinkFile,true)); + } + else { + inline = converter.createElement("span"); + inline.setAttribute("class","nolink"); + } + p.appendChild(inline); + return inline; + } + + //////////////////////////////////////////////////////////////////////// + // BLOCK TEXT (returns current html node at end of block) + //////////////////////////////////////////////////////////////////////// + + public Node traverseBlockText(Node onode, Node hnode) { + return traverseBlockText(onode,0,null,hnode); + } + + private Node traverseBlockText(Node onode, int nLevel, String styleName, Node hnode) { + if (!onode.hasChildNodes()) { return hnode; } + bAfterHeading = false; + NodeList nList = onode.getChildNodes(); + int nLen = nList.getLength(); + int i = 0; + while (i < nLen) { + Node child = nList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + // Block splitting + nDontSplitLevel++; + + if (OfficeReader.isDrawElement(child)) { + getDrawCv().handleDrawElement((Element)child,(Element)hnode,null,nFloatMode); + } + else if (nodeName.equals(XMLString.TEXT_P)) { + // is there a block element, we should use? + XhtmlStyleMap xpar = config.getXParStyleMap(); + String sDisplayName = ofr.getParStyles().getDisplayName(Misc.getAttribute(child,XMLString.TEXT_STYLE_NAME)); + + if (sDisplayName!=null && xpar.contains(sDisplayName)) { + Node curHnode = hnode; + String sBlockElement = xpar.getBlockElement(sDisplayName); + String sBlockCss = xpar.getBlockCss(sDisplayName); + if (xpar.getBlockElement(sDisplayName).length()>0) { + Element block = converter.createElement(xpar.getBlockElement(sDisplayName)); + if (!"(none)".equals(xpar.getBlockCss(sDisplayName))) { + block.setAttribute("class",xpar.getBlockCss(sDisplayName)); + } + hnode.appendChild(block); + curHnode = block; + } + boolean bMoreParagraphs = true; + do { + handleParagraph(child,curHnode); + bMoreParagraphs = false; + if (++i<nLen) { + child = nList.item(i); + String cnodeName = child.getNodeName(); + if (cnodeName.equals(XMLString.TEXT_P)) { + String sCurDisplayName = ofr.getParStyles().getDisplayName(Misc.getAttribute(child,XMLString.TEXT_STYLE_NAME)); + if (sCurDisplayName!=null && xpar.contains(sCurDisplayName)) { + if (sBlockElement.equals(xpar.getBlockElement(sCurDisplayName)) && + sBlockCss.equals(xpar.getBlockCss(sCurDisplayName))) { + bMoreParagraphs = true; + } + } + } + } + } while (bMoreParagraphs); + i--; + } + else { + handleParagraph(child,hnode); + } + } + else if(nodeName.equals(XMLString.TEXT_H)) { + int nOutlineLevel = getOutlineLevel((Element)child); + Node rememberNode = hnode; + hnode = maybeSplit(hnode,nOutlineLevel,bAfterHeading); + handleHeading(child,hnode,rememberNode!=hnode); + } + else if (nodeName.equals(XMLString.TEXT_LIST) || // oasis + nodeName.equals(XMLString.TEXT_UNORDERED_LIST) || // old + nodeName.equals(XMLString.TEXT_ORDERED_LIST)) // old + { + if (listIsOnlyHeadings(child)) { + nDontSplitLevel--; + hnode = handleFakeList(child,nLevel+1,styleName,hnode); + nDontSplitLevel++; + } + else { + handleList(child,nLevel+1,styleName,hnode); + } + } + else if (nodeName.equals(XMLString.TABLE_TABLE)) { + getTableCv().handleTable(child,hnode); + } + else if (nodeName.equals(XMLString.TABLE_SUB_TABLE)) { + getTableCv().handleTable(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_SECTION)) { + nDontSplitLevel--; + hnode = handleSection(child,hnode); + nDontSplitLevel++; + } + else if (nodeName.equals(XMLString.TEXT_TABLE_OF_CONTENT)) { + if (!ofr.getTocReader((Element)child).isByChapter()) { + hnode = maybeSplit(hnode,1,bAfterHeading); + } + handleTOC(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_ILLUSTRATION_INDEX)) { + handleLOF(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_TABLE_INDEX)) { + handleLOT(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_OBJECT_INDEX)) { + handleObjectIndex(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_USER_INDEX)) { + handleUserIndex(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_ALPHABETICAL_INDEX)) { + hnode = maybeSplit(hnode,1,bAfterHeading); + handleAlphabeticalIndex(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_BIBLIOGRAPHY)) { + hnode = maybeSplit(hnode,1,bAfterHeading); + handleBibliography(child,hnode); + } + else if (nodeName.equals(XMLString.OFFICE_ANNOTATION)) { + converter.handleOfficeAnnotation(child,hnode); + } + else if (nodeName.equals(XMLString.TEXT_SEQUENCE_DECLS)) { + //handleSeqeuenceDecls(child); + } + // Reenable splitting + nDontSplitLevel--; + // Remember if this was a heading + if (nDontSplitLevel==0) { + bAfterHeading = nodeName.equals(XMLString.TEXT_H); + } + } + i++; + } + return hnode; + } + + private Node maybeSplit(Node node, int nLevel, boolean bAfterHeading) { + if (nDontSplitLevel>1) { // we cannot split due to a nested structure + return node; + } + if (bAfterHeading && nLevel-nLastSplitLevel<=nRepeatLevels) { + // we cannot split because we are right after a heading and the + // maximum number of parent headings on the page is not reached + return node; + } + if (nSplit>=nLevel && converter.outFileHasContent()) { + // No objections, this is a level that causes splitting + return converter.nextOutFile(); + } + return node; + } + + /* Process a text:section tag (returns current html node) */ + private Node handleSection(Node onode, Node hnode) { + String sName = Misc.getAttribute(onode,XMLString.TEXT_NAME); + String sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + Element div = converter.createElement("div"); + hnode.appendChild(div); + converter.addTarget(div,sName+"|region"); + StyleInfo sectionInfo = new StyleInfo(); + getSectionSc().applyStyle(sStyleName,sectionInfo); + applyStyle(sectionInfo,div); + sections.push(onode); + Node newhnode = traverseBlockText(onode, div); + sections.pop(); + return newhnode.getParentNode(); + } + + private void handleHeading(Node onode, Node hnode, boolean bAfterSplit) { + int nListLevel = getOutlineLevel((Element)onode); + boolean bUnNumbered = "true".equals(Misc.getAttribute(onode,XMLString.TEXT_IS_LIST_HEADER)); + boolean bRestart = "true".equals(Misc.getAttribute(onode,XMLString.TEXT_RESTART_NUMBERING)); + int nStartValue = Misc.getPosInteger(Misc.getAttribute(onode,XMLString.TEXT_START_VALUE),1)-1; + handleHeading(onode, hnode, bAfterSplit, ofr.getOutlineStyle(), + nListLevel, bUnNumbered, bRestart, nStartValue); + } + + /* + * Process a text:h tag + */ + private void handleHeading(Node onode, Node hnode, boolean bAfterSplit, + ListStyle listStyle, int nListLevel, boolean bUnNumbered, + boolean bRestart, int nStartValue) { + // Note: nListLevel may in theory be different from the outline level, + // though the ui in OOo does not allow this + + // Numbering: It is possible to define outline numbering in CSS2 + // using counters; but this is not supported by Mozilla 1.0. + // TODO: Offer CSS2 solution as an alternative later. + + // Note: Conditional styles are not supported + int nLevel = getOutlineLevel((Element)onode); + if (nLevel<=6) { + if (nLevel==1) { currentChapter = (Element) onode; } + // If split output, add headings of higher levels + if (bAfterSplit && nSplit>0) { + int nFirst = nLevel-nRepeatLevels; + if (nFirst<0) { nFirst=0; } + for (int i=nFirst; i<nLevel; i++) { + if (currentHeading[i]!=null) { + hnode.appendChild(converter.importNode(currentHeading[i],true)); + } + } + } + + // add Hx element + Element heading = converter.createElement("h"+nLevel); + traverseFloats(onode,hnode,heading); + hnode.appendChild(heading); + // Apply writing direction + String sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style!=null) { + StyleInfo headInfo = new StyleInfo(); + StyleConverterHelper.applyDirection(style,headInfo); + getParSc().applyStyle(headInfo,heading); + } + + getParSc().setHeadingStyle(nLevel,sStyleName); + + // Prepend asapNode + prependAsapNode(heading); + + // Prepend numbering + ListCounter counter = getListCounter(listStyle); + if (bRestart) { counter.restart(nListLevel,nStartValue); } + String sLabel = counter.step(nListLevel).getLabel(); + if (!bUnNumbered && sLabel.length()>0) { + Element span = converter.createElement("span"); + StyleInfo info = new StyleInfo(); + info.sClass = "SectionNumber"; + if (listStyle!=null) { + String sTextStyleName = listStyle.getLevelProperty( + nListLevel,XMLString.TEXT_STYLE_NAME); + getTextSc().applyStyle(sTextStyleName, info); + } + getTextSc().applyStyle(info, span); + heading.appendChild(span); + span.appendChild( converter.createTextNode(sLabel) ); + } + + // Add to toc + if (!bInToc) { + converter.addTarget(heading,"toc"+(++nTocIndex)); + TocEntry entry = new TocEntry(); + entry.onode = (Element) onode; + entry.sLabel = sLabel; + entry.nFileIndex = converter.getOutFileIndex(); + entry.nOutlineLevel = nLevel; + entry.nOutlineNumber = naturalOutline.step(nLevel).getValues(); + tocEntries.add(entry); + } + + traverseInlineText(onode,heading); + + // Keep track of current headings for split output + currentHeading[nLevel] = heading; + for (int i=nLevel+1; i<=6; i++) { + currentHeading[i] = null; + } + } + else { // beyond h6 - export as ordinary paragraph + handleParagraph(onode,hnode); + } + } + + /* + * Process a text:p tag + */ + private void handleParagraph(Node onode, Node hnode) { + boolean bIsEmpty = OfficeReader.isWhitespaceContent(onode); + if (config.ignoreEmptyParagraphs() && bIsEmpty) { return; } + String sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + + Element par; + if (ofr.isSpreadsheet()) { // attach inline text directly to parent (always a table cell) + par = (Element) hnode; + } + else { + // Hack because createParagraph doesn't work the way we need here :-( + Element temp = converter.createElement("temp"); + par = createParagraph(temp, sStyleName); + prependAsapNode(par); + traverseFloats(onode,hnode,par); + hnode.appendChild(temp.getFirstChild()); + } + + // Maybe add to toc + if (ofr.isIndexSourceStyle(getParSc().getRealParStyleName(sStyleName))) { + converter.addTarget(par,"toc"+(++nTocIndex)); + TocEntry entry = new TocEntry(); + entry.onode = (Element) onode; + entry.sLabel = sCurrentListLabel; + entry.nFileIndex = converter.getOutFileIndex(); + tocEntries.add(entry); + } + sCurrentListLabel = null; + + if (!bIsEmpty) { + par = createTextBackground(par, sStyleName); + traverseInlineText(onode,par); + } + else { + // An empty paragraph (this includes paragraphs that only contains + // whitespace) is ignored by the browser, hence we add + par.appendChild( converter.createTextNode("\u00A0") ); + } + } + + private void prependAsapNode(Node node) { + if (asapNode!=null) { + // May float past a split; check this first + if (asapNode.getOwnerDocument()!=node.getOwnerDocument()) { + asapNode = converter.importNode(asapNode,true); + } + node.appendChild(asapNode); asapNode = null; + } + } + + + /////////////////////////////////////////////////////////////////////////// + // LISTS + /////////////////////////////////////////////////////////////////////////// + + // Helper: Get a list counter for a list style + private ListCounter getListCounter(ListStyle style) { + if (style==ofr.getOutlineStyle()) { + // Outline numbering has a special counter + return outlineNumbering; + } + else if (style!=null) { + // Get existing or create new counter + if (listCounters.containsKey(style.getName())) { + return (ListCounter) listCounters.get(style.getName()); + } + else { + ListCounter counter = new ListCounter(style); + listCounters.put(style.getName(),counter); + return counter; + } + } + else { + // No style, return a dummy + return new ListCounter(); + } + } + + // Helper: Check if a list contains any items + private boolean hasItems(Node onode) { + Node child = onode.getFirstChild(); + while (child!=null) { + if (Misc.isElement(child,XMLString.TEXT_LIST_ITEM) || + Misc.isElement(child,XMLString.TEXT_LIST_HEADER)) { + return true; + } + child = child.getNextSibling(); + } + return false; + } + + // TODO: Merge these three methods + + /* + * Process a text:ordered-list tag. + */ + private void handleOL (Node onode, int nLevel, String sStyleName, Node hnode) { + if (hasItems(onode)) { + // add an OL element + Element list = converter.createElement("ol"); + StyleInfo listInfo = new StyleInfo(); + getListSc().applyStyle(nLevel,sStyleName,listInfo); + applyStyle(listInfo,list); + hnode.appendChild(list); + traverseList(onode,nLevel,sStyleName,list); + } + } + + /* + * Process a text:unordered-list tag. + */ + private void handleUL (Node onode, int nLevel, String sStyleName, Node hnode) { + if (hasItems(onode)) { + // add an UL element + Element list = converter.createElement("ul"); + StyleInfo listInfo = new StyleInfo(); + getListSc().applyStyle(nLevel,sStyleName,listInfo); + applyStyle(listInfo,list); + hnode.appendChild(list); + traverseList(onode,nLevel,sStyleName,list); + } + } + + private void handleList(Node onode, int nLevel, String sStyleName, Node hnode) { + // In OpenDocument, we should use the style to determine the type of list + String sStyleName1 = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + if (sStyleName1!=null) { sStyleName = sStyleName1; } + ListStyle style = ofr.getListStyle(sStyleName); + if (style!=null && style.isNumber(nLevel)) { + handleOL(onode,nLevel,sStyleName,hnode); + } + else { + handleUL(onode,nLevel,sStyleName,hnode); + } + } + + /* + * Process the contents of a list (Changed as suggested by Nick Bower) + * The option xhtml_use_list_hack triggers some *invalid* code: + * - the attribute start on ol (is valid in html 4 transitional) + * - the attribute value on li (is valid in html 4 transitional) + * (these attributes are supposed to be replaced by css, but browsers + * generally don't support that) + * - generates <ol><ol><li>...</li></ol></ol> instead of + * <ol><li><ol><li>...</li></ol></li></ol> in case the first child of + * a list item is a new list. This occurs when a list is *continued* at + * level 2 or higher. This hack seems to be the only solution that + * actually produces correct results in browsers :-( + */ + private void traverseList (Node onode, int nLevel, String styleName, Element hnode) { + ListCounter counter = getListCounter(ofr.getListStyle(styleName)); + + // Restart numbering, if required + if (counter!=null) { + boolean bContinueNumbering = "true".equals(Misc.getAttribute(onode,XMLString.TEXT_CONTINUE_NUMBERING)); + if (bContinueNumbering) { + if (config.xhtmlUseListHack()) { + hnode.setAttribute("start",Integer.toString(counter.getValue(nLevel)+1)); + } + } + else if (counter!=null) { + counter.restart(nLevel); + } + } + + if (onode.hasChildNodes()) { + NodeList nList = onode.getChildNodes(); + int len = nList.getLength(); + + for (int i = 0; i < len; i++) { + Node child = nList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(XMLString.TEXT_LIST_ITEM)) { + // Check to see if first child is a new list + boolean bIsImmediateNestedList = false; + Element child1 = Misc.getFirstChildElement(child); + if (child1.getTagName().equals(XMLString.TEXT_ORDERED_LIST) || // old + child1.getTagName().equals(XMLString.TEXT_UNORDERED_LIST) || // old + child1.getTagName().equals(XMLString.TEXT_LIST)) { // oasis + bIsImmediateNestedList = true; + } + + if (config.xhtmlUseListHack() && bIsImmediateNestedList) { + traverseListItem(child,nLevel,styleName,hnode); + } + else { + // add an li element + sCurrentListLabel = counter.step(nLevel).getLabel(); + Element item = converter.createElement("li"); + StyleInfo info = new StyleInfo(); + getPresentationSc().applyOutlineStyle(nLevel,info); + applyStyle(info,item); + hnode.appendChild(item); + if (config.xhtmlUseListHack()) { + boolean bRestart = "true".equals(Misc.getAttribute(child, + XMLString.TEXT_RESTART_NUMBERING)); + int nStartValue = Misc.getPosInteger(Misc.getAttribute(child, + XMLString.TEXT_START_VALUE),1); + if (bRestart) { + item.setAttribute("value",Integer.toString(nStartValue)); + if (counter!=null) { + sCurrentListLabel = counter.restart(nLevel,nStartValue).getLabel(); + } + } + } + traverseListItem(child,nLevel,styleName,item); + } + } + if (nodeName.equals(XMLString.TEXT_LIST_HEADER)) { + // add an li element + Element item = converter.createElement("li"); + hnode.appendChild(item); + item.setAttribute("style","list-style-type:none"); + traverseListItem(child,nLevel,styleName,item); + } + } + } + } + } + + + /* + * Process the contents of a list item + * (a list header should only contain paragraphs, but we don't care) + */ + private void traverseListItem (Node onode, int nLevel, String styleName, Node hnode) { + // First check if we have a single paragraph to be omitted + // This should happen if we ignore styles and have no style-map + // for the paragraph style used + if (config.xhtmlFormatting()!=XhtmlConfig.CONVERT_ALL && onode.hasChildNodes()) { + NodeList list = onode.getChildNodes(); + int nLen = list.getLength(); + int nParCount = 0; + boolean bNoPTag = true; + for (int i=0; i<nLen; i++) { + if (list.item(i).getNodeType()==Node.ELEMENT_NODE) { + if (list.item(i).getNodeName().equals(XMLString.TEXT_P)) { + nParCount++; + if (bNoPTag) { + String sDisplayName = ofr.getParStyles().getDisplayName(Misc.getAttribute(list.item(0),XMLString.TEXT_STYLE_NAME)); + if (config.getXParStyleMap().contains(sDisplayName)) { + bNoPTag = false; + } + } + } + else { // found non-text:p element + bNoPTag=false; + } + } + } + if (bNoPTag && nParCount<=1) { + // traverse the list + for (int i = 0; i < nLen; i++) { + Node child = list.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(XMLString.TEXT_P)) { + traverseInlineText(child,hnode); + } + if (nodeName.equals(XMLString.TEXT_LIST)) { // oasis + handleList(child,nLevel+1,styleName,hnode); + } + if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) { // old + handleOL(child,nLevel+1,styleName,hnode); + } + if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) { // old + handleUL(child,nLevel+1,styleName,hnode); + } + } + } + return; + } + } + // Still here? - traverse block text as usual! + traverseBlockText(onode,nLevel,styleName,hnode); + } + + /////////////////////////////////////////////////////////////////////////// + // FAKE LISTS + /////////////////////////////////////////////////////////////////////////// + + // A fake list is a list which is converted into a sequence of numbered + // paragraphs rather than into a list. + // Currently this is done for list which only containsheadings + + // Helper: Check to see, if this list contains only headings + // (If so, we will ignore the list and apply the numbering to the headings) + private boolean listIsOnlyHeadings(Node node) { + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + if (nodeName.equals(XMLString.TEXT_LIST_ITEM)) { + if (!itemIsOnlyHeadings(child)) return false; + } + else if (nodeName.equals(XMLString.TEXT_LIST_HEADER)) { + if (!itemIsOnlyHeadings(child)) return false; + } + } + child = child.getNextSibling(); + } + return true; + } + + private boolean itemIsOnlyHeadings(Node node) { + Node child = node.getFirstChild(); + while (child!=null) { + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + if (nodeName.equals(XMLString.TEXT_LIST)) { + if (!listIsOnlyHeadings(child)) return false; + } + else if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) { + if (!listIsOnlyHeadings(child)) return false; + } + else if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) { + if (!listIsOnlyHeadings(child)) return false; + } + else if(!nodeName.equals(XMLString.TEXT_H)) { + return false; + } + } + child = child.getNextSibling(); + } + return true; + } + + // Splitting may occur inside a fake list, so we return the (new) hnode + private Node handleFakeList(Node onode, int nLevel, String sStyleName, Node hnode) { + String sStyleName1 = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + if (sStyleName1!=null) { sStyleName = sStyleName1; } + return traverseFakeList(onode,hnode,nLevel,sStyleName); + } + + // Traverse a list which is not exported as a list but as a sequence of + // numbered headings/paragraphs + private Node traverseFakeList (Node onode, Node hnode, int nLevel, String sStyleName) { + // Restart numbering? + boolean bContinueNumbering ="true".equals( + Misc.getAttribute(onode,XMLString.TEXT_CONTINUE_NUMBERING)); + if (!bContinueNumbering) { + getListCounter(ofr.getListStyle(sStyleName)).restart(nLevel); + } + + Node child = onode.getFirstChild(); + while (child!=null) { + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sNodeName = child.getNodeName(); + + if (sNodeName.equals(XMLString.TEXT_LIST_ITEM)) { + boolean bRestart = "true".equals(Misc.getAttribute(child, + XMLString.TEXT_RESTART_NUMBERING)); + int nStartValue = Misc.getPosInteger(Misc.getAttribute(child, + XMLString.TEXT_START_VALUE),1); + hnode = traverseFakeListItem(child, hnode, nLevel, sStyleName, false, bRestart, nStartValue); + } + else if (sNodeName.equals(XMLString.TEXT_LIST_HEADER)) { + hnode = traverseFakeListItem(child, hnode, nLevel, sStyleName, true, false, 0); + } + } + child = child.getNextSibling(); + } + return hnode; + } + + + // Process the contents of a fake list item + private Node traverseFakeListItem (Node onode, Node hnode, int nLevel, + String sStyleName, boolean bUnNumbered, boolean bRestart, int nStartValue) { + Node child = onode.getFirstChild(); + while (child!=null) { + if (child.getNodeType() == Node.ELEMENT_NODE) { + String sNodeName = child.getNodeName(); + + if (sNodeName.equals(XMLString.TEXT_H)) { + nDontSplitLevel++; + int nOutlineLevel = getOutlineLevel((Element)onode); + Node rememberNode = hnode; + hnode = maybeSplit(hnode,nOutlineLevel,bAfterHeading); + handleHeading(child, hnode, rememberNode!=hnode, + ofr.getListStyle(sStyleName), nLevel, + bUnNumbered, bRestart, nStartValue); + nDontSplitLevel--; + if (nDontSplitLevel==0) { bAfterHeading=true; } + } + else if (sNodeName.equals(XMLString.TEXT_P)) { + // Currently we only handle fakes lists containing headings + } + else if (sNodeName.equals(XMLString.TEXT_LIST)) { // oasis + return traverseFakeList(child, hnode, nLevel+1, sStyleName); + } + else if (sNodeName.equals(XMLString.TEXT_ORDERED_LIST)) { // old + return traverseFakeList(child, hnode, nLevel+1, sStyleName); + } + else if (sNodeName.equals(XMLString.TEXT_UNORDERED_LIST)) { // old + return traverseFakeList(child, hnode, nLevel+1, sStyleName); + } + } + child = child.getNextSibling(); + } + return hnode; + } + + + ////////////////////////////////////////////////////////////////////////// + // INDEXES + ////////////////////////////////////////////////////////////////////////// + + /* Process table of contents + */ + private void handleTOC(Node onode, Node hnode) { + if (!ofr.getTocReader((Element)onode).isByChapter()) { + nTocFileIndex = converter.getOutFileIndex(); + } + + Element div = converter.createElement("div"); + hnode.appendChild(div); + + IndexData data = new IndexData(); + data.nOutFileIndex = converter.getOutFileIndex(); + data.onode = (Element) onode; + data.chapter = currentChapter; + data.hnode = (Element) div; + indexes.add(data); // to be processed later with generateTOC + } + + private void generateToc(IndexData data) { + Element onode = data.onode; + Element chapter = data.chapter; + Element div = data.hnode; + + int nSaveOutFileIndex = converter.getOutFileIndex(); + converter.changeOutFile(data.nOutFileIndex); + + bInToc = true; + TocReader tocReader = ofr.getTocReader(onode); + + StyleInfo sectionInfo = new StyleInfo(); + getSectionSc().applyStyle(tocReader.getStyleName(),sectionInfo); + applyStyle(sectionInfo,div); + + if (tocReader.getName()!=null) { converter.addTarget(div,tocReader.getName()); } + // Generate title + Element title = tocReader.getIndexTitleTemplate(); + if (title!=null) { + String sStyleName = Misc.getAttribute(title,XMLString.TEXT_STYLE_NAME); + Element p = createParagraph(div,sStyleName); + traversePCDATA(title,p); + } + + // TODO: Read the entire content of the entry templates! + String[] sEntryStyleName = new String[11]; + for (int i=1; i<=10; i++) { + Element entryTemplate = tocReader.getTocEntryTemplate(i); + if (entryTemplate!=null) { + sEntryStyleName[i] = Misc.getAttribute(entryTemplate,XMLString.TEXT_STYLE_NAME); + } + } + + int nStart = 0; + int nLen = tocEntries.size(); + + // Find the chapter + if (tocReader.isByChapter() && chapter!=null) { + for (int i=0; i<nLen; i++) { + TocEntry entry = (TocEntry) tocEntries.get(i); + if (entry.onode==chapter) { nStart=i; break; } + } + + } + + // Generate entries + for (int i=nStart; i<nLen; i++) { + TocEntry entry = (TocEntry) tocEntries.get(i); + String sNodeName = entry.onode.getTagName(); + if (XMLString.TEXT_H.equals(sNodeName)) { + int nLevel = getOutlineLevel(entry.onode); + + if (nLevel==1 && tocReader.isByChapter() && entry.onode!=chapter) { break; } + if (tocReader.useOutlineLevel() && nLevel<=tocReader.getOutlineLevel()) { + Element p = createParagraph(div,sEntryStyleName[nLevel]); + if (entry.sLabel!=null) { + Element span = converter.createElement("span"); + p.appendChild(span); + span.setAttribute("class","SectionNumber"); + span.appendChild(converter.createTextNode(entry.sLabel)); + } + Element a = converter.createLink("toc"+i); + p.appendChild(a); + traverseInlineText(entry.onode,a); + } + else { + String sStyleName = getParSc().getRealParStyleName(entry.onode.getAttribute(XMLString.TEXT_STYLE_NAME)); + nLevel = tocReader.getIndexSourceStyleLevel(sStyleName); + if (tocReader.useIndexSourceStyles() && 1<=nLevel && nLevel<=tocReader.getOutlineLevel()) { + Element p = createParagraph(div,sEntryStyleName[nLevel]); + if (entry.sLabel!=null) { + p.appendChild(converter.createTextNode(entry.sLabel)); + } + Element a = converter.createLink("toc"+i); + p.appendChild(a); + traverseInlineText(entry.onode,a); + } + } + } + else if (XMLString.TEXT_P.equals(sNodeName)) { + String sStyleName = getParSc().getRealParStyleName(entry.onode.getAttribute(XMLString.TEXT_STYLE_NAME)); + int nLevel = tocReader.getIndexSourceStyleLevel(sStyleName); + if (tocReader.useIndexSourceStyles() && 1<=nLevel && nLevel<=tocReader.getOutlineLevel()) { + Element p = createParagraph(div,sEntryStyleName[nLevel]); + if (entry.sLabel!=null) { + p.appendChild(converter.createTextNode(entry.sLabel)); + } + Element a = converter.createLink("toc"+i); + p.appendChild(a); + traverseInlineText(entry.onode,a); + } + } + else if (XMLString.TEXT_TOC_MARK.equals(sNodeName)) { + int nLevel = Misc.getPosInteger(entry.onode.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1); + if (tocReader.useIndexMarks() && nLevel<=tocReader.getOutlineLevel()) { + Element p = createParagraph(div,sEntryStyleName[nLevel]); + Element a = converter.createLink("toc"+i); + p.appendChild(a); + a.appendChild(converter.createTextNode(IndexMark.getIndexValue(entry.onode))); + } + } + else if (XMLString.TEXT_TOC_MARK_START.equals(sNodeName)) { + int nLevel = Misc.getPosInteger(entry.onode.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1); + if (tocReader.useIndexMarks() && nLevel<=tocReader.getOutlineLevel()) { + Element p = createParagraph(div,sEntryStyleName[nLevel]); + Element a = converter.createLink("toc"+i); + p.appendChild(a); + a.appendChild(converter.createTextNode(IndexMark.getIndexValue(entry.onode))); + } + } + } + bInToc = false; + + converter.changeOutFile(nSaveOutFileIndex); + } + + /* + * Process list of illustrations + */ + private void handleLOF (Node onode, Node hnode) { + // later + } + + /* + * Process list of tables + */ + private void handleLOT (Node onode, Node hnode) { + // later + } + + /* + * Process Object index + */ + private void handleObjectIndex (Node onode, Node hnode) { + // later + } + + /* + * Process User index + */ + private void handleUserIndex (Node onode, Node hnode) { + // later + } + + /* + * Process Alphabetical index + */ + private void handleAlphabeticalIndex (Node onode, Node hnode) { + nAlphabeticalIndex = converter.getOutFileIndex(); + Node source = Misc.getChildByTagName(onode,XMLString.TEXT_ALPHABETICAL_INDEX_SOURCE); + if (source!=null) { + Element div = converter.createElement("div"); + converter.addTarget(div,"alphabeticalindex"); + hnode.appendChild(div); + // Generate title + Node title = Misc.getChildByTagName(source,XMLString.TEXT_INDEX_TITLE_TEMPLATE); + if (title!=null) { + String sStyleName = Misc.getAttribute(title,XMLString.TEXT_STYLE_NAME); + Element p = createParagraph(div,sStyleName); + traversePCDATA(title,p); + } + // Collect style name for entries + // TODO: Should read the entire template + String sEntryStyleName = null; + if (source.hasChildNodes()) { + NodeList nl = source.getChildNodes(); + int nLen = nl.getLength(); + for (int i = 0; i < nLen; i++) { + Node child = nl.item(i); + if (child.getNodeType() == Node.ELEMENT_NODE + && child.getNodeName().equals(XMLString.TEXT_ALPHABETICAL_INDEX_ENTRY_TEMPLATE)) { + // Note: There are actually three outline-levels: separator, 1, 2 and 3 + int nLevel = Misc.getPosInteger(Misc.getAttribute(child,XMLString.TEXT_OUTLINE_LEVEL),1); + if (nLevel==1) { + sEntryStyleName = Misc.getAttribute(child,XMLString.TEXT_STYLE_NAME); + } + } + } + } + // Sort the index entries + Collator collator; + String sLanguage = Misc.getAttribute(source,XMLString.FO_LANGUAGE); + if (sLanguage==null) { // use default locale + collator = Collator.getInstance(); + } + else { + String sCountry = Misc.getAttribute(source,XMLString.FO_COUNTRY); + if (sCountry==null) { sCountry=""; } + collator = Collator.getInstance(new Locale(sLanguage,sCountry)); + } + for (int i = 0; i<=nIndexIndex; i++) { + for (int j = i+1; j<=nIndexIndex ; j++) { + AlphabeticalEntry entryi = (AlphabeticalEntry) index.get(i); + AlphabeticalEntry entryj = (AlphabeticalEntry) index.get(j); + if (collator.compare(entryi.sWord, entryj.sWord) > 0) { + index.set(i,entryj); + index.set(j,entryi); + } + } + } + // Generate the index + Element table = converter.createElement("table"); + table.setAttribute("style","width:100%"); + div.appendChild(table); + Element tr = converter.createElement("tr"); + table.appendChild(tr); + Element[] td = new Element[4]; + for (int i=0; i<4; i++) { + td[i] = converter.createElement("td"); + td[i].setAttribute("style","vertical-align:top"); + tr.appendChild(td[i]); + } + int nColEntries = nIndexIndex/4+1; + int nColIndex = -1; + for (int i=0; i<=nIndexIndex; i++) { + if (i%nColEntries==0) { nColIndex++; } + AlphabeticalEntry entry = (AlphabeticalEntry) index.get(i); + Element p = createParagraph(td[nColIndex],sEntryStyleName); + Element a = converter.createLink("idx"+entry.nIndex); + p.appendChild(a); + a.appendChild(converter.createTextNode(entry.sWord)); + } + } + + } + + /* + * Process Bibliography + */ + private void handleBibliography (Node onode, Node hnode) { + // Use the content, not the template + // This is a temp. solution. Later we want to be able to create + // hyperlinks from the bib-item to the actual entry in the bibliography, + // so we have to recreate the bibliography from the template. + Node body = Misc.getChildByTagName(onode,XMLString.TEXT_INDEX_BODY); + if (body!=null) { + Element div = converter.createElement("div"); + converter.addTarget(div,"bibliography"); + hnode.appendChild(div); + //asapNode = converter.createTarget("bibliography"); + Node title = Misc.getChildByTagName(body,XMLString.TEXT_INDEX_TITLE); + if (title!=null) { traverseBlockText(title,div); } + traverseBlockText(body,div); + } + } + + //////////////////////////////////////////////////////////////////////// + // INLINE TEXT + //////////////////////////////////////////////////////////////////////// + + /* Process floating frames bound to this inline text (ie. paragraph) */ + private void traverseFloats(Node onode, Node hnodeBlock, Node hnodeInline) { + Node child = onode.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + Element elm = (Element) child; + String sTag = elm.getTagName(); + if (OfficeReader.isDrawElement(elm)) { + elm = getDrawCv().getRealDrawElement(elm); + if (elm!=null) { + String sAnchor = elm.getAttribute(XMLString.TEXT_ANCHOR_TYPE); + // Convert only floating frames; text-boxes must always float + if (!"as-char".equals(sAnchor)) { + getDrawCv().handleDrawElement(elm,(Element)hnodeBlock, + (Element)hnodeInline,nFloatMode); + } + else if (XMLString.DRAW_TEXT_BOX.equals(sTag)) { + getDrawCv().handleDrawElement(elm,(Element)hnodeBlock, + (Element)hnodeInline,DrawConverter.INLINE); + } + } + } + else if (OfficeReader.isTextElement(elm)) { + // Do not descend into {foot|end}notes + if (!OfficeReader.isNoteElement(elm)) { + traverseFloats(elm,hnodeBlock,hnodeInline); + } + } + } + child = child.getNextSibling(); + } + } + + /* + * Process inline text + */ + private void traverseInlineText (Node onode,Node hnode) { + //String styleName = Misc.getAttribute(onode, XMLString.TEXT_STYLE_NAME); + + if (onode.hasChildNodes()) { + NodeList nList = onode.getChildNodes(); + int nLen = nList.getLength(); + + for (int i = 0; i < nLen; i++) { + + Node child = nList.item(i); + short nodeType = child.getNodeType(); + + switch (nodeType) { + case Node.TEXT_NODE: + String s = child.getNodeValue(); + if (s.length() > 0) { + hnode.appendChild( converter.createTextNode(s) ); + } + break; + + case Node.ELEMENT_NODE: + String sName = child.getNodeName(); + if (OfficeReader.isDrawElement(child)) { + Element elm = getDrawCv().getRealDrawElement((Element)child); + if (elm!=null) { + String sAnchor = (elm.getAttribute(XMLString.TEXT_ANCHOR_TYPE)); + if ("as-char".equals(sAnchor)) { + getDrawCv().handleDrawElement(elm,null,(Element)hnode,DrawConverter.INLINE); + } + } + } + else if (child.getNodeName().equals(XMLString.TEXT_S)) { + if (config.ignoreDoubleSpaces()) { + hnode.appendChild( converter.createTextNode(" ") ); + } + else { + int count= Misc.getPosInteger(Misc.getAttribute(child,XMLString.TEXT_C),1); + for ( ; count > 0; count--) { + hnode.appendChild( converter.createTextNode("\u00A0") ); + } + } + } + else if (sName.equals(XMLString.TEXT_TAB_STOP)) { + handleTabStop(child,hnode); + } + else if (sName.equals(XMLString.TEXT_TAB)) { // oasis + handleTabStop(child,hnode); + } + else if (sName.equals(XMLString.TEXT_LINE_BREAK)) { + if (!config.ignoreHardLineBreaks()) { + hnode.appendChild( converter.createElement("br") ); + } + } + else if (sName.equals(XMLString.TEXT_SPAN)) { + handleSpan(child,hnode); + } + else if (sName.equals(XMLString.TEXT_A)) { + handleAnchor(child,hnode); + } + else if (sName.equals(XMLString.TEXT_FOOTNOTE)) { + handleFootnote(child,hnode); + } + else if (sName.equals(XMLString.TEXT_ENDNOTE)) { + handleEndnote(child,hnode); + } + else if (sName.equals(XMLString.TEXT_NOTE)) { // oasis + if ("endnote".equals(Misc.getAttribute(child,XMLString.TEXT_NOTE_CLASS))) { + handleEndnote(child,hnode); + } + else { + handleFootnote(child,hnode); + } + } + else if (sName.equals(XMLString.TEXT_SEQUENCE)) { + handleSequence(child,hnode); + } + else if (sName.equals(XMLString.TEXT_PAGE_NUMBER)) { + handlePageNumber(child,hnode); + } + else if (sName.equals(XMLString.TEXT_PAGE_COUNT)) { + handlePageCount(child,hnode); + } + else if (sName.equals(XMLString.TEXT_SEQUENCE_REF)) { + handleSequenceRef(child,hnode); + } + else if (sName.equals(XMLString.TEXT_FOOTNOTE_REF)) { + handleNoteRef(child,hnode); + } + else if (sName.equals(XMLString.TEXT_ENDNOTE_REF)) { + handleNoteRef(child,hnode); + } + else if (sName.equals(XMLString.TEXT_NOTE_REF)) { // oasis + handleNoteRef(child,hnode); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK)) { + handleReferenceMark(child,hnode); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_MARK_START)) { + handleReferenceMark(child,hnode); + } + else if (sName.equals(XMLString.TEXT_REFERENCE_REF)) { + handleReferenceRef(child,hnode); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK)) { + handleBookmark(child,hnode); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK_START)) { + handleBookmark(child,hnode); + } + else if (sName.equals(XMLString.TEXT_BOOKMARK_REF)) { + handleBookmarkRef(child,hnode); + } + else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK)) { + handleAlphabeticalIndexMark(child,hnode); + } + else if (sName.equals(XMLString.TEXT_ALPHABETICAL_INDEX_MARK_START)) { + handleAlphabeticalIndexMarkStart(child,hnode); + } + else if (sName.equals(XMLString.TEXT_TOC_MARK)) { + handleTocMark(child,hnode); + } + else if (sName.equals(XMLString.TEXT_TOC_MARK_START)) { + handleTocMark(child,hnode); + } + else if (sName.equals(XMLString.TEXT_BIBLIOGRAPHY_MARK)) { + handleBibliographyMark(child,hnode); + } + else if (sName.equals(XMLString.OFFICE_ANNOTATION)) { + converter.handleOfficeAnnotation(child,hnode); + } + else if (sName.startsWith("text:")) { + traverseInlineText(child,hnode); + } + // other tags are ignored; + break; + default: + // Do nothing + } + } + } + } + + private void handleTabStop(Node onode, Node hnode) { + // xhtml does not have tab stops, we export a space, which the + // user may choose to format + if (config.getXhtmlTabstopStyle().length()>0) { + Element span = converter.createElement("span"); + hnode.appendChild(span); + span.setAttribute("class",config.getXhtmlTabstopStyle()); + span.appendChild(converter.createTextNode(" ")); + } + else { + hnode.appendChild(converter.createTextNode(" ")); + } + } + + private void handleSpan(Node onode, Node hnode) { + String sStyleName = Misc.getAttribute(onode,XMLString.TEXT_STYLE_NAME); + Element span = createInline((Element) hnode,sStyleName); + traverseInlineText (onode,span); + } + + private void traversePCDATA(Node onode, Node hnode) { + if (onode.hasChildNodes()) { + NodeList nl = onode.getChildNodes(); + int nLen = nl.getLength(); + for (int i=0; i<nLen; i++) { + if (nl.item(i).getNodeType()==Node.TEXT_NODE) { + hnode.appendChild( converter.createTextNode(nl.item(i).getNodeValue()) ); + } + } + } + } + + protected void handleAnchor(Node onode, Node hnode) { + Element anchor = converter.createLink((Element)onode); + hnode.appendChild(anchor); + traverseInlineText(onode,anchor); + } + + /* Process a footnote */ + private void handleFootnote(Node onode, Node hnode) { + String sId = Misc.getAttribute(onode,XMLString.TEXT_ID); + Element span = createInline((Element) hnode,sFntCitBodyStyle); + // Create target and back-link + Element link = converter.createLink(sId); + converter.addTarget(link,"body"+sId); + span.appendChild(link); + Node citation = Misc.getChildByTagName(onode,XMLString.TEXT_FOOTNOTE_CITATION); + if (citation==null) { // try oasis + citation = Misc.getChildByTagName(onode,XMLString.TEXT_NOTE_CITATION); + } + traversePCDATA(citation,link); + footnotes.add(onode); + } + + public void insertFootnotes(Node hnode) { + int n = footnotes.size(); + for (int i=0; i<n; i++) { + Node footnote = (Node) footnotes.get(i); + String sId = Misc.getAttribute(footnote,XMLString.TEXT_ID); + Node citation = Misc.getChildByTagName(footnote,XMLString.TEXT_FOOTNOTE_CITATION); + if (citation==null) { // try oasis + citation = Misc.getChildByTagName(footnote,XMLString.TEXT_NOTE_CITATION); + } + Node body = Misc.getChildByTagName(footnote,XMLString.TEXT_FOOTNOTE_BODY); + if (body==null) { // try oasis + body = Misc.getChildByTagName(footnote,XMLString.TEXT_NOTE_BODY); + } + traverseNoteBody(sId,sFntCitStyle,citation,body,hnode); + } + footnotes.clear(); + } + + /* Process an endnote */ + private void handleEndnote(Node onode, Node hnode) { + String sId = Misc.getAttribute(onode,XMLString.TEXT_ID); + Element span = createInline((Element) hnode,sEntCitBodyStyle); + // Create target and back-link + Element link = converter.createLink(sId); + converter.addTarget(link,"body"+sId); + span.appendChild(link); + Node citation = Misc.getChildByTagName(onode,XMLString.TEXT_ENDNOTE_CITATION); + if (citation==null) { // try oasis + citation = Misc.getChildByTagName(onode,XMLString.TEXT_NOTE_CITATION); + } + traversePCDATA(citation,link); + endnotes.add(onode); + } + + public void insertEndnotes(Node hnode) { + int n = endnotes.size(); + if (nSplit>0 && n>0) { hnode = converter.nextOutFile(); } + for (int i=0; i<n; i++) { + Node endnote = (Node) endnotes.get(i); + String sId = Misc.getAttribute(endnote,XMLString.TEXT_ID); + Node citation = Misc.getChildByTagName(endnote,XMLString.TEXT_ENDNOTE_CITATION); + if (citation==null) { // try oasis + citation = Misc.getChildByTagName(endnote,XMLString.TEXT_NOTE_CITATION); + } + Node body = Misc.getChildByTagName(endnote,XMLString.TEXT_ENDNOTE_BODY); + if (body==null) { // try oasis + body = Misc.getChildByTagName(endnote,XMLString.TEXT_NOTE_BODY); + } + traverseNoteBody(sId,sEntCitStyle,citation,body,hnode); + } + } + + /* + * Process the contents of a footnote or endnote + */ + private void traverseNoteBody (String sId, String sCitStyle, Node citation,Node onode, Node hnode) { + // Create the anchor/footnote symbol: + // Create target and link + Element link = converter.createLink("body"+sId); + converter.addTarget(link,sId); + StyleInfo linkInfo = new StyleInfo(); + getTextSc().applyStyle(sCitStyle,linkInfo); + applyStyle(linkInfo,link); + traversePCDATA(citation,link); + // Add a space and save it for later insertion: + Element span = converter.createElement("span"); + span.appendChild(link); + span.appendChild(converter.createTextNode(" ")); + asapNode = span; + + traverseBlockText(onode,hnode); + + /*if (onode.hasChildNodes()) { + NodeList nList = onode.getChildNodes(); + int len = nList.getLength(); + + for (int i = 0; i < len; i++) { + Node child = nList.item(i); + + if (child.getNodeType() == Node.ELEMENT_NODE) { + String nodeName = child.getNodeName(); + + if (nodeName.equals(XMLString.TEXT_H)) { + handleHeading(child,hnode); + } + + if (nodeName.equals(XMLString.TEXT_P)) { + handleParagraph(child,hnode); + } + + if (nodeName.equals(XMLString.TEXT_ORDERED_LIST)) { + handleOL(child,0,null,hnode); + } + + if (nodeName.equals(XMLString.TEXT_UNORDERED_LIST)) { + handleUL(child,0,null,hnode); + } + } + } + }*/ + } + + private void handlePageNumber(Node onode, Node hnode) { + // doesn't make any sense... + hnode.appendChild( converter.createTextNode("(Page number)") ); + } + + private void handlePageCount(Node onode, Node hnode) { + // also no sense + hnode.appendChild( converter.createTextNode("(Page count)") ); + } + + private void handleSequence(Node onode, Node hnode) { + // Use current value, but turn references into hyperlinks + String sName = Misc.getAttribute(onode,XMLString.TEXT_REF_NAME); + if (sName!=null && !bInToc) { + Element anchor = converter.createTarget("seq"+sName); + hnode.appendChild(anchor); + traversePCDATA(onode,anchor); + } + else { + traversePCDATA(onode,hnode); + } + } + + private void createReference(Node onode, Node hnode, String sPrefix) { + // Turn reference into hyperlink + String sFormat = Misc.getAttribute(onode,XMLString.TEXT_REFERENCE_FORMAT); + String sName = Misc.getAttribute(onode,XMLString.TEXT_REF_NAME); + Element anchor = converter.createLink(sPrefix+sName); + hnode.appendChild(anchor); + if ("page".equals(sFormat)) { // all page numbers are 1 :-) + anchor.appendChild( converter.createTextNode("1") ); + } + else { // in other cases use current value + traversePCDATA(onode,anchor); + } + } + + private void handleSequenceRef(Node onode, Node hnode) { + createReference(onode,hnode,"seq"); + } + + private void handleNoteRef(Node onode, Node hnode) { + createReference(onode,hnode,""); + } + + private void handleReferenceMark(Node onode, Node hnode) { + String sName = Misc.getAttribute(onode,XMLString.TEXT_NAME); + if (sName!=null && !bInToc) { + hnode.appendChild(converter.createTarget("ref"+sName)); + } + } + + private void handleReferenceRef(Node onode, Node hnode) { + createReference(onode,hnode,"ref"); + } + + private void handleBookmark(Node onode, Node hnode) { + // Note: Two targets (may be the target of a hyperlink or a reference) + String sName = Misc.getAttribute(onode,XMLString.TEXT_NAME); + if (sName!=null && !bInToc) { + hnode.appendChild(converter.createTarget(sName)); + hnode.appendChild(converter.createTarget("bkm"+sName)); + } + } + + private void handleBookmarkRef(Node onode, Node hnode) { + createReference(onode,hnode,"bkm"); + } + + private void handleAlphabeticalIndexMark(Node onode, Node hnode) { + if (bInToc) { return; } + String sWord = Misc.getAttribute(onode,XMLString.TEXT_STRING_VALUE); + if (sWord==null) { return; } + AlphabeticalEntry entry = new AlphabeticalEntry(); + entry.sWord = sWord; entry.nIndex = ++nIndexIndex; + index.add(entry); + hnode.appendChild(converter.createTarget("idx"+nIndexIndex)); + } + + private void handleAlphabeticalIndexMarkStart(Node onode, Node hnode) { + if (bInToc) { return; } + String sWord = IndexMark.getIndexValue(onode); + if (sWord==null) { return; } + AlphabeticalEntry entry = new AlphabeticalEntry(); + entry.sWord = sWord; entry.nIndex = ++nIndexIndex; + index.add(entry); + hnode.appendChild(converter.createTarget("idx"+nIndexIndex)); + } + + private void handleTocMark(Node onode, Node hnode) { + hnode.appendChild(converter.createTarget("toc"+(++nTocIndex))); + TocEntry entry = new TocEntry(); + entry.onode = (Element) onode; + entry.nFileIndex = converter.getOutFileIndex(); + tocEntries.add(entry); + } + + private void handleBibliographyMark(Node onode, Node hnode) { + if (bInToc) { + traversePCDATA(onode,hnode); + } + else { + Element anchor = converter.createLink("bibliography"); + hnode.appendChild(anchor); + traversePCDATA(onode,anchor); + } + } + + /////////////////////////////////////////////////////////////////////////// + // UTILITY METHODS + /////////////////////////////////////////////////////////////////////////// + + /* apply hard formatting attribute style maps */ + private Element applyAttributes(Element node, StyleWithProperties style) { + // Do nothing if we convert hard formatting + if (config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_STYLES) { return node; } + // Do nothing if this is not an automatic style + if (style==null) { return node; } + if (!style.isAutomatic()) { return node; } + node = applyAttribute(node,"bold",getTextSc().isBold(style)); + node = applyAttribute(node,"italics",getTextSc().isItalics(style)); + node = applyAttribute(node,"fixed",getTextSc().isFixed(style)); + node = applyAttribute(node,"superscript",getTextSc().isSuperscript(style)); + node = applyAttribute(node,"subscript",getTextSc().isSubscript(style)); + return node; + } + + /* apply hard formatting attribute style maps */ + private Element applyAttribute(Element node, String sAttr, boolean bApply) { + if (!bApply) { return node; } + XhtmlStyleMap xattr = config.getXAttrStyleMap(); + if (!xattr.contains(sAttr)) { return node; } + Element attr = converter.createElement(xattr.getElement(sAttr)); + if (!"(none)".equals(xattr.getCss(sAttr))) { + attr.setAttribute("class",xattr.getCss(sAttr)); + } + node.appendChild(attr); + return attr; + } + + /* Create a styled paragraph node */ + private Element createParagraph(Element node, String sStyleName) { + StyleInfo info = new StyleInfo(); + getParSc().applyStyle(sStyleName,info); + Element par = converter.createElement(info.sTagName); + node.appendChild(par); + applyStyle(info,par); + StyleWithProperties style = ofr.getParStyle(sStyleName); + if (style!=null && style.isAutomatic()) { + return applyAttributes(par,style); + } + else { + return par; + } + } + + /* Create an inline node with background style from paragraph style */ + private Element createTextBackground(Element node, String sStyleName) { + if (config.xhtmlFormatting()==XhtmlConfig.IGNORE_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_HARD) { + return node; + } + String sBack = getParSc().getTextBackground(sStyleName); + if (sBack.length()>0) { + Element span = converter.createElement("span"); + span.setAttribute("style",sBack); + node.appendChild(span); + return span; + } + else { + return node; + } + } + + /* Create a styled inline node */ + private Element createInline(Element node, String sStyleName) { + StyleInfo info = new StyleInfo(); + getTextSc().applyStyle(sStyleName,info); + Element newNode = node; + if (info.hasAttributes() || !"span".equals(info.sTagName)) { + // We need to create a new element + newNode = converter.createElement(info.sTagName); + node.appendChild(newNode); + applyStyle(info,newNode); + } + return applyAttributes(newNode,ofr.getTextStyle(sStyleName)); + } + + private int getOutlineLevel(Element node) { + return ofr.isOpenDocument() ? + Misc.getPosInteger(node.getAttribute(XMLString.TEXT_OUTLINE_LEVEL),1): + Misc.getPosInteger(node.getAttribute(XMLString.TEXT_LEVEL),1); + } + + + +} + + diff --git a/source/java/writer2latex/xhtml/TextStyleConverter.java b/source/java/writer2latex/xhtml/TextStyleConverter.java new file mode 100644 index 0000000..0fdf153 --- /dev/null +++ b/source/java/writer2latex/xhtml/TextStyleConverter.java @@ -0,0 +1,432 @@ +/************************************************************************ + * + * TextStyleConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import java.util.Enumeration; +import java.util.Hashtable; + +import writer2latex.office.FontDeclaration; +import writer2latex.office.OfficeReader; +import writer2latex.office.OfficeStyleFamily; +import writer2latex.office.StyleWithProperties; +import writer2latex.office.XMLString; +import writer2latex.util.CSVList; +import writer2latex.util.ExportNameCollection; +import writer2latex.util.Misc; + +/** + * This class converts OpenDocument text styles to CSS2 styles. + * This includes conversion of text properties in other styles + * (paragraph, cell, graphic and presentation styles). + * <ul><li>TODO: Support CJK and CTL</li> + * <li>TODO: Support style:use-window-font-color ("automatic color")</li> + * <li>TODO: Support style:font-charset (other encoding)</li> + * <li>TODO: Support style:font-size-rel</li> + * <li>TODO: Support text:display and text:condition</li></ul> + + */ +public class TextStyleConverter extends StyleWithPropertiesConverterHelper { + + // OpenDocument does *not* define the style for links without style name, + // but OOo uses these styles, and so do we if they are available + // (Caveat: OOo does not export "Visited Internet Link" until a link is actually clicked) + private static final String DEFAULT_LINK_STYLE = "Internet link"; // Not "Link"! + private static final String DEFAULT_VISITED_LINK_STYLE = "Visited Internet Link"; + + // Bookkeeping for anchors + private ExportNameCollection anchorStyleNames = new ExportNameCollection(true); + private ExportNameCollection anchorVisitedStyleNames = new ExportNameCollection(true); + private Hashtable anchorCombinedStyleNames = new Hashtable(); + private Hashtable orgAnchorStyleNames = new Hashtable(); + private Hashtable orgAnchorVisitedStyleNames = new Hashtable(); + + /** Create a new <code>TextStyleConverter</code> + * @param ofr an <code>OfficeReader</code> to read style information from + * @param config the configuration to use + * @param converter the main <code>Converter</code> class + * @param nType the type of xhtml to use + */ + public TextStyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) { + super(ofr,config,converter,nType); + this.styleMap = config.getXTextStyleMap(); + this.bConvertStyles = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_HARD; + this.bConvertHard = config.xhtmlFormatting()==XhtmlConfig.CONVERT_ALL || config.xhtmlFormatting()==XhtmlConfig.IGNORE_STYLES; + } + + /** Apply a link style, using a combination of two text styles + * @param sStyleName name of the OpenDocument style + * @param sVisitedStyleName name of the OpenDocument style for visited links + * @param info the <code>StyleInfo</code> object to add information to + */ + public void applyAnchorStyle(String sStyleName, String sVisitedStyleName, + StyleInfo info) { + if (sStyleName==null || sVisitedStyleName==null) { return; } + if (sStyleName.length()==0 || sVisitedStyleName.length()==0) { return; } + // Look for a style map + String sDisplayName = ofr.getTextStyles().getDisplayName(sStyleName); + if (styleMap.contains(sDisplayName)) { // class name from config + if (!"(none)".equals(styleMap.getCss(sDisplayName))) { + info.sClass = styleMap.getCss(sDisplayName); + } + return; + } + + String sName = sStyleName+sVisitedStyleName; + if (!anchorCombinedStyleNames.containsKey(sName)) { + String sExportName; + // This combination is not seen before, but the base style may be known + // In that case, use the visited style name as well + if (anchorStyleNames.containsName(sStyleName)) { + sExportName = anchorStyleNames.getExportName(sStyleName) + +anchorVisitedStyleNames.getExportName(sVisitedStyleName); + } + else { + sExportName = anchorStyleNames.getExportName(sStyleName); + } + anchorCombinedStyleNames.put(sName,sExportName); + orgAnchorStyleNames.put(sExportName,sStyleName); + orgAnchorVisitedStyleNames.put(sExportName,sVisitedStyleName); + } + info.sClass = (String)anchorCombinedStyleNames.get(sName); + } + + /** <p>Convert style information for used styles</p> + * @param sIndent a String of spaces to add before each line + */ + public String getStyleDeclarations(String sIndent) { + StringBuffer buf = new StringBuffer(); + buf.append(super.getStyleDeclarations(sIndent)); + if (bConvertStyles) { + // Export anchor styles + // Default is always the styles "Internet link" and "Visited Internet Link"(?) + StyleWithProperties defaultLinkStyle = (StyleWithProperties) + getStyles().getStyleByDisplayName(DEFAULT_LINK_STYLE); + if (defaultLinkStyle!=null) { + CSVList props = new CSVList(";"); + cssText(defaultLinkStyle,props,true); + cssHyperlink(defaultLinkStyle,props); + buf.append(sIndent) + .append("a:link {").append(props.toString()).append("}\n"); + } + + defaultLinkStyle = (StyleWithProperties) + getStyles().getStyleByDisplayName(DEFAULT_VISITED_LINK_STYLE); + if (defaultLinkStyle!=null) { + CSVList props = new CSVList(";"); + cssText(defaultLinkStyle,props,true); + cssHyperlink(defaultLinkStyle,props); + buf.append(sIndent) + .append("a:visited {").append(props.toString()).append("}\n"); + } + + // Remaining link styles... + Enumeration enumer = anchorCombinedStyleNames.elements(); + while (enumer.hasMoreElements()) { + String sExportName = (String) enumer.nextElement(); + String sStyleName = (String) orgAnchorStyleNames.get(sExportName); + String sVisitedStyleName = (String) orgAnchorVisitedStyleNames.get(sExportName); + + StyleWithProperties style = ofr.getTextStyle(sStyleName); + + if (style!=null) { + CSVList props = new CSVList(";"); + cssText(style,props,true); + cssHyperlink(style,props); + buf.append(sIndent).append("a.").append(sExportName) + .append(":link {").append(props.toString()).append("}\n"); + } + + style = ofr.getTextStyle(sVisitedStyleName); + if (style!=null) { + CSVList props = new CSVList(";"); + cssText(style,props,true); + cssHyperlink(style,props); + buf.append(sIndent).append("a.").append(sExportName) + .append(":visited {").append(props.toString()).append("}\n"); + } + } + } + return buf.toString(); + + } + + /** Get the family of text (character) styles + * @return the style family + */ + public OfficeStyleFamily getStyles() { + return ofr.getTextStyles(); + } + + /** Create default tag name to represent a text + * @param style to use + * @return the tag name. + */ + public String getDefaultTagName(StyleWithProperties style) { + return "span"; + } + + /** Convert formatting properties for a specific text style. + * @param style the style to convert + * @param props the <code>CSVList</code> object to add information to + * @param bInherit true if properties should be inherited from parent style(s) + */ + public void applyProperties(StyleWithProperties style, CSVList props, boolean bInherit) { + cssText(style,props,bInherit); + } + + // Methods to query individual formatting properties (no inheritance) + + // Does this style contain the bold attribute? + public boolean isBold(StyleWithProperties style) { + String s = style.getProperty(XMLString.FO_FONT_WEIGHT,false); + return s!=null && "bold".equals(s); + } + + // Does this style contain the italics/oblique attribute? + public boolean isItalics(StyleWithProperties style) { + String s = style.getProperty(XMLString.FO_FONT_STYLE,false); + return s!=null && !"normal".equals(s); + } + + // Does this style contain a fixed pitch font? + public boolean isFixed(StyleWithProperties style) { + String s = style.getProperty(XMLString.STYLE_FONT_NAME,false); + String s2 = null; + String s3 = null; + if (s!=null) { + FontDeclaration fd = (FontDeclaration) ofr.getFontDeclarations().getStyle(s); + if (fd!=null) { + s2 = fd.getFontFamilyGeneric(); + s3 = fd.getFontPitch(); + } + } + else { + s = style.getProperty(XMLString.FO_FONT_FAMILY,false); + s2 = style.getProperty(XMLString.STYLE_FONT_FAMILY_GENERIC,false); + s3 = style.getProperty(XMLString.STYLE_FONT_PITCH,false); + } + if ("fixed".equals(s3)) { return true; } + if ("modern".equals(s2)) { return true; } + return false; + } + + // Does this style specify superscript? + public boolean isSuperscript(StyleWithProperties style) { + String sPos = style.getProperty(XMLString.STYLE_TEXT_POSITION,false); + if (sPos==null) return false; + if (sPos.startsWith("sub")) return false; + if (sPos.startsWith("-")) return false; + return true; + } + + // Does this style specify subscript? + public boolean isSubscript(StyleWithProperties style) { + String sPos = style.getProperty(XMLString.STYLE_TEXT_POSITION,false); + if (sPos==null) return false; + if (sPos.startsWith("sub")) return true; + if (sPos.startsWith("-")) return true; + return false; + } + + //////////////////////////////////////////////////////////////////////////// + // OpenDocument text properties + // Text properties can be applied to text, paragraph, cell, graphic and + // presentation styles. + // Language and country attributes are handled elsewhere + // The following attributes are currently not supported: + // - style:use-window-font-color ("automatic color") + // - style:font-charset (other encoding) + // - style:font-size-rel + // - text:display + // - text:condition + // Also all attributes for CJK and CTL text are currently ignored: + // style:font-name-*, style:font-family-*, style:font-family-generic-*, + // style:font-style-name-*, style:font-pitch-*, style:font-charset-*, + // style:font-size-*, style:font-size-rel-*, style:script-type + // The following attributes cannot be supported using CSS2: + // - style:text-outline + // - style:font-relief + // - style:text-line-trough-* (formatting of line through) + // - style:text-underline-* (formatting of underline) + // - style:letter-kerning + // - style:text-combine-* + // - style:text-emphasis + // - style:text-scale + // - style:text-rotation-* + // - fo:hyphenate + // - fo:hyphenation-* + // + + public void cssText(StyleWithProperties style, CSVList props, boolean bInherit) { + cssTextCommon(style,props,bInherit); + cssTextBackground(style,props,bInherit); + } + + public void cssTextCommon(StyleWithProperties style, CSVList props, boolean bInherit) { + String s=null,s2=null,s3=null,s4=null; + CSVList val; + + // Font family + if (bInherit || style.getProperty(XMLString.STYLE_FONT_NAME,false)!=null) { + val = new CSVList(","); // multivalue property! + // Get font family information from font declaration or from style + s = style.getProperty(XMLString.STYLE_FONT_NAME); + if (s!=null) { + FontDeclaration fd = (FontDeclaration) ofr.getFontDeclarations().getStyle(s); + if (fd!=null) { + s = fd.getFontFamily(); + s2 = fd.getFontFamilyGeneric(); + s3 = fd.getFontPitch(); + } + } + else { + s = style.getProperty(XMLString.FO_FONT_FAMILY); + s2 = style.getProperty(XMLString.STYLE_FONT_FAMILY_GENERIC); + s3 = style.getProperty(XMLString.STYLE_FONT_PITCH); + } + + // Add the western font family (CJK and CTL is more complicated) + if (s!=null) { val.addValue(s); } + // Add generic font family + if ("fixed".equals(s3)) { val.addValue("monospace"); } + else if ("roman".equals(s2)) { val.addValue("serif"); } + else if ("swiss".equals(s2)) { val.addValue("sans-serif"); } + else if ("modern".equals(s2)) { val.addValue("monospace"); } + else if ("decorative".equals(s2)) { val.addValue("fantasy"); } + else if ("script".equals(s2)) { val.addValue("cursive"); } + else if ("system".equals(s2)) { val.addValue("serif"); } // System default font + if (!val.isEmpty()) { props.addValue("font-family",val.toString()); } + } + + // Font style (italics): This property fit with css2 + s = style.getProperty(XMLString.FO_FONT_STYLE,bInherit); + if (s!=null) { props.addValue("font-style",s); } + + // Font variant (small caps): This property fit with css2 + s = style.getProperty(XMLString.FO_FONT_VARIANT,bInherit); + if (s!=null) { props.addValue("font-variant",s); } + + // Font weight (bold): This property fit with css2 + s = style.getProperty(XMLString.FO_FONT_WEIGHT,bInherit); + if (s!=null) { props.addValue("font-weight",s); } + + // Font size: Absolute values of this property fit with css2 + // this is handled together with sub- and superscripts (style:text-position) + // First value: sub, super or percentage (raise/lower relative to font height) + // Second value (optional): percentage (relative size); + if (bInherit || style.getProperty(XMLString.FO_FONT_SIZE,false)!=null + || style.getProperty(XMLString.STYLE_TEXT_POSITION,false)!=null) { + s = style.getAbsoluteProperty(XMLString.FO_FONT_SIZE); + s2 = style.getProperty(XMLString.STYLE_TEXT_POSITION); + if (s2!=null) { + s2 = s2.trim(); + int i = s2.indexOf(" "); + if (i>0) { // two values + s3 = s2.substring(0,i); + s4 = s2.substring(i+1); + } + else { // one value + s3 = s2; s4="100%"; + } + if (s!=null) { props.addValue("font-size",Misc.multiply(s4,scale(s))); } + else { props.addValue("font-size",s4); } + props.addValue("vertical-align",s3); + } + else if (s!=null) { + props.addValue("font-size",scale(s)); + } + } + + // Color: This attribute fit with css2 + s = style.getProperty(XMLString.FO_COLOR,bInherit); + if (s!=null) { props.addValue("color",s); } + + // Shadow: This attribute fit with css2 + // (Currently OOo has only one shadow style, which is saved as 1pt 1pt) + s = style.getProperty(XMLString.FO_TEXT_SHADOW,bInherit); + if (s!=null) { props.addValue("text-shadow",s); } + + // Text decoration. Here OOo is more flexible that CSS2. + if (ofr.isOpenDocument()) { + s = style.getProperty(XMLString.STYLE_TEXT_LINE_THROUGH_STYLE,bInherit); + s2 = style.getProperty(XMLString.STYLE_TEXT_UNDERLINE_STYLE,bInherit); + } + else { + s = style.getProperty(XMLString.STYLE_TEXT_CROSSING_OUT,bInherit); + s2 = style.getProperty(XMLString.STYLE_TEXT_UNDERLINE,bInherit); + } + s3 = style.getProperty(XMLString.STYLE_TEXT_BLINKING,bInherit); + // Issue: Since these three properties all maps to the single CSS property + // text-decoration, there is no way to turn on one kind of decoration and + // turn another one off (without creating another inline element). + // If one decoration is turned of, we turn them all off: + if ("none".equals(s) || "none".equals(s2) || "false".equals(s3)) { + props.addValue("text-decoration","none"); + } + else { // set the required properties + val = new CSVList(" "); // multivalue property! + if (s!=null && !"none".equals(s)) { val.addValue("line-through"); } + if (s2!=null && !"none".equals(s2)) { val.addValue("underline"); } + if (s3!=null && "true".equals(s3)) { val.addValue("blink"); } + if (!val.isEmpty()) { props.addValue("text-decoration",val.toString()); } + } + + // Letter spacing: This property fit with css + s = style.getProperty(XMLString.FO_LETTER_SPACING,bInherit); + if (s!=null) { props.addValue("letter-spacing",scale(s)); } + + // Capitalization: This property fit with css + s = style.getProperty(XMLString.FO_TEXT_TRANSFORM,bInherit); + if (s!=null) { props.addValue("text-transform",s); } + } + + public void cssTextBackground(StyleWithProperties style, CSVList props, boolean bInherit) { + // Background color: This attribute fit with css when applied to inline text + String s =ofr.isOpenDocument() ? + style.getTextProperty(XMLString.FO_BACKGROUND_COLOR,bInherit) : + style.getTextProperty(XMLString.STYLE_TEXT_BACKGROUND_COLOR,bInherit); + if (s!=null) { props.addValue("background-color",s); } + } + + private void cssHyperlink(StyleWithProperties style, CSVList props) { + String s1,s2; + // For hyperlinks, export text-decoration:none even if nothing is defined in source + if (ofr.isOpenDocument()) { + s1 = style.getProperty(XMLString.STYLE_TEXT_LINE_THROUGH_STYLE,true); + s2 = style.getProperty(XMLString.STYLE_TEXT_UNDERLINE_STYLE,true); + } + else { + s1 = style.getProperty(XMLString.STYLE_TEXT_CROSSING_OUT,true); + s2 = style.getProperty(XMLString.STYLE_TEXT_UNDERLINE,true); + } + String s3 = style.getProperty(XMLString.STYLE_TEXT_BLINKING,true); + if (s1==null && s2==null && s3==null) { + props.addValue("text-decoration","none"); + } + } + +} diff --git a/source/java/writer2latex/xhtml/Xhtml10Converter.java b/source/java/writer2latex/xhtml/Xhtml10Converter.java new file mode 100644 index 0000000..d9f9f5d --- /dev/null +++ b/source/java/writer2latex/xhtml/Xhtml10Converter.java @@ -0,0 +1,35 @@ +/************************************************************************ + * + * Xhtml10Converter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-09) + * + */ + +package writer2latex.xhtml; + +public class Xhtml10Converter extends Converter { + + public Xhtml10Converter() { + super(XhtmlDocument.XHTML10); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/xhtml/XhtmlConfig.java b/source/java/writer2latex/xhtml/XhtmlConfig.java new file mode 100644 index 0000000..2b93bad --- /dev/null +++ b/source/java/writer2latex/xhtml/XhtmlConfig.java @@ -0,0 +1,255 @@ +/************************************************************************ + * + * XhtmlConfig.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-14) + * + */ + +package writer2latex.xhtml; + +import java.util.Enumeration; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import writer2latex.base.BooleanOption; +import writer2latex.base.IntegerOption; +import writer2latex.base.Option; +import writer2latex.util.Misc; + +public class XhtmlConfig extends writer2latex.base.ConfigBase { + // Implement configuration methods + protected int getOptionCount() { return 36; } + protected String getDefaultConfigPath() { return "/writer2latex/xhtml/config/"; } + + // Override setOption: To be backwards compatible, we must accept options + // with the prefix xhtml_ + public void setOption(String sName,String sValue) { + if (sName.startsWith("xhtml_")) { sName = sName.substring(6); } + // this option has been renamed: + if (sName.equals("keep_image_size")) { sName = "original_image_size"; } + super.setOption(sName, sValue); + } + + // Formatting + public static final int IGNORE_ALL = 0; + public static final int IGNORE_STYLES = 1; + public static final int IGNORE_HARD = 2; + public static final int CONVERT_ALL = 3; + + // Options + private static final int IGNORE_HARD_LINE_BREAKS = 0; + private static final int IGNORE_EMPTY_PARAGRAPHS = 1; + private static final int IGNORE_DOUBLE_SPACES = 2; + private static final int ORIGINAL_IMAGE_SIZE = 3; + private static final int NO_DOCTYPE = 4; + private static final int ADD_BOM = 5; + private static final int ENCODING = 6; + private static final int USE_NAMED_ENTITIES = 7; + private static final int CUSTOM_STYLESHEET = 8; + private static final int FORMATTING = 9; + private static final int FRAME_FORMATTING = 10; + private static final int SECTION_FORMATTING = 11; + private static final int TABLE_FORMATTING = 12; + private static final int IGNORE_TABLE_DIMENSIONS = 13; + private static final int USE_DUBLIN_CORE = 14; + private static final int NOTES = 15; + private static final int CONVERT_TO_PX = 16; + private static final int SCALING = 17; + private static final int COLUMN_SCALING = 18; + private static final int FLOAT_OBJECTS = 19; + private static final int TABSTOP_STYLE = 20; + private static final int USE_LIST_HACK = 21; + private static final int SPLIT_LEVEL = 22; + private static final int REPEAT_LEVELS = 23; + private static final int CALC_SPLIT = 24; + private static final int DISPLAY_HIDDEN_SHEETS = 25; + private static final int DISPLAY_HIDDEN_ROWS_COLS = 26; + private static final int DISPLAY_FILTERED_ROWS_COLS = 27; + private static final int APPLY_PRINT_RANGES = 28; + private static final int USE_TITLE_AS_HEADING = 29; + private static final int USE_SHEET_NAMES_AS_HEADINGS = 30; + private static final int XSLT_PATH = 31; + private static final int SAVE_IMAGES_IN_SUBDIR = 32; + private static final int UPLINK = 33; + private static final int DIRECTORY_ICON = 34; + private static final int DOCUMENT_ICON = 35; + + protected XhtmlStyleMap xpar = new XhtmlStyleMap(); + protected XhtmlStyleMap xtext = new XhtmlStyleMap(); + protected XhtmlStyleMap xframe = new XhtmlStyleMap(); + protected XhtmlStyleMap xlist = new XhtmlStyleMap(); + protected XhtmlStyleMap xattr = new XhtmlStyleMap(); + + public XhtmlConfig() { + super(); + // create options with default values + options[IGNORE_HARD_LINE_BREAKS] = new BooleanOption("ignore_hard_line_breaks","false"); + options[IGNORE_EMPTY_PARAGRAPHS] = new BooleanOption("ignore_empty_paragraphs","false"); + options[IGNORE_DOUBLE_SPACES] = new BooleanOption("ignore_double_spaces","false"); + options[ORIGINAL_IMAGE_SIZE] = new BooleanOption("original_image_size","false"); + options[NO_DOCTYPE] = new BooleanOption("no_doctype","false"); + options[ADD_BOM] = new BooleanOption("add_bom","false"); + options[ENCODING] = new Option("encoding","UTF-8"); + options[USE_NAMED_ENTITIES] = new BooleanOption("use_named_entities","false"); + options[CUSTOM_STYLESHEET] = new Option("custom_stylesheet",""); + options[FORMATTING] = new XhtmlFormatOption("formatting","convert_all"); + options[FRAME_FORMATTING] = new XhtmlFormatOption("frame_formatting","convert_all"); + options[SECTION_FORMATTING] = new XhtmlFormatOption("section_formatting","convert_all"); + options[TABLE_FORMATTING] = new XhtmlFormatOption("table_formatting","convert_all"); + options[IGNORE_TABLE_DIMENSIONS] = new BooleanOption("ignore_table_dimensions","false"); + options[USE_DUBLIN_CORE] = new BooleanOption("use_dublin_core","true"); + options[NOTES] = new BooleanOption("notes","true"); + options[CONVERT_TO_PX] = new BooleanOption("convert_to_px","true"); + options[SCALING] = new Option("scaling","100%"); + options[COLUMN_SCALING] = new Option("column_scaling","100%"); + options[FLOAT_OBJECTS] = new BooleanOption("float_objects","true"); + options[TABSTOP_STYLE] = new Option("tabstop_style",""); + options[USE_LIST_HACK] = new BooleanOption("use_list_hack","false"); + options[SPLIT_LEVEL] = new IntegerOption("split_level","0") { + public void setString(String sValue) { + super.setString(sValue); + nValue = Misc.getPosInteger(sValue,0); + } + }; + options[REPEAT_LEVELS] = new IntegerOption("repeat_levels","5") { + public void setString(String sValue) { + super.setString(sValue); + nValue = Misc.getPosInteger(sValue,0); + } + }; + options[CALC_SPLIT] = new BooleanOption("calc_split","false"); + options[DISPLAY_HIDDEN_SHEETS] = new BooleanOption("display_hidden_sheets","false"); + options[DISPLAY_HIDDEN_ROWS_COLS] = new BooleanOption("display_hidden_rows_cols","false"); + options[DISPLAY_FILTERED_ROWS_COLS] = new BooleanOption("display_filtered_rows_cols","false"); + options[APPLY_PRINT_RANGES] = new BooleanOption("apply_print_ranges","false"); + options[USE_TITLE_AS_HEADING] = new BooleanOption("use_title_as_heading","true"); + options[USE_SHEET_NAMES_AS_HEADINGS] = new BooleanOption("use_sheet_names_as_headings","true"); + options[XSLT_PATH] = new Option("xslt_path",""); + options[SAVE_IMAGES_IN_SUBDIR] = new BooleanOption("save_images_in_subdir","false"); + options[UPLINK] = new Option("uplink",""); + options[DIRECTORY_ICON] = new Option("directory_icon",""); + options[DOCUMENT_ICON] = new Option("document_icon",""); + } + + protected void readInner(Element elm) { + if (elm.getTagName().equals("xhtml-style-map")) { + String sName = elm.getAttribute("name"); + String sFamily = elm.getAttribute("family"); + if (sFamily.length()==0) { // try old name + sFamily = elm.getAttribute("class"); + } + String sBlockElement = elm.getAttribute("block-element"); + String sBlockCss = elm.getAttribute("block-css"); + if (sBlockCss.length()==0) { sBlockCss="(none)"; } + String sElement = elm.getAttribute("element"); + String sCss = elm.getAttribute("css"); + if (sCss.length()==0) { sCss="(none)"; } + if ("paragraph".equals(sFamily)) { + xpar.put(sName,sBlockElement,sBlockCss,sElement,sCss); + } + else if ("text".equals(sFamily)) { + xtext.put(sName,sBlockElement,sBlockCss,sElement,sCss); + } + else if ("frame".equals(sFamily)) { + xframe.put(sName,sBlockElement,sBlockCss,sElement,sCss); + } + else if ("list".equals(sFamily)) { + xlist.put(sName,sBlockElement,sBlockCss,sElement,sCss); + } + else if ("attribute".equals(sFamily)) { + xattr.put(sName,sBlockElement,sBlockCss,sElement,sCss); + } + } + } + + protected void writeInner(Document dom) { + writeXStyleMap(dom,xpar,"paragraph"); + writeXStyleMap(dom,xtext,"text"); + writeXStyleMap(dom,xlist,"list"); + writeXStyleMap(dom,xframe,"frame"); + writeXStyleMap(dom,xframe,"attribute"); + } + + private void writeXStyleMap(Document dom, XhtmlStyleMap sm, String sFamily) { + Enumeration smEnum = sm.getNames(); + while (smEnum.hasMoreElements()) { + String sName = (String) smEnum.nextElement(); + Element smNode = dom.createElement("xhtml-style-map"); + smNode.setAttribute("name",sName); + smNode.setAttribute("family",sFamily); + smNode.setAttribute("element",sm.getElement(sName)); + smNode.setAttribute("css",sm.getCss(sName)); + String sBlockElement = sm.getBlockElement(sName); + if (sBlockElement!=null) { smNode.setAttribute("block-element",sm.getCss(sBlockElement)); } + String sBlockCss = sm.getBlockCss(sName); + if (sBlockCss!=null) { smNode.setAttribute("block-css",sm.getCss(sBlockCss)); } + dom.getDocumentElement().appendChild(smNode); + } + } + + // Convenience accessor methods + public boolean ignoreHardLineBreaks() { return ((BooleanOption) options[IGNORE_HARD_LINE_BREAKS]).getValue(); } + public boolean ignoreEmptyParagraphs() { return ((BooleanOption) options[IGNORE_EMPTY_PARAGRAPHS]).getValue(); } + public boolean ignoreDoubleSpaces() { return ((BooleanOption) options[IGNORE_DOUBLE_SPACES]).getValue(); } + public boolean originalImageSize() { return ((BooleanOption) options[ORIGINAL_IMAGE_SIZE]).getValue(); } + public boolean xhtmlNoDoctype() { return ((BooleanOption) options[NO_DOCTYPE]).getValue(); } + public boolean xhtmlAddBOM() { return ((BooleanOption) options[ADD_BOM]).getValue(); } + public String xhtmlEncoding() { return options[ENCODING].getString(); } + public boolean useNamedEntities() { return ((BooleanOption) options[USE_NAMED_ENTITIES]).getValue(); } + public String xhtmlCustomStylesheet() { return options[CUSTOM_STYLESHEET].getString(); } + public int xhtmlFormatting() { return ((XhtmlFormatOption) options[FORMATTING]).getValue(); } + public int xhtmlFrameFormatting() { return ((XhtmlFormatOption) options[FRAME_FORMATTING]).getValue(); } + public int xhtmlSectionFormatting() { return ((XhtmlFormatOption) options[SECTION_FORMATTING]).getValue(); } + public int xhtmlTableFormatting() { return ((XhtmlFormatOption) options[TABLE_FORMATTING]).getValue(); } + public boolean xhtmlIgnoreTableDimensions() { return ((BooleanOption) options[IGNORE_TABLE_DIMENSIONS]).getValue(); } + public boolean xhtmlUseDublinCore() { return ((BooleanOption) options[USE_DUBLIN_CORE]).getValue(); } + public boolean xhtmlNotes() { return ((BooleanOption) options[NOTES]).getValue(); } + public boolean xhtmlConvertToPx() { return ((BooleanOption) options[CONVERT_TO_PX]).getValue(); } + public String getXhtmlScaling() { return options[SCALING].getString(); } + public String getXhtmlColumnScaling() { return options[COLUMN_SCALING].getString(); } + public boolean xhtmlFloatObjects() { return ((BooleanOption) options[FLOAT_OBJECTS]).getValue(); } + public String getXhtmlTabstopStyle() { return options[TABSTOP_STYLE].getString(); } + public boolean xhtmlUseListHack() { return ((BooleanOption) options[USE_LIST_HACK]).getValue(); } + public int getXhtmlSplitLevel() { return ((IntegerOption) options[SPLIT_LEVEL]).getValue(); } + public int getXhtmlRepeatLevels() { return ((IntegerOption) options[REPEAT_LEVELS]).getValue(); } + public boolean xhtmlCalcSplit() { return ((BooleanOption) options[CALC_SPLIT]).getValue(); } + public boolean xhtmlDisplayHiddenSheets() { return ((BooleanOption) options[DISPLAY_HIDDEN_SHEETS]).getValue(); } + public boolean displayHiddenRowsCols() { return ((BooleanOption) options[DISPLAY_HIDDEN_ROWS_COLS]).getValue(); } + public boolean displayFilteredRowsCols() { return ((BooleanOption) options[DISPLAY_FILTERED_ROWS_COLS]).getValue(); } + public boolean applyPrintRanges() { return ((BooleanOption) options[APPLY_PRINT_RANGES]).getValue(); } + public boolean xhtmlUseTitleAsHeading() { return ((BooleanOption) options[USE_TITLE_AS_HEADING]).getValue(); } + public boolean xhtmlUseSheetNamesAsHeadings() { return ((BooleanOption) options[USE_SHEET_NAMES_AS_HEADINGS]).getValue(); } + public String getXsltPath() { return options[XSLT_PATH].getString(); } + public boolean saveImagesInSubdir() { return ((BooleanOption) options[SAVE_IMAGES_IN_SUBDIR]).getValue(); } + public String getXhtmlUplink() { return options[UPLINK].getString(); } + public String getXhtmlDirectoryIcon() { return options[DIRECTORY_ICON].getString(); } + public String getXhtmlDocumentIcon() { return options[DOCUMENT_ICON].getString(); } + + public XhtmlStyleMap getXParStyleMap() { return xpar; } + public XhtmlStyleMap getXTextStyleMap() { return xtext; } + public XhtmlStyleMap getXFrameStyleMap() { return xframe; } + public XhtmlStyleMap getXListStyleMap() { return xlist; } + public XhtmlStyleMap getXAttrStyleMap() { return xattr; } + +} + diff --git a/source/java/writer2latex/xhtml/XhtmlDocument.java b/source/java/writer2latex/xhtml/XhtmlDocument.java new file mode 100644 index 0000000..9717b98 --- /dev/null +++ b/source/java/writer2latex/xhtml/XhtmlDocument.java @@ -0,0 +1,782 @@ +/************************************************************************ + * + * XhtmlDocument.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2009 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2009-02-19) + * + */ + + //TODO: Remove redundant lang and dir attributes + //TODO: Add named entities outside ISO-latin 1 + +package writer2latex.xhtml; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +//import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentType; +import org.w3c.dom.DOMImplementation; +//import org.xml.sax.SAXException; +//import org.xml.sax.SAXParseException; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +//import javax.xml.parsers.ParserConfigurationException; + +import writer2latex.xmerge.DOMDocument; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.IOException; + +/** + * An implementation of <code>Document</code> for + * XHTML documents. + */ +public class XhtmlDocument extends DOMDocument { + + /** Constant to identify XHTML 1.0 strict documents */ + public static final int XHTML10 = 0; + + /** Constant to identify XHTML 1.1 documents (not used currently) */ + public static final int XHTML11 = 1; + + /** Constant to identify XHTML + MathML documents */ + public static final int XHTML_MATHML = 2; + + /** Constant to identify XHTML + MathML documents using the xsl transformations + * from w3c's math working group (http://www.w3.org/Math/XSL/) + */ + public static final int XHTML_MATHML_XSL = 3; + + private static final String[] sExtension = { ".html", ".html", ".xhtml", ".xml" }; + + private static final String[] sEmpty = { "base", "meta", "link", "hr", "br", "param", "img", "area", "input", "col" }; + + private static String[] entities; // Not convenient to define directly due to a lot of null values + + // Type of document + private int nType; + + // Configuration + private String sEncoding = "UTF-8"; + private boolean bUseNamedEntities = false; + private char cLimit = 65535; + private boolean bNoDoctype = false; + private boolean bAddBOM = false; + private String sXsltPath = ""; + + // Content + private Element headNode = null; + private Element bodyNode = null; + private Element titleNode = null; + private Element contentNode = null; + private Element panelNode = null; + private Element headerNode = null; + private Element footerNode = null; + + public static final String getExtension(int nType) { + return sExtension[nType]; + } + + /** + * Constructor. This constructor also creates the DOM (minimal: root, head, + * title and body node only) - unlike the constructors in + * writer2latex.xmerge.DOMDocument. + * @param name <code>Document</code> name. + * @param nType the type of document + */ + public XhtmlDocument(String name, int nType) { + super(name,sExtension[nType]); + this.nType = nType; + // Define publicId and systemId + String sPublicId = null; + String sSystemId = null; + switch (nType) { + case XHTML10 : + sPublicId = "-//W3C//DTD XHTML 1.0 Strict//EN"; + sSystemId = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; + break; + case XHTML11 : + sPublicId = "-//W3C//DTD XHTML 1.1//EN"; + sSystemId = "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"; + break; + case XHTML_MATHML : + case XHTML_MATHML_XSL : + sPublicId = "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"; + sSystemId = "http://www.w3.org/Math/DTD/mathml2/xhtml-math11-f.dtd"; + //sSystemId = "http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd"; (old version) + /* An alternative is to use XHTML + MathML + SVG: + sPublicId = "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN", + sSystemId = "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd"); */ + } + + // create DOM + Document contentDOM = null; + try { + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder = builderFactory.newDocumentBuilder(); + DOMImplementation domImpl = builder.getDOMImplementation(); + DocumentType doctype = domImpl.createDocumentType("html", sPublicId, sSystemId); + contentDOM = domImpl.createDocument("http://www.w3.org/1999/xhtml","html",doctype); + } + catch (Throwable t) { + t.printStackTrace(); + } + contentDOM.getDocumentElement().setAttribute("xmlns","http://www.w3.org/1999/xhtml"); + // add head, title and body + headNode = contentDOM.createElement("head"); + titleNode = contentDOM.createElement("title"); + bodyNode = contentDOM.createElement("body"); + contentDOM.getDocumentElement().appendChild(headNode); + headNode.appendChild(titleNode); + contentDOM.getDocumentElement().appendChild(bodyNode); + contentNode = bodyNode; + setContentDOM(contentDOM); + + } + + public Element getHeadNode() { return headNode; } + + public Element getBodyNode() { return bodyNode; } + + public Element getTitleNode() { return titleNode; } + + public Element getContentNode() { return contentNode; } + + public void setContentNode(Element contentNode) { this.contentNode = contentNode; } + + public Element getPanelNode() { return panelNode; } + + public Element getHeaderNode() { return headerNode; } + + public Element getFooterNode() { return footerNode; } + + public void createHeaderFooter() { + headerNode = getContentDOM().createElement("div"); + headerNode.setAttribute("id","header"); + bodyNode.appendChild(headerNode); + contentNode = getContentDOM().createElement("div"); + contentNode.setAttribute("id","content"); + bodyNode.appendChild(contentNode); + footerNode = getContentDOM().createElement("div"); + footerNode.setAttribute("id","footer"); + bodyNode.appendChild(footerNode); + } + + public void setContentDOM(Document doc) { + super.setContentDOM(doc); + collectNodes(); + } + + public void read(InputStream is) throws IOException { + super.read(is); + collectNodes(); + } + + public void readFromTemplate(XhtmlDocument template) { + // Remove all current child nodes + Element root = getContentDOM().getDocumentElement(); + Node child = root.getFirstChild(); + while (child!=null) { + root.removeChild(child); + child = root.getFirstChild(); + } + + // Import all child nodes from template + Element templateRoot = template.getContentDOM().getDocumentElement(); + NodeList children = templateRoot.getChildNodes(); + int nLen = children.getLength(); + for (int i=0; i<nLen; i++) { + root.appendChild(getContentDOM().importNode(children.item(i),true)); + } + + // get the entry point nodes + collectNodes(); + } + + private void collectNodes(Element elm) { + String sTagName = elm.getTagName(); + if ("head".equals(sTagName)) { + headNode = elm; + } + else if ("body".equals(sTagName)) { + bodyNode = elm; + } + else if ("title".equals(sTagName)) { + titleNode = elm; + } + else if ("div".equals(sTagName)) { + String sId = elm.getAttribute("id"); + if ("content".equals(sId)) { contentNode = elm; } + else if ("header".equals(sId)) { headerNode = elm; } + else if ("footer".equals(sId)) { footerNode = elm; } + else if ("panel".equals(sId)) { panelNode = elm; } + } + + Node child = elm.getFirstChild(); + while (child!=null) { + if (child.getNodeType()==Node.ELEMENT_NODE) { + collectNodes((Element)child); + } + child = child.getNextSibling(); + } + } + + private void collectNodes() { + headNode = null; + bodyNode = null; + titleNode = null; + contentNode = null; + headerNode = null; + footerNode = null; + panelNode = null; + + Element elm = getContentDOM().getDocumentElement(); + collectNodes(elm); + if (contentNode==null) { contentNode = bodyNode; } + if (titleNode==null) { + titleNode = getContentDOM().createElement("title"); + headNode.appendChild(titleNode); + } + } + + public void setEncoding(String s) { + s = s.toUpperCase(); + if ("UTF-16".equals(s)) { + sEncoding = s; + cLimit = 65535; + } + else if ("ISO-8859-1".equals(s)) { + sEncoding = s; + cLimit = 255; + } + else if ("US-ASCII".equals(s)) { + sEncoding = s; + cLimit = 127; + } + else { + sEncoding = "UTF-8"; + cLimit = 65535; + } + } + + public String getEncoding() { return sEncoding; } + + public void setNoDoctype(boolean b) { bNoDoctype = b; } + + public void setAddBOM(boolean b) { bAddBOM = b; } + + public void setUseNamedEntities(boolean b) { + bUseNamedEntities = b; + } + + public void setXsltPath(String s) { sXsltPath = s; } + + public String getFileExtension() { return super.getFileExtension(); } + + /** + * Write out content to the supplied <code>OutputStream</code>. + * (with pretty printing) + * @param os XML <code>OutputStream</code>. + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + // Prepare named entities + prepareEntities(); + + OutputStreamWriter osw = new OutputStreamWriter(os,sEncoding); + // Add a BOM if the user desires so + if (bAddBOM) { osw.write("\uFEFF"); } + + // Omit xml prolog for pure xhtml documents (to be browser safe) + if (nType==XHTML_MATHML || nType==XHTML_MATHML_XSL) { + osw.write("<?xml version=\"1.0\" encoding=\""+sEncoding+"\" ?>\n"); + } + // Either specify doctype or xsl transformation (the user may require + // that no doctype is used; this may be desirable for further transformations) + if (nType==XHTML_MATHML_XSL) { + // Original url: http://www.w3.org/Math/XSL/pmathml.xsl + // Add trailing slash if needed + String sSlash = sXsltPath.length()>0 && !sXsltPath.endsWith("/") ? "/" : ""; + osw.write("<?xml-stylesheet type=\"text/xsl\" href=\""+sXsltPath+sSlash+"pmathml.xsl\"?>\n"); + } + else if (!bNoDoctype) { + osw.write("<!DOCTYPE html PUBLIC \""); + osw.write(getContentDOM().getDoctype().getPublicId()); + osw.write("\" \""); + osw.write(getContentDOM().getDoctype().getSystemId()); + osw.write("\">\n"); + } + write(getContentDOM().getDocumentElement(),0,osw); + osw.flush(); + osw.close(); + } + + private boolean isEmpty(String sTagName) { + for (int i=0; i<sEmpty.length; i++) { + if (sEmpty[i].equals(sTagName)) { return true; } + } + return false; + } + + // Write nodes; we only need element, text and comment nodes + private void write(Node node, int nLevel, OutputStreamWriter osw) throws IOException { + short nType = node.getNodeType(); + switch (nType) { + case Node.ELEMENT_NODE: + if (isEmpty(node.getNodeName())) { + // This node must be empty, we ignore childnodes + if (nLevel>=0) { writeSpaces(nLevel,osw); } + osw.write("<"+node.getNodeName()); + writeAttributes(node,osw); + osw.write(" />"); + if (nLevel>=0) { osw.write("\n"); } + } + else if (node.hasChildNodes()) { + // Block pretty print from this node? + NodeList list = node.getChildNodes(); + int nLen = list.getLength(); + boolean bBlockPrettyPrint = false; + if (nLevel>=0) { + for (int i = 0; i < nLen; i++) { + bBlockPrettyPrint |= list.item(i).getNodeType()==Node.TEXT_NODE; + } + } + // Print start tag + if (nLevel>=0) { writeSpaces(nLevel,osw); } + osw.write("<"+node.getNodeName()); + writeAttributes(node,osw); + osw.write(">"); + if (nLevel>=0 && !bBlockPrettyPrint) { osw.write("\n"); } + // Print children + for (int i = 0; i < nLen; i++) { + int nNextLevel; + if (bBlockPrettyPrint || nLevel<0) { nNextLevel=-1; } + else { nNextLevel=nLevel+1; } + write(list.item(i),nNextLevel,osw); + } + // Print end tag + if (nLevel>=0 && !bBlockPrettyPrint) { writeSpaces(nLevel,osw); } + osw.write("</"+node.getNodeName()+">"); + if (nLevel>=0) { osw.write("\n"); } + } + else { // empty element + if (nLevel>=0) { writeSpaces(nLevel,osw); } + osw.write("<"+node.getNodeName()); + writeAttributes(node,osw); + // HTML compatibility: use end-tag even if empty + if (nType<=XHTML11) { + osw.write("></"+node.getNodeName()+">"); + } + else { + osw.write(" />"); + } + if (nLevel>=0) { osw.write("\n"); } + } + break; + case Node.TEXT_NODE: + write(node.getNodeValue(),osw); + break; + case Node.COMMENT_NODE: + if (nLevel>=0) { writeSpaces(nLevel,osw); } + osw.write("<!-- "); + write(node.getNodeValue(),osw); + osw.write(" -->"); + if (nLevel>=0) { osw.write("\n"); } + } + } + + private void writeAttributes(Node node, OutputStreamWriter osw) throws IOException { + NamedNodeMap attr = node.getAttributes(); + int nLen = attr.getLength(); + for (int i=0; i<nLen; i++) { + Node item = attr.item(i); + osw.write(" "); + write(item.getNodeName(),osw); + osw.write("=\""); + writeAttribute(item.getNodeValue(),osw); + osw.write("\""); + } + } + + private void writeSpaces(int nCount, OutputStreamWriter osw) throws IOException { + for (int i=0; i<nCount; i++) { osw.write(" "); } + } + + private void write(String s, OutputStreamWriter osw) throws IOException { + // Allow null strings, though this means there is a bug somewhere... + if (s==null) { osw.write("null"); return; } + int nLen = s.length(); + char c; + for (int i=0; i<nLen; i++) { + c = s.charAt(i); + switch (c) { + case ('<'): osw.write("<"); break; + case ('>'): osw.write(">"); break; + case ('&'): osw.write("&"); break; + default: + write(c,osw); + } + } + } + + private void writeAttribute(String s, OutputStreamWriter osw) throws IOException { + int nLen = s.length(); + char c; + for (int i=0; i<nLen; i++) { + c = s.charAt(i); + switch (c) { + case ('<'): osw.write("<"); break; + case ('>'): osw.write(">"); break; + case ('&'): osw.write("&"); break; + case ('"'): osw.write("""); break; + case ('\''): osw.write( nType == XHTML10 ? "'" : "'"); break; + default: + write(c,osw); + } + } + } + + private void write(char c, OutputStreamWriter osw) throws IOException { + if (bUseNamedEntities) { + if (c<256 && entities[c]!=null) { + // XHTML has a named entity here + osw.write(entities[c]); + return; + } + String s=getMathMLEntity(c); + if (s!=null && (nType==XHTML_MATHML || nType==XHTML_MATHML_XSL)) { + // There's a MathML entity to use + osw.write(s); + return; + } + } + if (c>cLimit) { + osw.write("&#x"+Integer.toHexString(c).toUpperCase()+";"); + } + else { + osw.write(c); + } + } + + private static void prepareEntities() { + if (entities==null) { + entities = new String[256]; + // Latin 1 symbols + entities[160]=" "; + entities[161]="¡"; + entities[162]="¢"; + entities[163]="£"; + entities[164]="¤"; + entities[165]="¥"; + entities[166]="¦"; + entities[167]="§"; + entities[168]="¨"; + entities[169]="©"; + entities[170]="ª"; + entities[171]="«"; + entities[172]="¬"; + entities[173]="­"; + entities[174]="®"; + entities[175]="¯"; + entities[176]="°"; + entities[177]="±"; + entities[178]="²"; + entities[179]="³"; + entities[180]="´"; + entities[181]="µ"; + entities[182]="¶"; + entities[183]="·"; + entities[184]="¸"; + entities[185]="¹"; + entities[186]="º"; + entities[187]="»"; + entities[188]="¼"; + entities[189]="½"; + entities[190]="¾"; + entities[191]="¿"; + entities[215]="×"; + entities[247]="÷"; + // Latin 1 characters + entities[192]="À"; + entities[193]="Á"; + entities[194]="Â"; + entities[195]="Ã"; + entities[196]="Ä"; + entities[197]="Å"; + entities[198]="Æ"; + entities[199]="Ç"; + entities[200]="È"; + entities[201]="É"; + entities[202]="Ê"; + entities[203]="Ë"; + entities[204]="Ì"; + entities[205]="Í"; + entities[206]="Î"; + entities[207]="Ï"; + entities[208]="Ð"; + entities[209]="Ñ"; + entities[210]="Ò"; + entities[211]="Ó"; + entities[212]="Ô"; + entities[213]="Õ"; + entities[214]="Ö"; + entities[216]="Ø"; + entities[217]="Ù"; + entities[218]="Ú"; + entities[219]="Û"; + entities[220]="Ü"; + entities[221]="Ý"; + entities[222]="Þ"; + entities[223]="ß"; + entities[224]="à"; + entities[225]="á"; + entities[226]="â"; + entities[227]="ã"; + entities[228]="ä"; + entities[229]="å"; + entities[230]="æ"; + entities[231]="ç"; + entities[232]="è"; + entities[233]="é"; + entities[234]="ê"; + entities[235]="ë"; + entities[236]="ì"; + entities[237]="í"; + entities[238]="î"; + entities[239]="ï"; + entities[240]="ð"; + entities[241]="ñ"; + entities[242]="ò"; + entities[243]="ó"; + entities[244]="ô"; + entities[245]="õ"; + entities[246]="ö"; + entities[248]="ø"; + entities[249]="ù"; + entities[250]="ú"; + entities[251]="û"; + entities[252]="ü"; + entities[253]="ý"; + entities[254]="þ"; + entities[255]="ÿ"; + } + } + + // Translate character to MathML entity (contributed by Bruno Mascret) + private String getMathMLEntity(char c) { + switch (c) { + case '\u0192': return "ƒ";// lettre minuscule latine f hameon + case '\u0391': return "Α";// lettre majuscule grecque alpha + case '\u0392': return "Β";// lettre majuscule grecque beta + case '\u0393': return "Γ";// lettre majuscule grecque gamma + case '\u0394': return "Δ";// lettre majuscule grecque delta + case '\u0395': return "Ε";// lettre majuscule grecque epsilon + case '\u0396': return "Ζ";// lettre majuscule grecque zeta + case '\u0397': return "Η";// lettre majuscule grecque eta + case '\u0398': return "Θ";// lettre majuscule grecque theta + case '\u0399': return "Ι";// lettre majuscule grecque iota + case '\u039A': return "Κ";// lettre majuscule grecque kappa + case '\u039B': return "Λ";// lettre majuscule grecque lambda + case '\u039C': return "Μ";// lettre majuscule grecque mu + case '\u039D': return "Ν";// lettre majuscule grecque nu + case '\u039E': return "Ξ";// lettre majuscule grecque xi + case '\u039F': return "Ο";// lettre majuscule grecque omicron + case '\u03A0': return "Π";// lettre majuscule grecque pi + case '\u03A1': return "Ρ";// lettre majuscule grecque rho + case '\u03A3': return "Σ";// lettre majuscule grecque sigma (Il n'y pas de caractere Sigmaf ni U+03A2 non plus) + case '\u03A4': return "Τ";// lettre majuscule grecque tau + case '\u03A5': return "Υ";// lettre majuscule grecque upsilon + case '\u03A6': return "Φ";// lettre majuscule grecque phi + case '\u03A7': return "Χ";// lettre majuscule grecque chi + case '\u03A8': return "Ψ";// lettre majuscule grecque psi + case '\u03A9': return "Ω";// lettre majuscule grecque omega + case '\u03B1': return "α";// lettre minuscule grecque alpha + case '\u03B2': return "β";// lettre minuscule grecque beta + case '\u03B3': return "γ";// lettre minuscule grecque gamma + case '\u03B4': return "δ";// lettre minuscule grecque delta + //case '\u03B4': return "δ";// lettre minuscule grecque delta + case '\u03B5': return "ε";// lettre minuscule grecque epsilon + case '\u03B6': return "ζ";// lettre minuscule grecque zeta + case '\u03B7': return "η";// lettre minuscule grecque eta + case '\u03B8': return "θ";// lettre minuscule grecque theta + case '\u03B9': return "ι";// lettre minuscule grecque iota + case '\u03BA': return "κ";// lettre minuscule grecque kappa + case '\u03BB': return "λ";// lettre minuscule grecque lambda + case '\u03BC': return "μ";// lettre minuscule grecque mu + case '\u03BD': return "ν";// lettre minuscule grecque nu + case '\u03BE': return "ξ";// lettre minuscule grecque xi + case '\u03BF': return "ο";// lettre minuscule grecque omicron + case '\u03C0': return "π";// lettre minuscule grecque pi + case '\u03C1': return "ρ";// lettre minuscule grecque rho + case '\u03C2': return "ς";// lettre minuscule grecque final sigma + case '\u03C3': return "σ";// lettre minuscule grecque sigma + case '\u03C4': return "τ";// lettre minuscule grecque tau + case '\u03C5': return "υ";// lettre minuscule grecque upsilon + case '\u03C6': return "φ";// lettre minuscule grecque phi + case '\u03C7': return "χ";// lettre minuscule grecque chi + case '\u03C8': return "ψ";// lettre minuscule grecque psi + case '\u03C9': return "ω";// lettre minuscule grecque omega + case '\u03D1': return "ϑ";// lettre minuscule grecque theta symbol + case '\u03D2': return "ϒ";// symbole grec upsilon crochet + case '\u03D6': return "ϖ";// symbole grec pi + case '\u2022': return "•";// puce (Ce N'EST PAS la meme chose que l'operateur puce, U+2219) + case '\u2026': return "…";// points de suspension + case '\u2032': return "′";// prime + case '\u2033': return "″";// double prime + case '\u203E': return "‾";// tiret en chef + case '\u2044': return "⁄";// barre de fraction + case '\u2118': return "℘";// fonction elliptique de Weierstrass + case '\u2111': return "ℑ";// majuscule I gothique = partie imaginaire + case '\u211C': return "ℜ";// majuscule R gothique = partie reelle + case '\u2122': return "™";// symbole marque commerciale + case '\u2135': return "ℵ";// symbole alef = premier nombre transfini (Le symbole alef N'EST PAS pareil a la lettre hebreue alef, U+05D0 meme si on pourrait utiliser le meme glyphe pour representer les deux caracteres) + case '\u2190': return "←";// fleche vers la gauche + case '\u2191': return "↑";// fleche vers le haut + case '\u2192': return "→";// fleche vers la droite + case '\u2193': return "↓";// fleche vers le bas + case '\u2194': return "↔";// fleche bilaterale + case '\u21B5': return "↵";// fleche vers le bas avec coin vers la gauche = retour de chariot + case '\u21D0': return "⇐";// double fleche vers la gauche (ISO 10646 ne dit pas que lArr est la meme chose que la fleche 'est implique par' et n'a pas non plus d'autre caractere pour cette fonction. Alors ? On peut utiliser lArr pour 'est implique par' comme le suggere) + case '\u21D1': return "⇑";// double fleche vers le haut + case '\u21D2': return "⇒";// double fleche vers la droite (ISO 10646 ne dit pas qu'il s'agit du caractere 'implique' et n'a pas non plus d'autre caractere avec cette fonction. Alors ? On peut utiliser rArr pour 'implique' comme le suggere) + case '\u21D3': return "⇓";// double fleche vers le bas + case '\u21D4': return "⇔";// double fleche bilaterale + case '\u2200': return "∀";// pour tous + case '\u2202': return "∂";// derivee partielle + case '\u2203': return "∃";// il existe + case '\u2205': return "∅";// ensemble vide = symbole diametre + case '\u2207': return "∇";// nabla + case '\u2208': return "∈";// appartient + case '\u2209': return "∉";// n'appartient pas + case '\u220B': return "∋";// contient comme element (Est-ce qu'il ne pourrait pas y avoir un nom plus parlant que 'ni' ?) + case '\u220F': return "∏";// produit de la famille = signe produit (prod N'EST PAS le meme caractere que U+03A0 'lettre capitale grecque pi' meme si le meme glyphe peut s'utiliser pour les deux) + case '\u2211': return "∑";// sommation de la famille (sum N'EST PAS le meme caractere que U+03A3 'ettre capitale grecque sigma' meme si le meme glyphe peut s'utiliser pour les deux) + case '\u2212': return "−";// signe moins + case '\u2217': return "∗";// operateur asterisque + case '\u221A': return "√";// racine carree = signe radical + case '\u221D': return "∝";// proportionnel + case '\u221E': return "∞";// infini + case '\u2220': return "∠";// angle + case '\u2227': return "∧";// ET logique + case '\u2228': return "∨";// OU logique + case '\u2229': return "∩";// intersection = cap + case '\u222A': return "∪";// union = cup + case '\u222B': return "∫";// integrale + case '\u2234': return "∴";// par consequent + case '\u223C': return "∼";// operateur tilde = varie avec = similaire (L'operateur tilde N'EST PAS le meme caractere que le tilde U+007E, meme si le meme glyphe peut s'utiliser pour les deux) + case '\u2245': return "≅";// approximativement egal + case '\u2248': return "≈";// presque egal = asymptotique + case '\u2260': return "≠";// pas egal + case '\u2261': return "≡";// identique + //case '\u2261': return "≡";// identique + case '\u2264': return "≤";// plus petit ou egal + case '\u2265': return "≥";// plus grand ou egal + case '\u2282': return "⊂";// sous-ensemble de + case '\u2283': return "⊃";// sur-ensemble de (Remarquez que nsup 'pas un sur-ensemble de' 2285, n'est pas couvert par le codage de la police Symbol. Devrait-il l'etre par symetrie ? Il est dans) + case '\u2284': return "⊄";// pas un sous-ensemble de + case '\u2286': return "⊆";// sous-ensemble ou egal + case '\u2287': return "⊇";// sur-ensemble de ou egal + case '\u2295': return "⊕";// plus cercle = somme directe + case '\u2297': return "⊗";// multiplie par cercle = produit vectoriel + case '\u22A5': return "⊥";// taquet vers le haut = orthogonal = perpendiculaire + case '\u22C5': return "⋅";// operateur point (L'operateur point N'EST PAS le meme caractere que le 'point median', U+00B7) + case '\u2308': return "⌈";// plafond gauche = anglet gauche + case '\u2309': return "⌉";// plafond droite + case '\u230A': return "⌊";// plancher gauche + case '\u230B': return "⌋";// plancher droite + case '\u2329': return "⟨";// chevron vers la gauche (lang N'EST PAS le meme caractere que U+003C 'inferieur' ou U+2039 'guillemet simple vers la gauche') + case '\u232A': return "⟩";// chevron vers la droite (rang iN'EST PAS le meme caractere que U+003E 'superieur' ou U+203A 'guillemet simple vers la droite') + case '\u25CA': return "◊";// losange + case '\u2660': return "♠";// pique noir (Noir semble dire ici rempli par opposition ajoure) + case '\u2663': return "♣";// trefle noir + case '\u2665': return "♥";// coeur noir + case '\u2666': return "♦";// carreau noir + // truc pas prevus + case '\u2102': return "ℂ";// ensemble C des complexes + case '\u2115': return "ℕ";// ensemble N des entiers + case '\u211A': return "ℚ";// ensemble Q des rationnels + case '\u211D': return "ℝ";// ensemble R des reels + case '\u2124': return "ℤ";// ensemble R des entiers relatifs + case '\u2223': return "∣";// divise + case '\u2224': return "∤";// ne divise pas + case '\u2243': return "≃";// asymptotiquement egal + case '\u2244': return "≄";// asymptotiquement egal + case '\u2225': return "∥";// parallele + case '\u00B1': return "±";// plus ou moins + case '\u2213': return "∓"; // moins ou plus (different de plus ou moins) + case '\u2494': return "⩽"; // inferieur ou egal incline + case '\u2270': return "≰"; //non inferieur ou egal incline + case '\u00AC': return "¬";// signe not + case '\u00B0': return "ˆ";// petit cercle, operateur concatenation, normalement ° mais on va le considere comme circ + case '\u224A': return "≊";// approxivativement egal + case '\u002B': return "+"; // signe plus + case '\u00D7': return "×"; // signe multiplication (croix) + case '\u003D': return "="; // signe egal + case '\u226E': return "≮"; // non inferieur + case '\u2A7D': return "⩽"; // inferieur incline = leqslant + case '\u220A': return "∈";// appartient + case '\u2216': return "∖";// difference d'ensemble + case '\u2288': return "⊈";// ni un sous-ensemble ni egal + case '\u2289': return "⊉";// ni un surensemble ni egal + case '\u2285': return "⊅";// non un surensemble de + case '\u301A': return "⟦";// crochet gauche avec barre + case '\u301B': return "⟧";// crochet droit avec barre + case '\u2210': return "∐";// coproduit (Pi l'envers) + case '\u222C': return "∬";// integrale double + case '\u222D': return "∭";// integrale triple + case '\u222E': return "∮";// integrale de contour + case '\u222F': return "∯";// integrale de surface + case '\u2230': return "∰";// integrale de volume + case '\u210F': return "ℏ";// const de Planck sur 2Pi + case '\u2253': return "&;";// BUG points suspensions diagonale descendant droite + case '\u22EE': return "⋮";// points suspensions verticaux + case '\u22EF': return "⋯";// points suspensions horizontaux medians + case '\u22F0': return "⋰";// points suspensions diagonale montant droite + case '\u22F1': return "⋱";// points suspensions diagonale descendant droite + case '\u02DA': return "˚"; //rond en chef + case '\u00A8': return "¨"; // double point en chef(trema) + case '\u02D9': return "˙"; // point en chef + case '\u2015': return "―"; // barre horizonthale + case '\u00AF': return "¯"; // barre horizonthale en chef + case '\u0332': return "_"; // souligne + case '\u2222': return "∢"; // angle spherique + case '\u03F1': return "ϱ"; // symbole grec rho final + case '\u226B': return "≫"; // tres superieur + case '\u226A': return "≪"; // tres inferieur + default: return null; + } + } + + +} + + + + + + + + diff --git a/source/java/writer2latex/xhtml/XhtmlFormatOption.java b/source/java/writer2latex/xhtml/XhtmlFormatOption.java new file mode 100644 index 0000000..a9c0635 --- /dev/null +++ b/source/java/writer2latex/xhtml/XhtmlFormatOption.java @@ -0,0 +1,43 @@ +/************************************************************************ + * + * XhtmlFormatOption.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-08) + * + */ + +package writer2latex.xhtml; + +import writer2latex.base.IntegerOption; + +class XhtmlFormatOption extends IntegerOption { + public XhtmlFormatOption(String sName, String sDefaultValue) { + super(sName,sDefaultValue); + } + + public void setString(String sValue) { + super.setString(sValue); + if ("ignore_styles".equals(sValue)) nValue = XhtmlConfig.IGNORE_STYLES; + else if ("ignore_hard".equals(sValue)) nValue = XhtmlConfig.IGNORE_HARD; + else if ("ignore_all".equals(sValue)) nValue = XhtmlConfig.IGNORE_ALL; + else nValue = XhtmlConfig.CONVERT_ALL; + } +} diff --git a/source/java/writer2latex/xhtml/XhtmlMathMLConverter.java b/source/java/writer2latex/xhtml/XhtmlMathMLConverter.java new file mode 100644 index 0000000..fa23e50 --- /dev/null +++ b/source/java/writer2latex/xhtml/XhtmlMathMLConverter.java @@ -0,0 +1,35 @@ +/************************************************************************ + * + * XhtmlMathMLConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-09) + * + */ + +package writer2latex.xhtml; + +public class XhtmlMathMLConverter extends Converter { + + public XhtmlMathMLConverter() { + super(XhtmlDocument.XHTML_MATHML); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/xhtml/XhtmlMathMLXSLConverter.java b/source/java/writer2latex/xhtml/XhtmlMathMLXSLConverter.java new file mode 100644 index 0000000..a6e5c07 --- /dev/null +++ b/source/java/writer2latex/xhtml/XhtmlMathMLXSLConverter.java @@ -0,0 +1,35 @@ +/************************************************************************ + * + * XhtmlMathMLXSLConverter.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-09-09) + * + */ + +package writer2latex.xhtml; + +public class XhtmlMathMLXSLConverter extends Converter { + + public XhtmlMathMLXSLConverter() { + super(XhtmlDocument.XHTML_MATHML_XSL); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/xhtml/XhtmlStyleMap.java b/source/java/writer2latex/xhtml/XhtmlStyleMap.java new file mode 100644 index 0000000..8bd3d03 --- /dev/null +++ b/source/java/writer2latex/xhtml/XhtmlStyleMap.java @@ -0,0 +1,69 @@ +/************************************************************************ + * + * XhtmlStyleMap.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2003 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.3.2 (2003-11-25) + * + */ + +package writer2latex.xhtml; + +import java.util.Hashtable; +import java.util.Enumeration; + +public class XhtmlStyleMap { + private Hashtable blockElement = new Hashtable(); + private Hashtable blockCss = new Hashtable(); + private Hashtable element = new Hashtable(); + private Hashtable css = new Hashtable(); + + public void put(String sName, String sBlockElement, String sBlockCss, String sElement, String sCss) { + blockElement.put(sName,sBlockElement); + blockCss.put(sName,sBlockCss); + element.put(sName,sElement); + css.put(sName,sCss); + } + + public boolean contains(String sName) { + return sName!=null && element.containsKey(sName); + } + + public String getBlockElement(String sName) { + return (String) blockElement.get(sName); + } + + public String getBlockCss(String sName) { + return (String) blockCss.get(sName); + } + + public String getElement(String sName) { + return (String) element.get(sName); + } + + public String getCss(String sName) { + return (String) css.get(sName); + } + + public Enumeration getNames() { + return element.keys(); + } + +} diff --git a/source/java/writer2latex/xhtml/config/cleanxhtml.xml b/source/java/writer2latex/xhtml/config/cleanxhtml.xml new file mode 100644 index 0000000..adbaa44 --- /dev/null +++ b/source/java/writer2latex/xhtml/config/cleanxhtml.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- cleanxhtml.xml + This is a sample configuration file for Writer2xhtml. + The options are set to produce a clean XHTML file using + standard XHTML elements, using a custom style sheet for + formatting. --> + +<config> + <!-- options --> + <option name="no_doctype" value="false" /> + <!-- put url to your own stylesheet here: --> + <option name="custom_stylesheet" value="http://www.w3.org/StyleSheets/Core/Oldstyle" /> + <option name="formatting" value="ignore_all" /> + <option name="frame_formatting" value="ignore_all" /> + <option name="section_formatting" value="ignore_all" /> + <option name="table_formatting" value="ignore_all" /> + <option name="ignore_table_dimensions" value="true" /> + <option name="use_dublin_core" value="true" /> + <option name="convert_to_px" value="true" /> + <option name="scaling" value="100%" /> + <option name="column_scaling" value="100%" /> + <option name="split_level" value="0" /> + <option name="calc_split" value="false" /> + <option name="ignore_hard_line_breaks" value="true" /> + <option name="ignore_empty_paragraphs" value="false" /> + <option name="ignore_double_spaces" value="true" /> + + <!-- map OOo paragraph styles to xhtml elements --> + <xhtml-style-map name="Text body" family="paragraph" element="p" css="(none)" /> + <xhtml-style-map name="Sender" family="paragraph" element="address" css="(none)" /> + <xhtml-style-map name="Preformatted Text" family="paragraph" element="pre" css="(none)" /> + <xhtml-style-map name="Quotations" family="paragraph" +block-element="blockquote" block-css="(none)" element="p" css="(none)" /> + <xhtml-style-map name="List Heading" family="paragraph" block-element="dl" block-css="(none)" element="dt" css="(none)" /> + <xhtml-style-map name="List Contents" family="paragraph" block-element="dl" block-css="(none)" element="dd" css="(none)" /> + <xhtml-style-map name="Horizontal Line" family="paragraph" element="hr" css="(none)" /> + + <!-- map OOo text styles to xhtml elements --> + <xhtml-style-map name="Citation" family="text" element="cite" css="(none)" /> + <xhtml-style-map name="Definition" family="text" element="dfn" css="(none)" /> + <xhtml-style-map name="Emphasis" family="text" element="em" css="(none)" /> + <xhtml-style-map name="Example" family="text" element="samp" css="(none)" /> + <xhtml-style-map name="Source Text" family="text" element="code" css="(none)" /> + <xhtml-style-map name="Strong Emphasis" family="text" element="strong" css="(none)" /> + <xhtml-style-map name="Teletype" family="text" element="tt" css="(none)" /> + <xhtml-style-map name="User entry" family="text" element="kbd" css="(none)" /> + <xhtml-style-map name="Variable" family="text" element="var" css="(none)" /> + + <!-- map OOo hard formatting attributes to xhtml elements --> + <xhtml-style-map name="bold" family="attribute" element="b" css="(none)" /> + <xhtml-style-map name="italics" family="attribute" element="i" css="(none)" /> + <xhtml-style-map name="fixed" family="attribute" element="tt" css="(none)" /> + <xhtml-style-map name="superscript" family="attribute" element="sup" css="(none)" /> + <xhtml-style-map name="subscript" family="attribute" element="sub" css="(none)" /> + + <!-- Notes: Writer2xhtml with the above rules support a subset of xhtml elements; + some remarks on other elements: + * The inline elements q, abbr, acronym, big and small are not supported, but + you can add support for those if you define your own styles in OOo and map + them to these elements. + * del, ins, object and map are not currently supported by Writer2xhtml, but + might be in the future + --> + +</config> + diff --git a/source/java/writer2latex/xmerge/BinaryGraphicsDocument.java b/source/java/writer2latex/xmerge/BinaryGraphicsDocument.java new file mode 100644 index 0000000..c1e05a0 --- /dev/null +++ b/source/java/writer2latex/xmerge/BinaryGraphicsDocument.java @@ -0,0 +1,165 @@ +/************************************************************************ + * + * BinaryGraphicsDocument.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2008 by Henrik Just + * + * All Rights Reserved. + * + * Version 1.0 (2008-11-23) + * + */ + +package writer2latex.xmerge; + +import java.io.OutputStream; +import java.io.InputStream; +//import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import writer2latex.util.Misc; + + +/** + * <p>Class representing a binary graphics document. + * This class is used for representing graphics documents that are <i>not</i> + * interpreted in any way, but simply copied verbatim from the source format + * to the target format.</p> + * + * <p><code>GraphicsDocument</code> is used to create new graphics documents.</p> + * + */ +public class BinaryGraphicsDocument implements Document { + + //private final static int BUFFERSIZE = 1024; + + private String docName; + + private byte[] data; + private int nOff; + private int nLen; + + private String sFileExtension; + private String sMimeType; + + /** + * <p>Constructs a new graphics document.</p> + * + * <p>This new document does not contain any information. Document data must + * either be added using appropriate methods, or an existing file can be + * {@link #read(InputStream) read} from an <code>InputStream</code>.</p> + * + * @param name The name of the <code>GraphicsDocument</code>. + */ + public BinaryGraphicsDocument(String name, String sFileExtension, String sMimeType) { + this.sFileExtension = sFileExtension; + this.sMimeType = sMimeType; + docName = trimDocumentName(name); + } + + + /** + * <p>This method reads <code>byte</code> data from the InputStream.</p> + * + * @param is InputStream containing a binary data file. + * + * @throws IOException In case of any I/O errors. + */ + public void read(InputStream is) throws IOException { + data = Misc.inputStreamToByteArray(is); + } + + public void read(byte[] data) { + read(data,0,data.length); + } + + public void read(byte[] data, int nOff, int nLen) { + this.data = data; + this.nOff = nOff; + this.nLen = nLen; + } + + /* + * Utility method to make sure the document name is stripped of any file + * extensions before use. + */ + private String trimDocumentName(String name) { + String temp = name.toLowerCase(); + + if (temp.endsWith(getFileExtension())) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - getFileExtension().length(); + name = name.substring(0,endIndex); + } + + return name; + } + + /** + * <p>Returns the <code>Document</code> name with no file extension.</p> + * + * @return The <code>Document</code> name with no file extension. + */ + public String getName() { + return docName; + } + + /** + * <p>Returns the <code>Document</code> name with file extension.</p> + * + * @return The <code>Document</code> name with file extension. + */ + public String getFileName() { + return new String(docName + getFileExtension()); + } + + + /** + * <p>Writes out the <code>Document</code> content to the specified + * <code>OutputStream</code>.</p> + * + * <p>This method may not be thread-safe. + * Implementations may or may not synchronize this + * method. User code (i.e. caller) must make sure that + * calls to this method are thread-safe.</p> + * + * @param os <code>OutputStream</code> to write out the + * <code>Document</code> content. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + os.write(data, nOff, nLen); + } + + /** + * Returns the file extension for this type of + * <code>Document</code>. + * + * @return The file extension of <code>Document</code>. + */ + public String getFileExtension(){ return sFileExtension; } + + /** + * Method to return the MIME type of the document. + * + * @return String The document's MIME type. + */ + public String getDocumentMIMEType(){ return sMimeType; } + +} \ No newline at end of file diff --git a/source/java/writer2latex/xmerge/ConvertData.java b/source/java/writer2latex/xmerge/ConvertData.java new file mode 100644 index 0000000..c28dbe5 --- /dev/null +++ b/source/java/writer2latex/xmerge/ConvertData.java @@ -0,0 +1,179 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for Writer2LaTeX +// Change: The first document added will become the "master document" 2006/10/05 +// Version 1.0 (2008-11-24) + +package writer2latex.xmerge; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Vector; +import java.util.Iterator; + +import writer2latex.api.ConverterResult; +import writer2latex.api.OutputFile; + +/** + * <p><code>ConvertData</code> is used as a container for passing + * <code>OutputFile</code> objects in and out of the <code>Convert</code> + * class. The <code>ConvertData</code> contains a <code>String</code> + * name and a <code>Vector</code> of <code>OutputFile</code> objects.</p> + * + * @author Martin Maher + */ +public class ConvertData implements ConverterResult { + + /** + * Vector of <code>OutputFile</code> objects. + */ + private Vector v = new Vector(); + + /** Master doc */ + private OutputFile masterDoc = null; + + /** + * Name of the <code>ConvertData</code> object. + */ + private String name; + + + /** + * Resets ConvertData. This empties all <code>OutputFile</code> + * objects from this class. This allows reuse of a + * <code>ConvertData</code>. + */ + public void reset() { + name = null; + v.removeAllElements(); + } + + /** + * Returns the <code>OutputFile</code> name. + * + * @return The <code>OutputFile</code> name. + */ + public String getName() { + return name; + } + + + /** + * Sets the <code>OutputFile</code> name. + * + * @param docName The name of the <code>OutputFile</code>. + */ + public void setName(String docName) { + name = docName; + } + + /** + * Adds a <code>OutputFile</code> to the vector. + * + * @param doc The <code>OutputFile</code> to add. + */ + public void addDocument(OutputFile doc) { + if (v.size()==0) { masterDoc = doc; } + v.add(doc); + } + + /** Get the master document + * @return <code>OutputFile</code> the master document + */ + public OutputFile getMasterDocument() { + return masterDoc; + } + + /** Check if a given document is the master document + * @param doc The <code>OutputFile</code> to check + * @return true if this is the master document + */ + public boolean isMasterDocument(OutputFile doc) { + return doc == masterDoc; + } + + + /** + * Gets an <code>Iterator</code> to access the <code>Vector</code> + * of <code>OutputFile</code> objects + * + * @return The <code>Iterator</code> to access the + * <code>Vector</code> of <code>OutputFile</code> objects. + */ + public Iterator iterator() { + return v.iterator(); + } + + + /** + * Gets the number of <code>OutputFile</code> objects currently stored + * + * @return The number of <code>OutputFile</code> objects currently + * stored. + */ + public int getNumDocuments() { + return (v.size()); + } + + public void write(File dir) throws IOException { + if (dir!=null && !dir.exists()) throw new IOException("Directory does not exist"); + Iterator docEnum = iterator(); + while (docEnum.hasNext()) { + OutputFile docOut = (OutputFile) docEnum.next(); + String sDirName = ""; + String sFileName = docOut.getFileName(); + File subdir = dir; + int nSlash = sFileName.indexOf("/"); + if (nSlash>-1) { + sDirName = sFileName.substring(0,nSlash); + sFileName = sFileName.substring(nSlash+1); + subdir = new File(dir,sDirName); + if (!subdir.exists()) { subdir.mkdir(); } + } + File outfile = new File (subdir,sFileName); + FileOutputStream fos = new FileOutputStream(outfile); + docOut.write(fos); + fos.flush(); + fos.close(); + } + + } +} + diff --git a/source/java/writer2latex/xmerge/DOMDocument.java b/source/java/writer2latex/xmerge/DOMDocument.java new file mode 100644 index 0000000..04f2acd --- /dev/null +++ b/source/java/writer2latex/xmerge/DOMDocument.java @@ -0,0 +1,400 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for writer2latex +// Version 1.0 (2008-11-23) + +package writer2latex.xmerge; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +//import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +//import java.io.ByteArrayInputStream; +//import java.io.IOException; + + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; +//import org.xml.sax.SAXParseException; + +/** + * An implementation of <code>Document</code> for + * StarOffice documents. + */ +public class DOMDocument + implements writer2latex.xmerge.Document { + + /** Factory for <code>DocumentBuilder</code> objects. */ + private static DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + + /** DOM <code>Document</code> of content.xml. */ + private Document contentDoc = null; + + /** DOM <code>Document</code> of styles.xml. */ + //private Document styleDoc = null; + + private String documentName = null; + private String fileName = null; + private String fileExt = null; + + /** Resources object. */ + //private Resources res = null; + + + /** + * Default constructor. + * + * @param name <code>Document</code> name. + * @param ext <code>Document</code> extension. + */ + public DOMDocument(String name,String ext) + { + this(name,ext,true, false); + } + + /** + * Returns the file extension of the <code>Document</code> + * represented. + * + * @return file extension of the <code>Document</code>. + */ + protected String getFileExtension() { + return fileExt; + } + + + /** + * Constructor with arguments to set <code>namespaceAware</code> + * and <code>validating</code> flags. + * + * @param name <code>Document</code> name (may or may not + * contain extension). + * @param ext <code>Document</code> extension. + * @param namespaceAware Value for <code>namespaceAware</code> flag. + * @param validating Value for <code>validating</code> flag. + */ + public DOMDocument(String name, String ext,boolean namespaceAware, boolean validating) { + + //res = Resources.getInstance(); + factory.setValidating(validating); + factory.setNamespaceAware(namespaceAware); + this.fileExt = ext; + this.documentName = trimDocumentName(name); + this.fileName = documentName + getFileExtension(); + } + + + /** + * Removes the file extension from the <code>Document</code> + * name. + * + * @param name Full <code>Document</code> name with extension. + * + * @return Name of <code>Document</code> without the extension. + */ + private String trimDocumentName(String name) { + String temp = name.toLowerCase(); + String ext = getFileExtension(); + + if (temp.endsWith(ext)) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - ext.length(); + name = name.substring(0,endIndex); + } + + return name; + } + + + /** + * Return a DOM <code>Document</code> object of the document content + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getContentDOM() { + + return contentDoc; + } + + /** + * Sets the Content of the <code>Document</code> to the contents of the + * supplied <code>Node</code> list. + * + * @param newDom DOM <code>Document</code> object. + */ + public void setContentDOM( Node newDom) { + contentDoc=(Document)newDom; + } + + + /** + * Return the name of the <code>Document</code>. + * + * @return The name of <code>Document</code>. + */ + public String getName() { + + return documentName; + } + + + /** + * Return the file name of the <code>Document</code>, possibly + * with the standard extension. + * + * @return The file name of <code>Document</code>. + */ + public String getFileName() { + + return fileName; + } + + + /** + * Read the Office <code>Document</code> from the specified + * <code>InputStream</code>. + * + * @param is Office document <code>InputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is) throws IOException { + DocumentBuilder builder = null; + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException ex) { + throw new IOException(ex.getMessage()); + } + try { + contentDoc= builder.parse(is); + } catch (SAXException ex) { + throw new IOException(ex.getMessage()); + } + } + + + /** + * Write out content to the supplied <code>OutputStream</code>. + * + * @param os XML <code>OutputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + + // set bytes for writing to output stream + byte contentBytes[] = docToBytes(contentDoc); + + os.write(contentBytes); + } + + + /** + * <p>Write out a <code>org.w3c.dom.Document</code> object into a + * <code>byte</code> array.</p> + * + * <p>TODO: remove dependency on com.sun.xml.tree.XmlDocument + * package!</p> + * + * @param Document DOM <code>Document</code> object. + * + * @return <code>byte</code> array of DOM <code>Document</code> + * object. + * + * @throws IOException If any I/O error occurs. + */ + private byte[] docToBytes(Document doc) + throws IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + java.lang.reflect.Constructor con; + java.lang.reflect.Method meth; + + String domImpl = doc.getClass().getName(); + + /* + * We may have multiple XML parsers in the Classpath. + * Depending on which one is first, the actual type of + * doc may vary. Need a way to find out which API is being + * used and use an appropriate serialization method. + */ + try { + // First of all try for JAXP 1.0 + if (domImpl.equals("com.sun.xml.tree.XmlDocument")) { + Class jaxpDoc = Class.forName("com.sun.xml.tree.XmlDocument"); + + // The method is in the XMLDocument class itself, not a helper + meth = jaxpDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.crimson.tree.XmlDocument")) + { + Class crimsonDoc = Class.forName("org.apache.crimson.tree.XmlDocument"); + // The method is in the XMLDocument class itself, not a helper + meth = crimsonDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.xerces.dom.DocumentImpl") + || domImpl.equals("org.apache.xerces.dom.DeferredDocumentImpl")) { + // Try for Xerces + Class xercesSer = + Class.forName("org.apache.xml.serialize.XMLSerializer"); + + // Get the OutputStream constructor + // May want to use the OutputFormat parameter at some stage too + con = xercesSer.getConstructor(new Class [] + { Class.forName("java.io.OutputStream"), + Class.forName("org.apache.xml.serialize.OutputFormat") } ); + + + // Get the serialize method + meth = xercesSer.getMethod("serialize", + new Class [] { Class.forName("org.w3c.dom.Document") } ); + + + // Get an instance + Object serializer = con.newInstance(new Object [] { baos, null } ); + + + // Now call serialize to write the document + meth.invoke(serializer, new Object [] { doc } ); + } + else if (domImpl.equals("gnu.xml.dom.DomDocument")) { + + Class gnuSer = Class.forName("gnu.xml.dom.ls.DomLSSerializer"); + + // Get the serialize method + meth = gnuSer.getMethod("serialize", + new Class [] { Class.forName("org.w3c.dom.Node"), + Class.forName("java.io.OutputStream") } ); + + // Get an instance + Object serializer = gnuSer.newInstance(); + + // Now call serialize to write the document + meth.invoke(serializer, new Object [] { doc, baos } ); + } + else { + // We dont have another parser + throw new IOException("No appropriate API (JAXP/Xerces) to serialize XML document: " + domImpl); + } + } + catch (ClassNotFoundException cnfe) { + throw new IOException(cnfe.toString()); + } + catch (Exception e) { + // We may get some other errors, but the bottom line is that + // the steps being executed no longer work + throw new IOException(e.toString()); + } + + byte bytes[] = baos.toByteArray(); + + return bytes; + } + + + /** + * Initializes a new DOM <code>Document</code> with the content + * containing minimum XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initContentDOM() throws IOException { + contentDoc = createDOM(""); + + } + + /** + * <p>Creates a new DOM <code>Document</code> containing minimum + * OpenOffice XML tags.</p> + * + * <p>This method uses the subclass + * <code>getOfficeClassAttribute</code> method to get the + * attribute for <i>office:class</i>.</p> + * + * @param rootName root name of <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + private final Document createDOM(String rootName) throws IOException { + + Document doc = null; + + try { + + DocumentBuilder builder = factory.newDocumentBuilder(); + doc = builder.newDocument(); + + } catch (ParserConfigurationException ex) { + System.err.println("Error:"+ ex); + + + } + + Element root = (Element) doc.createElement(rootName); + doc.appendChild(root); + + + return doc; + } + +} + + + + diff --git a/source/java/writer2latex/xmerge/Document.java b/source/java/writer2latex/xmerge/Document.java new file mode 100644 index 0000000..1a54ec6 --- /dev/null +++ b/source/java/writer2latex/xmerge/Document.java @@ -0,0 +1,86 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * This version is adapted for Writer2LaTeX + * version 1.0 (2008-11-22) + * + ************************************************************************/ + +package writer2latex.xmerge; + +//import java.io.OutputStream; +import java.io.InputStream; +import java.io.IOException; + +import writer2latex.api.OutputFile; + +/** + * <p>A <code>Document</code> represents any <code>Document</code> + * to be converted and the resulting <code>Document</code> from any + * conversion.</p> + * + * + * @author Herbie Ong + */ +public interface Document extends OutputFile { + + + /** + * <p>Reads the content from the <code>InputStream</code> into + * the <code>Document</code>.</p> + * + * <p>This method may not be thread-safe. + * Implementations may or may not synchronize this + * method. User code (i.e. caller) must make sure that + * calls to this method are thread-safe.</p> + * + * @param is <code>InputStream</code> to read in the + * <code>Document</code> content. + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is) throws IOException; + + + /** + * Returns the <code>Document</code> name with no file extension. + * + * @return The <code>Document</code> name with no file extension. + */ + public String getName(); + + +} + diff --git a/source/java/writer2latex/xmerge/EmbeddedBinaryObject.java b/source/java/writer2latex/xmerge/EmbeddedBinaryObject.java new file mode 100644 index 0000000..6f31b38 --- /dev/null +++ b/source/java/writer2latex/xmerge/EmbeddedBinaryObject.java @@ -0,0 +1,143 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * either of the following licenses + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for Writer2LaTeX +// Version 1.0 (2008-11-22) + +package writer2latex.xmerge; + +import org.w3c.dom.Document; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +//import org.w3c.dom.Node; + + +/** + * This class represents embedded object's in an OpenOffice.org document that + * have a binary representation. + */ +public class EmbeddedBinaryObject extends EmbeddedObject { + + /** The object's binary representation. */ + protected byte[] objData = null; + + /** + * Constructor for an embedded object stored using an XML representation. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + */ + public EmbeddedBinaryObject(String name, String type) { + super(name, type); + } + + + /** + * Package private constructor for use when reading an object from a + * compressed SX? file. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + * @param source The OfficeZip representation of the SX? file that stores + * the object. + */ + EmbeddedBinaryObject(String name, String type, OfficeZip source) { + super(name, type, source); + } + + + /** + * This method returns the data for this object. + * + * @return A <code>byte</code> array containing the object's data. + */ + public byte[] getBinaryData() { + + if (objData == null) { + // See if we came from a Zip file + if (zipFile != null) { + objData = zipFile.getNamedBytes(objName); + } + } + + return objData; + } + + + /** + * Sets the data for this object. + * + * @param data A <code>byte</code> array containing data for the object. + */ + public void setBinaryData(byte[] data) { + objData = data; + hasChanged = true; + } + + /** + * Package private method for writing the data of the EmbeddedObject to a + * SX? file. + * + * @param zip An <code>OfficeZip</code> instance representing the file + * the data is to be written to. + */ + void write(OfficeZip zip) { + if (hasChanged) { + zip.setNamedBytes(objName, objData); + } + } + + + /** + * Package private method that constructs the manifest.xml entries for this + * embedded object. + * + * @return Document <code>Document</code> containing the manifest entries. + */ + void writeManifestData(Document manifestDoc) throws DOMException { + Element objNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + objNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, objType); + objNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, objName); + + manifestDoc.getDocumentElement().appendChild(objNode); + } + +} + diff --git a/source/java/writer2latex/xmerge/EmbeddedObject.java b/source/java/writer2latex/xmerge/EmbeddedObject.java new file mode 100644 index 0000000..1d8cad8 --- /dev/null +++ b/source/java/writer2latex/xmerge/EmbeddedObject.java @@ -0,0 +1,129 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for Writer2LaTeX + +package writer2latex.xmerge; + +import java.io.IOException; + +import org.w3c.dom.Document; +import org.w3c.dom.DOMException; + + +public abstract class EmbeddedObject { + protected String objName; + protected String objType; + + /** Representation of the file from which this object was read. */ + protected OfficeZip zipFile = null; + + /** Flag indicating if this document has changed since reading or is new. */ + protected boolean hasChanged = false; + + /** + * Constructor for an embedded object stored using an XML representation. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + */ + public EmbeddedObject(String name, String type) { + objName = name; + objType = type; + + hasChanged = true; + } + + + /** + * Package private constructor for use when reading an object from a + * compressed SX? file. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + * @param source The OfficeZip representation of the SX? file that stores + * the object. + */ + EmbeddedObject(String name, String type, OfficeZip source) { + this(name, type); + zipFile = source; + } + + + /** + * Retrieves the name of the embedded object represented by an instance of + * this class. + * + * <b>N.B.</b>The name referes to the name as found in the + * <code>META-INF/manifest.xml</code> file. + * + * @return The name of the object. + */ + public final String getName() { + return objName; + } + + + /** + * Retrieves the type of the embedded object represented by an instance of + * this class. + * + * The <code>META-INF/manifest.xml</code> file currently represents the + * type of an object using MIME types. + */ + public final String getType() { + return objType; + } + + /** + * Package private method for writing the data of the EmbeddedObject to a + * SX? file. + * + * @param zip An <code>OfficeZip</code> instance representing the file + * the data is to be written to. + */ + abstract void write(OfficeZip zip) throws IOException; + + /** + * Package private method that constructs the manifest.xml entries for this + * embedded object. + * + * @return Document <code>Document</code> containing the manifest entries. + */ + abstract void writeManifestData(Document manifestDoc) throws DOMException; +} \ No newline at end of file diff --git a/source/java/writer2latex/xmerge/EmbeddedXMLObject.java b/source/java/writer2latex/xmerge/EmbeddedXMLObject.java new file mode 100644 index 0000000..6f497e7 --- /dev/null +++ b/source/java/writer2latex/xmerge/EmbeddedXMLObject.java @@ -0,0 +1,312 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for Writer2LaTeX +// Version 1.0 (2008-11-23) + +package writer2latex.xmerge; + +//import java.io.ByteArrayInputStream; +import java.io.IOException; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Document; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +//import org.xml.sax.EntityResolver; +//import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +/** + * This class represents those embedded objects in an OpenOffice.org document + * that have an XML representation. Currently, according to the OpenOffice.org + * File Format 1.0 document, there are 6 such objects: + * + * Formulae created with Math (application/vnd.sun.xml.math) + * Charts created with Chart (application/vnd.sun.xml.chart) + * Spreadsheets created with Calc (application/vnd.sun.xml.calc) + * Text created with Writer (application/vnd.sun.xml.writer) + * Drawings created with Draw (application/vnd.sun.xml.draw) + * Presentations created with Impress (application/vnd.sun.xml.impress) + * + * These object types are stored using a combination of content, settings and styles + * XML files. + */ +public class EmbeddedXMLObject extends EmbeddedObject { + + // Entries for the subdocuments that constitute this object; + protected Document contentDOM = null; + protected Document settingsDOM = null; + protected Document stylesDOM = null; + + private DocumentBuilder builder = null; + + /** + * Constructor for an embedded object stored using an XML representation. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + */ + public EmbeddedXMLObject(String name, String type) { + super(name, type); + } + + /** + * Package private constructor for use when reading an object from a + * compressed SX? file. + * + * @param name The name of the object. + * @param type The mime-type of the object. See the class summary. + * @param source The OfficeZip representation of the SX? file that stores + * the object. + */ + EmbeddedXMLObject(String name, String type, OfficeZip source) { + super(name, type, source); + } + + + /** + * Returns the content data for this embedded object. + * + * @return DOM represenation of "content.xml" + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + public Document getContentDOM() throws SAXException, IOException { + + if (contentDOM == null) { + contentDOM = getNamedDOM("content.xml"); + } + + return contentDOM; + } + + + /** + * Sets the content data for the embedded object. + * + * @param content DOM representation of the object's content. + */ + public void setContentDOM(Document content) { + contentDOM = content; + hasChanged = true; + } + + + /** + * Returns the settings data for this embedded object. + * + * @return DOM represenation of "settings.xml" + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + public Document getSettingsDOM() throws SAXException, IOException { + + if (settingsDOM == null) { + settingsDOM = getNamedDOM("settings.xml"); + } + + return settingsDOM; + } + + + /** + * Sets the settings data for the embedded object. + * + * @param settings DOM representation of the object's settings. + */ + public void setSettingsDOM(Document settings) { + settingsDOM = settings; + hasChanged = true; + } + + + /** + * Returns the style data for this embedded object. + * + * @return DOM represenation of "styles.xml" + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + public Document getStylesDOM() throws SAXException, IOException { + + if (stylesDOM == null) { + stylesDOM = getNamedDOM("styles.xml"); + } + + return stylesDOM; + } + + + /** + * Sets the styles data for the embedded object. + * + * @param styles DOM representation of the object's styles. + */ + public void setStylesDOM(Document styles) { + stylesDOM = styles; + hasChanged = true; + } + + + /** + * This method extracts the data for the given XML file from the SX? file + * and creates a DOM representation of it. + * + * @param name The name of the XML file to retrieve. It is paired with + * the object name to access the SX? file. + * + * @return DOM representation of the named XML file. + * + * @throws SAXException If any parser error occurs + * @throws IOException If any IO error occurs + */ + private Document getNamedDOM(String name) throws SAXException, IOException { + if (zipFile == null) { + return null; + } + + try { + if (builder == null) { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + + factory.setValidating(false); + builder = factory.newDocumentBuilder(); + } + + byte[] data = zipFile.getNamedBytes(new String(objName + "/" + name)); + if (data != null) { + return OfficeDocument.parse(builder, data); + } + else { + return null; + } + + } + catch (SAXException se) { + throw se; + } + catch (IOException ioe) { + throw ioe; + } + catch (ParserConfigurationException pce) { + throw new SAXException(pce); + } + } + + + /** + * Package private method for writing the data of the EmbeddedObject to a + * SX? file. + * + * @param zip An <code>OfficeZip</code> instance representing the file + * the data is to be written to. + */ + void write(OfficeZip zip) throws IOException { + if (hasChanged == true) { + if (contentDOM != null) { + zip.setNamedBytes(new String(objName + "/content.xml"), + OfficeDocument.docToBytes(contentDOM)); + } + if (settingsDOM != null) { + zip.setNamedBytes(new String(objName + "/settings.xml"), + OfficeDocument.docToBytes(settingsDOM)); + } + if (stylesDOM != null) { + zip.setNamedBytes(new String(objName + "/styles.xml"), + OfficeDocument.docToBytes(stylesDOM)); + } + } + } + + /** + * Package private method that constructs the manifest.xml entries for this + * embedded object. + * + * @param manifestDoc <code>Document</code> containing the manifest entries. + */ + void writeManifestData(Document manifestDoc) throws DOMException { + Node root = manifestDoc.getDocumentElement(); + + if (contentDOM != null) { + Element contentNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + contentNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + contentNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/content.xml")); + + root.appendChild(contentNode); + } + + if (settingsDOM != null) { + Element settingsNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + settingsNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + settingsNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/settings.xml")); + + root.appendChild(settingsNode); + } + + if (stylesDOM != null) { + Element stylesNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + stylesNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + stylesNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/styles.xml")); + } + + + Element objectNode = manifestDoc.createElement(OfficeConstants.TAG_MANIFEST_FILE); + + objectNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_TYPE, objType); + objectNode.setAttribute(OfficeConstants.ATTRIBUTE_MANIFEST_FILE_PATH, + new String(objName + "/")); + + root.appendChild(objectNode); + } + +} \ No newline at end of file diff --git a/source/java/writer2latex/xmerge/NewDOMDocument.java b/source/java/writer2latex/xmerge/NewDOMDocument.java new file mode 100644 index 0000000..5bd28f3 --- /dev/null +++ b/source/java/writer2latex/xmerge/NewDOMDocument.java @@ -0,0 +1,160 @@ +/************************************************************************ + * + * NewDOMDocument.java + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Copyright: 2002-2006 by Henrik Just + * + * All Rights Reserved. + * + * Version 0.5 (2006-10-01) + * + */ + +package writer2latex.xmerge; + +import org.w3c.dom.NodeList; +import org.w3c.dom.Node; +import org.w3c.dom.NamedNodeMap; + +import writer2latex.xmerge.DOMDocument; + +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.IOException; + +/** + * An extension of <code>DOMDocument</code> + * that overrides the write method. + * (This method fails with the version of xerces shipped with jre 1.5) + */ +public class NewDOMDocument extends DOMDocument { + + /** Constructor + */ + public NewDOMDocument(String sFileName, String sExtension) { + super(sFileName,sExtension); + } + + /** + * Write out content to the supplied <code>OutputStream</code>. + * (with pretty printing) + * @param os XML <code>OutputStream</code>. + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8"); + osw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"); + write(getContentDOM().getDocumentElement(),0,osw); + osw.flush(); + osw.close(); + } + + // Write nodes; we only need element, text and comment nodes + private void write(Node node, int nLevel, OutputStreamWriter osw) throws IOException { + short nType = node.getNodeType(); + switch (nType) { + case Node.ELEMENT_NODE: + if (node.hasChildNodes()) { + // Block pretty print from this node? + NodeList list = node.getChildNodes(); + int nLen = list.getLength(); + boolean bBlockPrettyPrint = false; + if (nLevel>=0) { + for (int i = 0; i < nLen; i++) { + bBlockPrettyPrint |= list.item(i).getNodeType()==Node.TEXT_NODE; + } + } + // Print start tag + if (nLevel>=0) { writeSpaces(nLevel,osw); } + osw.write("<"+node.getNodeName()); + writeAttributes(node,osw); + osw.write(">"); + if (nLevel>=0 && !bBlockPrettyPrint) { osw.write("\n"); } + // Print children + for (int i = 0; i < nLen; i++) { + int nNextLevel; + if (bBlockPrettyPrint || nLevel<0) { nNextLevel=-1; } + else { nNextLevel=nLevel+1; } + write(list.item(i),nNextLevel,osw); + } + // Print end tag + if (nLevel>=0 && !bBlockPrettyPrint) { writeSpaces(nLevel,osw); } + osw.write("</"+node.getNodeName()+">"); + if (nLevel>=0) { osw.write("\n"); } + } + else { // empty element + if (nLevel>=0) { writeSpaces(nLevel,osw); } + osw.write("<"+node.getNodeName()); + writeAttributes(node,osw); + osw.write(" />"); + if (nLevel>=0) { osw.write("\n"); } + } + break; + case Node.TEXT_NODE: + write(node.getNodeValue(),osw); + break; + case Node.COMMENT_NODE: + if (nLevel>=0) { writeSpaces(nLevel,osw); } + osw.write("<!-- "); + write(node.getNodeValue(),osw); + osw.write(" -->"); + if (nLevel>=0) { osw.write("\n"); } + } + } + + private void writeAttributes(Node node, OutputStreamWriter osw) throws IOException { + NamedNodeMap attr = node.getAttributes(); + int nLen = attr.getLength(); + for (int i=0; i<nLen; i++) { + Node item = attr.item(i); + osw.write(" "); + write(item.getNodeName(),osw); + osw.write("=\""); + write(item.getNodeValue(),osw); + osw.write("\""); + } + } + + private void writeSpaces(int nCount, OutputStreamWriter osw) throws IOException { + for (int i=0; i<nCount; i++) { osw.write(" "); } + } + + private void write(String s, OutputStreamWriter osw) throws IOException { + int nLen = s.length(); + char c; + for (int i=0; i<nLen; i++) { + c = s.charAt(i); + switch (c) { + case ('<'): osw.write("<"); break; + case ('>'): osw.write(">"); break; + case ('&'): osw.write("&"); break; + case ('"'): osw.write("""); break; + case ('\''): osw.write( "'"); break; + default: osw.write(c); + } + } + } + +} + + + + + + + + diff --git a/source/java/writer2latex/xmerge/OfficeConstants.java b/source/java/writer2latex/xmerge/OfficeConstants.java new file mode 100644 index 0000000..37ca471 --- /dev/null +++ b/source/java/writer2latex/xmerge/OfficeConstants.java @@ -0,0 +1,455 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version has been adapted for Writer2LaTeX + +package writer2latex.xmerge; + +/** + * This interface contains constants for StarOffice XML tags, + * attributes (StarCalc cell types, etc.). + * + * @author Herbie Ong, Paul Rank, Martin Maher + */ +public interface OfficeConstants { + + /** Element tag for <i>office:document</i>, this is the root tag. */ + public final static String TAG_OFFICE_DOCUMENT = "office:document"; + + /** + * Element tag for <i>office:document-content</i>, this is the root + * tag in content.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_CONTENT = "office:document-content"; + + /** + * Element tag for <i>office:document-settings</i>, this is the root + * tag in content.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_SETTINGS= "office:document-settings"; + + /** + * Element tag for <i>office:document-meta</i>, this is the root + * tag in content.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_META= "office:document-meta"; + + /** + * Element tag for <i>office:document-styles</i>, this is the root tag + * in styles.xml. + */ + public final static String TAG_OFFICE_DOCUMENT_STYLES = "office:document-styles"; + + /** + * Attribute tag for <i>office:class</i> of element + * <i>office:document</i>. + */ + public final static String ATTRIBUTE_OFFICE_CLASS = "office:class"; + + /** Element tag for <i>office:styles</i>. */ + public final static String TAG_OFFICE_STYLES = "office:styles"; + + /** Element tag for <i>office:meta</i>. */ + public final static String TAG_OFFICE_META = "office:meta"; + + /** Element tag for <i>office:automatic-styles</i>. */ + public final static String TAG_OFFICE_AUTOMATIC_STYLES = "office:automatic-styles"; + + /** Element tag for <i>office:master-styles</i>. */ + public final static String TAG_OFFICE_MASTER_STYLES = "office:master-styles"; + + /** Element tag for <i>office:body</i>. */ + public final static String TAG_OFFICE_BODY = "office:body"; + + /** Element tag for <i>office:font-face-decls</i>. */ + public final static String TAG_OFFICE_FONT_FACE_DECLS = "office:font-face-decls"; + + /** Element tag for <i>office:settings</i>. */ + public final static String TAG_OFFICE_SETTINGS = "office:settings"; + + //Adding + + /** Element tag for <i>text:variable-set</i>. */ + public final static String TAG_TEXT_VARIABLE_SET = "text:variable-set"; + + /** Element tag for <i>text:variable-get</i>. */ + public final static String TAG_TEXT_VARIABLE_GET = "text:variable-get"; +/** Element tag for <i>text:expression</i>. */ + public final static String TAG_TEXT_EXPRESSION = "text:expression"; + +/** Element tag for <i>text:user-field-get</i>. */ + public final static String TAG_TEXT_USER_FIELD_GET = "text:user-field-get"; + +/** Element tag for <i>text:page-variable-get</i>. */ + public final static String TAG_TEXT_PAGE_VARIABLE_GET = "text:page-variable-get"; +/** Element tag for <i>text:sequence</i>. */ + public final static String TAG_TEXT_SEQUENCE = "text:sequence"; + + /** Element tag for <i>text:text-input</i>. */ + public final static String TAG_TEXT_VARIABLE_INPUT = "text:variable-input"; + /** Element tag for <i>text:time</i>. */ + public final static String TAG_TEXT_TIME = "text:time"; + + /** Element tag for <i>text:page-count</i>. */ + public final static String TAG_TEXT_PAGE_COUNT = "text:page-count"; + /** Element tag for <i>text:page-number</i>. */ + public final static String TAG_TEXT_PAGE_NUMBER = "text:page-number"; + /** Element tag for <i>text:author-initials</i>. */ + public final static String TAG_TEXT_AUTHOR_INITIALS = "text:author-initials"; + /** Element tag for <i>text:subject</i>. */ + public final static String TAG_TEXT_SUBJECT = "text:subject"; + /** Element tag for <i>text:title</i>. */ + public final static String TAG_TEXT_TITLE = "text:title"; + /** Element tag for <i>text:creation-time</i>. */ + public final static String TAG_TEXT_CREATION_TIME = "text:creation-time"; + + /** Element tag for <i>text:date</i>. */ + public final static String TAG_TEXT_DATE = "text:date"; + /** Element tag for <i>text:text-input</i>. */ + public final static String TAG_TEXT_TEXT_INPUT = "text:text-input"; + + +//end adding + + /** Element tag for <i>office:font-decls</i>. */ + public final static String TAG_OFFICE_FONT_DECLS = "office:font-decls"; + + /** Element tag for <i>style:font-decl</i>. */ + public final static String TAG_STYLE_FONT_DECL = "style:font-decl"; + + /** Attribute tag for <i>style:name</i> of element <i>style:name</i>. */ + public final static String ATTRIBUTE_STYLE_NAME = "style:name"; + + /** + * Attribute tag for <i>style:font-pitch</i> of element + * <i>style:font-pitch</i>. + */ + public final static String ATTRIBUTE_STYLE_FONT_PITCH = "style:font-pitch"; + + /** + * Attribute tag for <i>fo:font-family</i> of element + * <i>fo:font-family</i>. + */ + public final static String ATTRIBUTE_FO_FONT_FAMILY = "fo:font-family"; + + /** + * Attribute tag for <i>fo:font-family</i> of element + * <i>fo:font-family</i>. + */ + public final static String ATTRIBUTE_FO_FONT_FAMILY_GENERIC = "fo:font-family-generic"; + + /** Element tag for <i>text:p</i>. */ + public final static String TAG_PARAGRAPH = "text:p"; + + /** Element tag for <i>text:</i>. */ + public final static String TAG_TEXT = "text:"; + + /** Element tag for <i>text:h</i>. */ + public final static String TAG_HEADING = "text:h"; + + /** Element tag for <i>text:s</i>. */ + public final static String TAG_SPACE = "text:s"; + + /** Element tag for <i>text:tab-stop</i>. */ + public final static String TAG_TAB_STOP = "text:tab-stop"; + + /** Element tag for <i>text:line-break</i>. */ + public final static String TAG_LINE_BREAK = "text:line-break"; + + /** Element tag for <i>text:span</i>. */ + public final static String TAG_SPAN = "text:span"; + + /** Element tag for <i>text:a</i>. */ + public final static String TAG_HYPERLINK = "text:a"; + + /** Element tag for <i>text:bookmark</i>. */ + public final static String TAG_BOOKMARK = "text:bookmark"; + + /** Element tag for <i>text:bookmark-start</i>. */ + public final static String TAG_BOOKMARK_START = "text:bookmark-start"; + + /** Element tag for <i>text:unordered-list</i>. */ + public final static String TAG_UNORDERED_LIST = "text:unordered-list"; + + /** Element tag for <i>text:ordered-list</i>. */ + public final static String TAG_ORDERED_LIST = "text:ordered-list"; + + /** Element tag for <i>text:list-header</i>. */ + public final static String TAG_LIST_HEADER = "text:list-header"; + + /** Element tag for <i>text:list-item</i>. */ + public final static String TAG_LIST_ITEM = "text:list-item"; + + /** Attribute tag for <i>text:c</i> of element <i>text:s</i>. */ + public final static String ATTRIBUTE_SPACE_COUNT = "text:c"; + + /** + * Attribute tag for <i>text:style-name</i> of element + * <i>text:style-name</i>. + */ + public final static String ATTRIBUTE_TEXT_STYLE_NAME = "text:style-name"; + + /** Element tag for <i>table:table</i>. */ + public final static String TAG_TABLE = "table:table"; + + /** Element tag for <i>table:named-expression</i>. */ + public final static String TAG_NAMED_EXPRESSIONS = "table:named-expressions"; + + /** Element tag for <i>table:named-range</i>. */ + public final static String TAG_TABLE_NAMED_RANGE= "table:named-range"; + + /** Element tag for <i>table:named-expression</i>. */ + public final static String TAG_TABLE_NAMED_EXPRESSION= "table:named-expression"; + + /** + * Attribute tag for <i>table:name</i> of element + * <i>table:table</i>. + */ + public final static String ATTRIBUTE_TABLE_NAME = "table:name"; + + /** + * Attribute tag for <i>table:expression</i> of element + * <i>table:named-range</i>. + */ + public final static String ATTRIBUTE_TABLE_EXPRESSION = "table:expression"; + + /** + * Attribute tag for <i>table:base-cell-address</i> of element + * <i>table:named-range</i>. + */ + public final static String ATTRIBUTE_TABLE_BASE_CELL_ADDRESS = "table:base-cell-address"; + + /** + * Attribute tag for <i>table:cell-range-address</i> of element + * <i>table:named-range</i>. + */ + public final static String ATTRIBUTE_TABLE_CELL_RANGE_ADDRESS = "table:cell-range-address"; + + /** Element tag for <i>table:table-row</i>. */ + public final static String TAG_TABLE_ROW = "table:table-row"; + + /** Element tag for <i>table:table-column</i>. */ + public final static String TAG_TABLE_COLUMN = "table:table-column"; + + /** + * Attribute tag for <i>table:default-cell-style-name</i> + * of element <i>table:table-column</i>. + */ + public final static String ATTRIBUTE_DEFAULT_CELL_STYLE = "table:default-cell-style-name"; + + /** Element tag for <i>table:scenario</i>. */ + public final static String TAG_TABLE_SCENARIO = "table:scenario"; + + /** Element tag for <i>table:table-cell</i>. */ + public final static String TAG_TABLE_CELL = "table:table-cell"; + + /** + * Attribute tag for <i>table:value-type</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_VALUE_TYPE = "table:value-type"; + + /** + * Attribute tag for <i>table:number-columns-repeated</i> + * of element <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_NUM_COLUMNS_REPEATED = + "table:number-columns-repeated"; + + /** + * Attribute tag for <i>table:number-rows-repeated</i> + * of element <i>table:table-row</i>. + */ + public final static String ATTRIBUTE_TABLE_NUM_ROWS_REPEATED = + "table:number-rows-repeated"; + + /** + * Attribute tag for <i>table:formula</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_FORMULA = "table:formula"; + + /** + * Attribute tag for <i>table:value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_VALUE = "table:value"; + + /** + * Attribute tag for <i>table:date-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_DATE_VALUE = "table:date-value"; + + /** + * Attribute tag for <i>table:time-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_TIME_VALUE = "table:time-value"; + + /** + * Attribute tag for <i>table:string-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_STRING_VALUE = + "table:string-value"; + + /** + * Attribute tag for <i>table:time-boolean-value</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_BOOLEAN_VALUE = + "table:boolean-value"; + + /** Attribute tag for <i>table:style-name</i> of table elements. */ + public final static String ATTRIBUTE_TABLE_STYLE_NAME = "table:style-name"; + + /** + * Attribute tag for <i>table:currency</i> of element + * <i>table:table-cell</i>. + */ + public final static String ATTRIBUTE_TABLE_CURRENCY = "table:currency"; + + /** The cell contains data of type <i>string</i>. */ + public final static String CELLTYPE_STRING = "string"; + + /** The cell contains data of type <i>float</i>. */ + public final static String CELLTYPE_FLOAT = "float"; + + /** The cell contains data of type <i>time</i>. */ + public final static String CELLTYPE_TIME = "time"; + + /** The cell contains data of type <i>date</i>. */ + public final static String CELLTYPE_DATE = "date"; + + /** The cell contains data of type <i>currency</i>. */ + public final static String CELLTYPE_CURRENCY = "currency"; + + /** The cell contains data of type <i>boolean</i>. */ + public final static String CELLTYPE_BOOLEAN = "boolean"; + + /** The cell contains data of type <i>percent</i>. */ + public final static String CELLTYPE_PERCENT = "percentage"; + + /** StarWriter XML file extension. */ + public final static String SXW_FILE_EXTENSION = ".sxw"; + + /** StarWriter XML <i>office:class</i> value. */ + public final static String SXW_TYPE = "text"; + + /** StarCalc XML file extension. */ + public final static String SXC_FILE_EXTENSION = ".sxc"; + + /** StarCalc XML <i>office:class</i> value. */ + public final static String SXC_TYPE = "spreadsheet"; + + /** Element tag for <i>manifest:manifest</i>entry in Manifest XML */ + public final static String TAG_MANIFEST_ROOT = "manifest:manifest"; + + /** Element tag for <i>manifest:file-entry</i> entry in Manifest XML. */ + public final static String TAG_MANIFEST_FILE = "manifest:file-entry"; + + /** + * Attribute tag for <i>manifest:media-type</i> of element + * <i>manifest:file-entry</i>. + */ + public final static String ATTRIBUTE_MANIFEST_FILE_TYPE = "manifest:media-type"; + + /** + * Attribute tag for <i>manifest:full-path</i> of element + * <i>manifest:file-entry</i>. + */ + public final static String ATTRIBUTE_MANIFEST_FILE_PATH = "manifest:full-path"; + + // Tags and Elements for the settings.xml + + /** Element tag for <i>config:config-item</i>. */ + public final static String TAG_CONFIG_ITEM = "config:config-item"; + + /** Element tag for <i>config:config-item-set</i>. */ + public final static String TAG_CONFIG_ITEM_SET = "config:config-item-set"; + + /** Element tag for <i>config:config-item-map-indexed</i>. */ + public final static String TAG_CONFIG_ITEM_MAP_INDEXED = "config:config-item-map-indexed"; + + /** Element tag for <i>config:config-item-map-named</i>. */ + public final static String TAG_CONFIG_ITEM_MAP_NAMED = "config:config-item-map-named"; + + /** Element tag for <i>config:config-item-map-entry</i>. */ + public final static String TAG_CONFIG_ITEM_MAP_ENTRY= "config:config-item-map-entry"; + + /** + * Attribute tag for <i>config:name</i> of element + * <i>config:config-item</i>. + */ + public final static String ATTRIBUTE_CONFIG_NAME = "config:name"; + + /** + * Attribute tag for <i>config:type</i> of element + * <i>config:config-item</i>. + */ + public final static String ATTRIBUTE_CONFIG_TYPE = "config:type"; + + + /** StarWriter XML MIME type. */ + public final static String SXW_MIME_TYPE = "application/vnd.sun.xml.writer"; + + /** StarWriter XML Template MIME type. */ + public final static String STW_MIME_TYPE = "application/vnd.sun.xml.writer.template"; + + /** StarCalc XML MIME type. */ + public final static String SXC_MIME_TYPE = "application/vnd.sun.xml.calc"; + + /** StarCalc XML Template MIME type. */ + public final static String STC_MIME_TYPE = "application/vnd.sun.xml.calc.template"; + + /** StarImpress XML MIME type. */ + public final static String SXI_MIME_TYPE = "application/vnd.sun.xml.impress"; + + /** StarImpress XML Template MIME type. */ + public final static String STI_MIME_TYPE = "application/vnd.sun.xml.impress.template"; + + /** StarDraw XML MIME type. */ + public final static String SXD_MIME_TYPE = "application/vnd.sun.xml.draw"; + + /** StarMath XML MIME type. */ + public final static String SXM_MIME_TYPE = "application/vnd.sun.xml.math"; + + /** StarWriter Global XML MIME Type */ + public final static String SXG_MIME_TYPE = "application/vnd.sun.xml.writer.global"; +} diff --git a/source/java/writer2latex/xmerge/OfficeDocument.java b/source/java/writer2latex/xmerge/OfficeDocument.java new file mode 100644 index 0000000..1b2ee49 --- /dev/null +++ b/source/java/writer2latex/xmerge/OfficeDocument.java @@ -0,0 +1,1287 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for Writer2LaTeX +// Version 1.0 (2008-11-22) + +package writer2latex.xmerge; + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.BufferedReader; +import java.io.StringReader; +import java.io.InputStreamReader; +//import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +//import java.util.zip.ZipEntry; +//import java.util.zip.ZipInputStream; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.Node; +import org.w3c.dom.Element; +import org.w3c.dom.Document; +import org.w3c.dom.DOMImplementation; +import org.w3c.dom.DocumentType; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.w3c.dom.NamedNodeMap; +import org.xml.sax.SAXException; +//import org.xml.sax.SAXParseException; + +import writer2latex.office.MIMETypes; +import writer2latex.util.Misc; + +//import org.openoffice.xmerge.util.Resources; +//import org.openoffice.xmerge.util.Debug; + +/** + * An implementation of <code>Document</code> for + * StarOffice documents. + */ +public class OfficeDocument + implements writer2latex.xmerge.Document, OfficeConstants { + + /** Factory for <code>DocumentBuilder</code> objects. */ + private static DocumentBuilderFactory factory = + DocumentBuilderFactory.newInstance(); + + /** DOM <code>Document</code> of content.xml. */ + private Document contentDoc = null; + + /** DOM <code>Document</code> of meta.xml. */ + private Document metaDoc = null; + + /** DOM <code>Document</code> of settings.xml. */ + private Document settingsDoc = null; + + /** DOM <code>Document</code> of content.xml. */ + private Document styleDoc = null; + + /** DOM <code>Docuemtn</code> of META-INF/manifest.xml. */ + private Document manifestDoc = null; + + private String documentName = null; + private String fileName = null; + + /** Resources object. */ + //private Resources res = null; + + /** + * <code>OfficeZip</code> object to store zip contents from + * read <code>InputStream</code>. Note that this member + * will still be null if it was initialized using a template + * file instead of reading from a StarOffice zipped + * XML file. + */ + private OfficeZip zip = null; + + /** Collection to keep track of the embedded objects in the document. */ + private Map embeddedObjects = null; + + /** + * Default constructor. + * + * @param name <code>Document</code> name. + */ + public OfficeDocument(String name) + { + this(name, true, false); + } + + + /** + * Constructor with arguments to set <code>namespaceAware</code> + * and <code>validating</code> flags. + * + * @param name <code>Document</code> name (may or may not + * contain extension). + * @param namespaceAware Value for <code>namespaceAware</code> flag. + * @param validating Value for <code>validating</code> flag. + */ + public OfficeDocument(String name, boolean namespaceAware, boolean validating) { + + //res = Resources.getInstance(); + factory.setValidating(validating); + factory.setNamespaceAware(namespaceAware); + this.documentName = trimDocumentName(name); + this.fileName = documentName + getFileExtension(); + } + + + /** + * Removes the file extension from the <code>Document</code> + * name. + * + * @param name Full <code>Document</code> name with extension. + * + * @return Name of <code>Document</code> without the extension. + */ + private String trimDocumentName(String name) { + String temp = name.toLowerCase(); + String ext = getFileExtension(); + + if (temp.endsWith(ext)) { + // strip the extension + int nlen = name.length(); + int endIndex = nlen - ext.length(); + name = name.substring(0,endIndex); + } + + return name; + } + + // FIX2 (HJ): Determine wether this is package or flat format + /** Package or flat format? + * @return true if the document is in package format, false if it's flat xml + */ + public boolean isPackageFormat() { return zip!=null; } + + /** + * Return a DOM <code>Document</code> object of the content.xml + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getContentDOM() { + + return contentDoc; + } + + /** + * Return a DOM <code>Document</code> object of the meta.xml + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getMetaDOM() { + + return metaDoc; + } + + + /** + * Return a DOM <code>Document</code> object of the settings.xml + * file. Note that a content DOM is not created when the constructor + * is called. So, either the <code>read</code> method or the + * <code>initContentDOM</code> method will need to be called ahead + * on this object before calling this method. + * + * @return DOM <code>Document</code> object. + */ + public Document getSettingsDOM() { + + return settingsDoc; + } + + + /** + * Sets the content tree of the document. + * + * @param newDom <code>Node</code> containing the new content tree. + */ + public void setContentDOM( Node newDom) { + contentDoc = (Document)newDom; + } + + + /** + * Sets the meta tree of the document. + * + * @param newDom <code>Node</code> containing the new meta tree. + */ + public void setMetaDOM (Node newDom) { + metaDoc = (Document)newDom; + } + + + /** + * Sets the settings tree of the document. + * + * @param newDom <code>Node</code> containing the new settings tree. + */ + public void setSettingsDOM (Node newDom) { + settingsDoc = (Document)newDom; + } + + + /** + * Sets the style tree of the document. + * + * @param newDom <code>Node</code> containing the new style tree. + */ + public void setStyleDOM (Node newDom) { + styleDoc = (Document)newDom; + } + + + /** + * Return a DOM <code>Document</code> object of the style.xml file. + * Note that this may return null if there is no style DOM. + * Note that a style DOM is not created when the constructor + * is called. Depending on the <code>InputStream</code>, a + * <code>read</code> method may or may not build a style DOM. When + * creating a new style DOM, call the <code>initStyleDOM</code> method + * first. + * + * @return DOM <code>Document</code> object. + */ + public Document getStyleDOM() { + + return styleDoc; + } + + + /** + * Return the name of the <code>Document</code>. + * + * @return The name of <code>Document</code>. + */ + public String getName() { + + return documentName; + } + + + /** + * Return the file name of the <code>Document</code>, possibly + * with the standard extension. + * + * @return The file name of <code>Document</code>. + */ + public String getFileName() { + + return fileName; + } + + + /** + * Returns the file extension for this type of + * <code>Document</code>. + * + * @return The file extension of <code>Document</code>. + */ + // TODO: is this used? + protected String getFileExtension() { return ""; } + + + /** + * Returns all the embedded objects (graphics, formulae, etc.) present in + * this document. + * + * @return An <code>Iterator</code> of <code>EmbeddedObject</code> objects. + */ + public Iterator getEmbeddedObjects() { + + if (embeddedObjects == null && manifestDoc != null) { + embeddedObjects = new HashMap(); + + // Need to read the manifest file and construct a list of objects + NodeList nl = manifestDoc.getElementsByTagName(TAG_MANIFEST_FILE); + + // Dont create the HashMap if there are no embedded objects + int len = nl.getLength(); + for (int i = 0; i < len; i++) { + Node n = nl.item(i); + + NamedNodeMap attrs = n.getAttributes(); + + String type = attrs.getNamedItem(ATTRIBUTE_MANIFEST_FILE_TYPE).getNodeValue(); + String path = attrs.getNamedItem(ATTRIBUTE_MANIFEST_FILE_PATH).getNodeValue(); + + + /* + * According to OpenOffice.org XML File Format document (ver. 1) + * there are only two types of embedded object: + * + * Objects with an XML representation. + * Objects without an XML representation. + * + * The former are represented by one or more XML files. + * The latter are in binary form. + */ + // FIX2 (HJ): Allow either OOo 1.x or OpenDocument embedded objects + if (type.startsWith("application/vnd.sun.xml") || type.startsWith("application/vnd.oasis.opendocument")) + { + if (path.equals("/")) { + // Exclude the main document entries + continue; + } + // Take off the trailing '/' + String name = path.substring(0, path.length() - 1); + embeddedObjects.put(name, new EmbeddedXMLObject(name, type, zip)); + } + else if (type.equals("text/xml")) { + // XML entries are either embedded StarOffice doc entries or main + // document entries + continue; + } + else { // FIX (HJ): allows empty MIME type + embeddedObjects.put(path, new EmbeddedBinaryObject(path, type, zip)); + } + } + } + + return embeddedObjects.values().iterator(); + } + + /** + * Returns the embedded object corresponding to the name provided. + * The name should be stripped of any preceding path characters, such as + * '/', '.' or '#'. + * + * @param name The name of the embedded object to retrieve. + * + * @return An <code>EmbeddedObject</code> instance representing the named + * object. + */ + public EmbeddedObject getEmbeddedObject(String name) { + if (name == null) { + return null; + } + + if (embeddedObjects == null) { + // FIX2 (HJ): Return null if there's no manifest + if (manifestDoc != null) { + getEmbeddedObjects(); + } + else { + return null; + } + } + + if (embeddedObjects.containsKey(name)) { + return (EmbeddedObject)embeddedObjects.get(name); + } + else { + return null; + } + } + + + /** + * Adds a new embedded object to the document. + * + * @param embObj An instance of <code>EmbeddedObject</code>. + */ + public void addEmbeddedObject(EmbeddedObject embObj) { + if (embObj == null) { + return; + } + + if (embeddedObjects == null) { + embeddedObjects = new HashMap(); + } + + embeddedObjects.put(embObj.getName(), embObj); + } + + + /** + * Read the Office <code>Document</code> from the given + * <code>InputStream</code>. + * FIX3 (HJ): Perform simple type detection to determine package or flat format + * + * @param is Office document <code>InputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is) throws IOException { + byte[] doc = Misc.inputStreamToByteArray(is); + boolean bZip = MIMETypes.ZIP.equals(MIMETypes.getMagicMIMEType(doc)); + // if it's zip, assume package - otherwise assume flat + read(new ByteArrayInputStream(doc),bZip); + } + + private void readZip(InputStream is) throws IOException { + + // Debug.log(Debug.INFO, "reading Office file"); + + DocumentBuilder builder = null; + + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException ex) { + throw new OfficeDocumentException(ex); + } + + // read in Office zip file format + + zip = new OfficeZip(); + zip.read(is); + + // grab the content.xml and + // parse it into contentDoc. + + byte contentBytes[] = zip.getContentXMLBytes(); + + if (contentBytes == null) { + + throw new OfficeDocumentException("Entry content.xml not found in file"); + } + + try { + + contentDoc = parse(builder, contentBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + + // if style.xml exists, grab the style.xml + // parse it into styleDoc. + + byte styleBytes[] = zip.getStyleXMLBytes(); + + if (styleBytes != null) { + + try { + + styleDoc = parse(builder, styleBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + } + + byte metaBytes[] = zip.getMetaXMLBytes(); + + if (metaBytes != null) { + + try { + + metaDoc = parse(builder, metaBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + } + + byte settingsBytes[] = zip.getSettingsXMLBytes(); + + if (settingsBytes != null) { + + try { + + settingsDoc = parse(builder, settingsBytes); + + } catch (SAXException ex) { + + throw new OfficeDocumentException(ex); + } + } + + + // Read in the META-INF/manifest.xml file + byte manifestBytes[] = zip.getManifestXMLBytes(); + + if (manifestBytes != null) { + + try { + manifestDoc = parse(builder, manifestBytes); + } catch (SAXException ex) { + throw new OfficeDocumentException(ex); + } + } + + } + + + /** + * Read the Office <code>Document</code> from the given + * <code>InputStream</code>. + * + * @param is Office document <code>InputStream</code>. + * @param isZip <code>boolean</code> Identifies whether + * a file is zipped or not + * + * @throws IOException If any I/O error occurs. + */ + public void read(InputStream is, boolean isZip) throws IOException { + + // Debug.log(Debug.INFO, "reading Office file"); + + DocumentBuilder builder = null; + + try { + builder = factory.newDocumentBuilder(); + } catch (ParserConfigurationException ex) { + throw new OfficeDocumentException(ex); + } + + if (isZip) + { + readZip(is); + } + else{ + try{ + //contentDoc= builder.parse((InputStream)is); + + Reader r = secondHack(is); + InputSource ins = new InputSource(r); + org.w3c.dom.Document newDoc = builder.parse(ins); + //org.w3c.dom.Document newDoc = builder.parse((InputStream)is); + Element rootElement=newDoc.getDocumentElement(); + + NodeList nodeList; + Node tmpNode; + Node rootNode = (Node)rootElement; + if (newDoc !=null){ + /*content*/ + contentDoc = createDOM(TAG_OFFICE_DOCUMENT_CONTENT); + rootElement=contentDoc.getDocumentElement(); + rootNode = (Node)rootElement; + + // FIX (HJ): Include office:font-decls in content DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_FONT_DECLS); + if (nodeList.getLength()>0){ + tmpNode = contentDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + // FIX2 (HJ): Include office:font-face-decls (OpenDocument) in content DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_FONT_FACE_DECLS); + if (nodeList.getLength()>0){ + tmpNode = contentDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nodeList.getLength()>0){ + tmpNode = contentDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_BODY); + if (nodeList.getLength()>0){ + tmpNode = contentDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + /*Styles*/ + styleDoc = createDOM(TAG_OFFICE_DOCUMENT_STYLES); + rootElement=styleDoc.getDocumentElement(); + rootNode = (Node)rootElement; + + // FIX (HJ): Include office:font-decls in styles DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_FONT_DECLS); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + // FIX2 (HJ): Include office:font-face-decls in styles DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_FONT_FACE_DECLS); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_STYLES); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + // FIX (HJ): Include office:automatic-styles in styles DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + // FIX (HJ): Include office:master-styles in styles DOM + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_MASTER_STYLES); + if (nodeList.getLength()>0){ + tmpNode = styleDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + /*Settings*/ + settingsDoc = createDOM(TAG_OFFICE_DOCUMENT_SETTINGS); + rootElement=settingsDoc.getDocumentElement(); + rootNode = (Node)rootElement; + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_SETTINGS); + if (nodeList.getLength()>0){ + tmpNode = settingsDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + /*Meta*/ + metaDoc = createDOM(TAG_OFFICE_DOCUMENT_META); + rootElement=metaDoc.getDocumentElement(); + rootNode = (Node)rootElement; + nodeList= newDoc.getElementsByTagName(TAG_OFFICE_META); + if (nodeList.getLength()>0){ + tmpNode = metaDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } + } + catch (SAXException ex) { + throw new OfficeDocumentException(ex); + } + } + + } + + + + /** + * Parse given <code>byte</code> array into a DOM + * <code>Document</code> object using the + * <code>DocumentBuilder</code> object. + * + * @param builder <code>DocumentBuilder</code> object for parsing. + * @param bytes <code>byte</code> array for parsing. + * + * @return Resulting DOM <code>Document</code> object. + * + * @throws SAXException If any parsing error occurs. + */ + static Document parse(DocumentBuilder builder, byte bytes[]) + throws SAXException, IOException { + + Document doc = null; + + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + + // TODO: replace hack with a more appropriate fix. + + Reader r = hack(is); + InputSource ins = new InputSource(r); + doc = builder.parse(ins); + + return doc; + } + + + /** + * Method to return the MIME type of the document. + * + * @return String The document's MIME type. + */ + // not really used... + protected String getDocumentMimeType() { return ""; } + + + /** + * Write out Office ZIP file format. + * + * @param os XML <code>OutputStream</code>. + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os) throws IOException { + if (zip == null) { + zip = new OfficeZip(); + } + + initManifestDOM(); + + Element domEntry; + Element manifestRoot = manifestDoc.getDocumentElement(); + + // The EmbeddedObjects come first. + Iterator embObjs = getEmbeddedObjects(); + while (embObjs.hasNext()) { + EmbeddedObject obj = (EmbeddedObject)embObjs.next(); + obj.writeManifestData(manifestDoc); + + obj.write(zip); + } + + // Add in the entry for the Pictures directory. Always present. + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "Pictures/"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, ""); + manifestRoot.appendChild(domEntry); + + // Write content to the Zip file and then write any of the optional + // data, if it exists. + zip.setContentXMLBytes(docToBytes(contentDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "content.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + + manifestRoot.appendChild(domEntry); + + if (styleDoc != null) { + zip.setStyleXMLBytes(docToBytes(styleDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "styles.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + manifestRoot.appendChild(domEntry); + } + + if (metaDoc != null) { + zip.setMetaXMLBytes(docToBytes(metaDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "meta.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + manifestRoot.appendChild(domEntry); + } + + if (settingsDoc != null) { + zip.setSettingsXMLBytes(docToBytes(settingsDoc)); + + domEntry = manifestDoc.createElement(TAG_MANIFEST_FILE); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "settings.xml"); + domEntry.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, "text/xml"); + manifestRoot.appendChild(domEntry); + } + + zip.setManifestXMLBytes(docToBytes(manifestDoc)); + + zip.write(os); + } + + + /** + * Write out Office ZIP file format. + * + * @param os XML <code>OutputStream</code>. + * @param isZip <code>boolean</code> + * + * @throws IOException If any I/O error occurs. + */ + public void write(OutputStream os, boolean isZip) throws IOException { + + // Create an OfficeZip object if one does not exist. + if (isZip){ + write(os); + } + else{ + try{ + DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder builder= builderFactory.newDocumentBuilder(); + DOMImplementation domImpl = builder.getDOMImplementation(); + domImpl.createDocumentType("office:document","-//OpenOffice.org//DTD OfficeDocument 1.0//EN",null); + org.w3c.dom.Document newDoc = domImpl.createDocument("http://openoffice.org/2000/office","office:document",null); + + + Element rootElement=newDoc.getDocumentElement(); + rootElement.setAttribute("xmlns:office","http://openoffice.org/2000/office"); + rootElement.setAttribute("xmlns:style","http://openoffice.org/2000/style" ); + rootElement.setAttribute("xmlns:text","http://openoffice.org/2000/text"); + rootElement.setAttribute("xmlns:table","http://openoffice.org/2000/table"); + + rootElement.setAttribute("xmlns:draw","http://openoffice.org/2000/drawing"); + rootElement.setAttribute("xmlns:fo","http://www.w3.org/1999/XSL/Format" ); + rootElement.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink" ); + rootElement.setAttribute("xmlns:dc","http://purl.org/dc/elements/1.1/" ); + rootElement.setAttribute("xmlns:meta","http://openoffice.org/2000/meta" ); + rootElement.setAttribute("xmlns:number","http://openoffice.org/2000/datastyle" ); + rootElement.setAttribute("xmlns:svg","http://www.w3.org/2000/svg" ); + rootElement.setAttribute("xmlns:chart","http://openoffice.org/2000/chart" ); + rootElement.setAttribute("xmlns:dr3d","http://openoffice.org/2000/dr3d" ); + rootElement.setAttribute("xmlns:math","http://www.w3.org/1998/Math/MathML" ); + rootElement.setAttribute("xmlns:form","http://openoffice.org/2000/form" ); + rootElement.setAttribute("xmlns:script","http://openoffice.org/2000/script" ); + rootElement.setAttribute("xmlns:config","http://openoffice.org/2001/config" ); + // #i41033# OASIS format needs the "office:class" set. + if(getDocumentMimeType() == SXC_MIME_TYPE) + rootElement.setAttribute("office:class","spreadsheet" ); + else if(getDocumentMimeType() == SXW_MIME_TYPE) + rootElement.setAttribute("office:class","text" ); + rootElement.setAttribute("office:version","1.0"); + + + NodeList nodeList; + Node tmpNode; + Node rootNode = (Node)rootElement; + if (metaDoc !=null){ + nodeList= metaDoc.getElementsByTagName(TAG_OFFICE_META); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + }if (styleDoc !=null){ + nodeList= styleDoc.getElementsByTagName(TAG_OFFICE_STYLES); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + }if (settingsDoc !=null){ + nodeList= settingsDoc.getElementsByTagName(TAG_OFFICE_SETTINGS); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } + if (contentDoc !=null){ + nodeList= contentDoc.getElementsByTagName(TAG_OFFICE_AUTOMATIC_STYLES); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + + nodeList= contentDoc.getElementsByTagName(TAG_OFFICE_BODY); + if (nodeList.getLength()>0){ + tmpNode = newDoc.importNode(nodeList.item(0),true); + rootNode.appendChild(tmpNode); + } + } + + byte contentBytes[] = docToBytes(newDoc); + os.write(contentBytes); + } + catch(Exception exc){ + System.err.println("\nException in OfficeDocument.write():" +exc); + } + //byte contentBytes[] = docToBytes(contentDoc); + } + } + + + /** + * <p>Write out a <code>org.w3c.dom.Document</code> object into a + * <code>byte</code> array.</p> + * + * <p>TODO: remove dependency on com.sun.xml.tree.XmlDocument + * package!</p> + * + * @param Document DOM <code>Document</code> object. + * + * @return <code>byte</code> array of DOM <code>Document</code> + * object. + * + * @throws IOException If any I/O error occurs. + */ + static byte[] docToBytes(Document doc) + throws IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + java.lang.reflect.Constructor con; + java.lang.reflect.Method meth; + + String domImpl = doc.getClass().getName(); + + /* + * We may have multiple XML parsers in the Classpath. + * Depending on which one is first, the actual type of + * doc may vary. Need a way to find out which API is being + * used and use an appropriate serialization method. + */ + try { + // First of all try for JAXP 1.0 + if (domImpl.equals("com.sun.xml.tree.XmlDocument")) { + + // Debug.log(Debug.INFO, "Using JAXP"); + + Class jaxpDoc = Class.forName("com.sun.xml.tree.XmlDocument"); + + // The method is in the XMLDocument class itself, not a helper + meth = jaxpDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.crimson.tree.XmlDocument")) + { + // Debug.log(Debug.INFO, "Using Crimson"); + + Class crimsonDoc = Class.forName("org.apache.crimson.tree.XmlDocument"); + // The method is in the XMLDocument class itself, not a helper + meth = crimsonDoc.getMethod("write", + new Class[] { Class.forName("java.io.OutputStream") } ); + + meth.invoke(doc, new Object [] { baos } ); + } + else if (domImpl.equals("org.apache.xerces.dom.DocumentImpl") + || domImpl.equals("org.apache.xerces.dom.DeferredDocumentImpl")) { + + // Debug.log(Debug.INFO, "Using Xerces"); + + // Try for Xerces + Class xercesSer = + Class.forName("org.apache.xml.serialize.XMLSerializer"); + + // Get the OutputStream constructor + // May want to use the OutputFormat parameter at some stage too + con = xercesSer.getConstructor(new Class [] + { Class.forName("java.io.OutputStream"), + Class.forName("org.apache.xml.serialize.OutputFormat") } ); + + + // Get the serialize method + meth = xercesSer.getMethod("serialize", + new Class [] { Class.forName("org.w3c.dom.Document") } ); + + + // Get an instance + Object serializer = con.newInstance(new Object [] { baos, null } ); + + + // Now call serialize to write the document + meth.invoke(serializer, new Object [] { doc } ); + } + else { + // We don't have another parser + throw new IOException("No appropriate API (JAXP/Xerces) to serialize XML document: " + domImpl); + } + } + catch (ClassNotFoundException cnfe) { + throw new IOException(cnfe.toString()); + } + catch (Exception e) { + // We may get some other errors, but the bottom line is that + // the steps being executed no longer work + throw new IOException(e.toString()); + } + + byte bytes[] = baos.toByteArray(); + + return bytes; + } + + + /** + * Initializes a new DOM <code>Document</code> with the content + * containing minimum OpenOffice XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initContentDOM() throws IOException { + + contentDoc = createDOM(TAG_OFFICE_DOCUMENT_CONTENT); + + // this is a work-around for a bug in Office6.0 - not really + // needed but StarCalc 6.0 will crash without this tag. + Element root = contentDoc.getDocumentElement(); + + Element child = contentDoc.createElement(TAG_OFFICE_FONT_DECLS); + root.appendChild(child); + + child = contentDoc.createElement(TAG_OFFICE_AUTOMATIC_STYLES); + root.appendChild(child); + + child = contentDoc.createElement(TAG_OFFICE_BODY); + root.appendChild(child); + } + + /** + * Initializes a new DOM <code>Document</code> with the content + * containing minimum OpenOffice XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initSettingsDOM() throws IOException { + + settingsDoc = createSettingsDOM(TAG_OFFICE_DOCUMENT_SETTINGS); + + // this is a work-around for a bug in Office6.0 - not really + // needed but StarCalc 6.0 will crash without this tag. + Element root = settingsDoc.getDocumentElement(); + + Element child = settingsDoc.createElement(TAG_OFFICE_SETTINGS); + root.appendChild(child); + } + + /** + * Initializes a new DOM Document with styles + * containing minimum OpenOffice XML tags. + * + * @throws IOException If any I/O error occurs. + */ + public final void initStyleDOM() throws IOException { + + styleDoc = createDOM(TAG_OFFICE_DOCUMENT_STYLES); + } + + /** + * <p>Creates a new DOM <code>Document</code> containing minimum + * OpenOffice XML tags.</p> + * + * <p>This method uses the subclass + * <code>getOfficeClassAttribute</code> method to get the + * attribute for <i>office:class</i>.</p> + * + * @param rootName root name of <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + private final Document createSettingsDOM(String rootName) throws IOException { + + Document doc = null; + + try { + + DocumentBuilder builder = factory.newDocumentBuilder(); + doc = builder.newDocument(); + + } catch (ParserConfigurationException ex) { + + throw new OfficeDocumentException(ex); + + } + + Element root = (Element) doc.createElement(rootName); + doc.appendChild(root); + + root.setAttribute("xmlns:office", "http://openoffice.org/2000/office"); + root.setAttribute("xmlns:xlink", "http://openoffice.org/1999/xlink"); + root.setAttribute("xmlns:config", "http://openoffice.org/2001/config"); + root.setAttribute("office:version", "1.0"); + + return doc; + } + + + /** + * <p>Creates a new DOM <code>Document</code> containing minimum + * OpenOffice XML tags.</p> + * + * <p>This method uses the subclass + * <code>getOfficeClassAttribute</code> method to get the + * attribute for <i>office:class</i>.</p> + * + * @param rootName root name of <code>Document</code>. + * + * @throws IOException If any I/O error occurs. + */ + private final Document createDOM(String rootName) throws IOException { + + Document doc = null; + + try { + + DocumentBuilder builder = factory.newDocumentBuilder(); + doc = builder.newDocument(); + + } catch (ParserConfigurationException ex) { + + throw new OfficeDocumentException(ex); + + } + + Element root = (Element) doc.createElement(rootName); + doc.appendChild(root); + + root.setAttribute("xmlns:office", "http://openoffice.org/2000/office"); + root.setAttribute("xmlns:style", "http://openoffice.org/2000/style"); + root.setAttribute("xmlns:text", "http://openoffice.org/2000/text"); + root.setAttribute("xmlns:table", "http://openoffice.org/2000/table"); + root.setAttribute("xmlns:draw", "http://openoffice.org/2000/drawing"); + root.setAttribute("xmlns:fo", "http://www.w3.org/1999/XSL/Format"); + root.setAttribute("xmlns:xlink", "http://www.w3.org/1999/xlink"); + root.setAttribute("xmlns:number", "http://openoffice.org/2000/datastyle"); + root.setAttribute("xmlns:svg", "http://www.w3.org/2000/svg"); + root.setAttribute("xmlns:chart", "http://openoffice.org/2000/chart"); + root.setAttribute("xmlns:dr3d", "http://openoffice.org/2000/dr3d"); + root.setAttribute("xmlns:math", "http://www.w3.org/1998/Math/MathML"); + root.setAttribute("xmlns:form", "http://openoffice.org/2000/form"); + root.setAttribute("xmlns:script", "http://openoffice.org/2000/script"); + root.setAttribute("office:class", getOfficeClassAttribute()); + root.setAttribute("office:version", "1.0"); + + return doc; + } + + + /** + * Return the <i>office:class</i> attribute value. + * + * @return The attribute value. + */ + // not really used... + protected String getOfficeClassAttribute() { return ""; } + + + /** + * <p>Hacked code to filter <!DOCTYPE> tag before + * sending stream to parser.</p> + * + * <p>This hacked code needs to be changed later on.</p> + * + * <p>Issue: using current jaxp1.0 parser, there is no way + * to turn off processing of dtds. Current set of dtds + * have bugs, processing them will throw exceptions.</p> + * + * <p>This is a simple hack that assumes the whole <!DOCTYPE> + * tag are all in the same line. This is sufficient for + * current StarOffice 6.0 generated XML files. Since this + * hack really needs to go away, I don't want to spend + * too much time in making it a perfect hack.</p> + * FIX (HJ): Removed requirement for DOCTYPE to be in one line + * FIX (HJ): No longer removes newlines + * + * @param is <code>InputStream</code> to be filtered. + * + * @return Reader value without the <!DOCTYPE> tag. + * + * @throws IOException If any I/O error occurs. + */ + private static Reader hack(InputStream is) throws IOException { + + BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + StringBuffer buffer = new StringBuffer(); + + String str = null; + + while ((str = br.readLine()) != null) { + + int sIndex = str.indexOf("<!DOCTYPE"); + + if (sIndex > -1) { + + buffer.append(str.substring(0, sIndex)); + + int eIndex = str.indexOf('>', sIndex + 8 ); + + if (eIndex > -1) { + + buffer.append(str.substring(eIndex + 1, str.length())); + // FIX (HJ): Preserve the newline + buffer.append("\n"); + + } else { + + // FIX (HJ): More than one line. Search for '>' in following lines + boolean bOK = false; + while ((str = br.readLine())!=null) { + eIndex = str.indexOf('>'); + if (eIndex>-1) { + buffer.append(str.substring(eIndex+1)); + // FIX (HJ): Preserve the newline + buffer.append("\n"); + bOK = true; + break; + } + } + + if (!bOK) { throw new IOException("Invalid XML"); } + } + + } else { + + buffer.append(str); + // FIX (HJ): Preserve the newline + buffer.append("\n"); + } + } + + StringReader r = new StringReader(buffer.toString()); + return r; + } + + /** + * <p>Transform the InputStream to a Reader Stream.</p> + * + * <p>This hacked code needs to be changed later on.</p> + * + * <p>Issue: the new oasis input file stream means + * that the old input stream fails. see #i33702# </p> + * + * @param is <code>InputStream</code> to be filtered. + * + * @return Reader value of the InputStream(). + * + * @throws IOException If any I/O error occurs. + */ + private static Reader secondHack(InputStream is) throws IOException { + + BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8")); + char[] charArray = new char[4096]; + StringBuffer sBuf = new StringBuffer(); + int n = 0; + while ((n=br.read(charArray, 0, charArray.length)) > 0) + sBuf.append(charArray, 0, n); + + // ensure there is no trailing garbage after the end of the stream. + int sIndex = sBuf.lastIndexOf("</office:document>"); + sBuf.delete(sIndex, sBuf.length()); + sBuf.append("</office:document>"); + StringReader r = new StringReader(sBuf.toString()); + return r; + } + + + /** + * Method to create the initial entries in the manifest.xml file stored + * in an SX? file. + */ + private void initManifestDOM() throws IOException { + + try { + DocumentBuilder builder = factory.newDocumentBuilder(); + DOMImplementation domImpl = builder.getDOMImplementation(); + + DocumentType docType = domImpl.createDocumentType(TAG_MANIFEST_ROOT, + "-//OpenOffice.org//DTD Manifest 1.0//EN", + "Manifest.dtd"); + manifestDoc = domImpl.createDocument("manifest", TAG_MANIFEST_ROOT, docType); + } catch (ParserConfigurationException ex) { + throw new OfficeDocumentException(ex); + } + + // Add the <manifest:manifest> entry + Element manifestRoot = manifestDoc.getDocumentElement(); + + manifestRoot.setAttribute("xmlns:manifest", "http://openoffice.org/2001/manifest"); + + Element docRoot = manifestDoc.createElement(TAG_MANIFEST_FILE); + + docRoot.setAttribute(ATTRIBUTE_MANIFEST_FILE_PATH, "/"); + docRoot.setAttribute(ATTRIBUTE_MANIFEST_FILE_TYPE, getDocumentMimeType()); + + manifestRoot.appendChild(docRoot); + } +} + diff --git a/source/java/writer2latex/xmerge/OfficeDocumentException.java b/source/java/writer2latex/xmerge/OfficeDocumentException.java new file mode 100644 index 0000000..f3549a3 --- /dev/null +++ b/source/java/writer2latex/xmerge/OfficeDocumentException.java @@ -0,0 +1,145 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for Writer2LaTeX +// Version 1.0 (2008-11-22) + +package writer2latex.xmerge; + +import java.io.IOException; + +//import javax.xml.parsers.ParserConfigurationException; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +//import org.openoffice.xmerge.util.Resources; + +/** + * Used by OfficeDocument to encapsulate exceptions. It will add + * more details to the message string if it is of type + * <code>SAXParseException</code>. + * + * @author Herbie Ong + */ + +public final class OfficeDocumentException extends IOException { + + StringBuffer message = null; + + + /** + * Constructor, capturing additional information from the + * <code>SAXException</code>. + * + * @param e The <code>SAXException</code>. + */ + public OfficeDocumentException(SAXException e) { + super(e.toString()); + message = new StringBuffer(); + if (e instanceof SAXParseException) { + String msgParseError = + "PARSE_ERROR"; + String msgLine = + "LINE"; + String msgColumn = + "COLUMN"; + String msgPublicId = + "PUBLIC_ID"; + String msgSystemId = + "SYSTEM_ID"; + SAXParseException spe = (SAXParseException) e; + message.append(msgParseError); + message.append(": "); + message.append(msgLine); + message.append(": "); + message.append(spe.getLineNumber()); + message.append(", "); + message.append(msgColumn); + message.append(": "); + message.append(spe.getColumnNumber()); + message.append(", "); + message.append(msgSystemId); + message.append(": "); + message.append(spe.getSystemId()); + message.append(", "); + message.append(msgPublicId); + message.append(": "); + message.append(spe.getPublicId()); + message.append("\n"); + } + + // if there exists an embedded exception + Exception ex = e.getException(); + if (ex != null) { + message.append(ex.getMessage()); + } + } + + + /** + * Constructor, creates exception with provided message. + * + * @param s Message value for the exception. + */ + public OfficeDocumentException(String s) { + super(s); + } + + + /** + * Constructor, creates exception with the message + * corresponding to the message value of the provided + * exception. + * + * @param e The Exception. + */ + public OfficeDocumentException(Exception e) { + super(e.getMessage()); + } + + + /** + * Returns the message value for the <code>Exception</code>. + * + * @return The message value for the <code>Exception</code>. + */ + public String getMessage() { + return message.toString() + super.getMessage(); + } +} + diff --git a/source/java/writer2latex/xmerge/OfficeZip.java b/source/java/writer2latex/xmerge/OfficeZip.java new file mode 100644 index 0000000..6de0010 --- /dev/null +++ b/source/java/writer2latex/xmerge/OfficeZip.java @@ -0,0 +1,473 @@ +/************************************************************************ + * + * The Contents of this file are made available subject to the terms of + * + * - GNU Lesser General Public License Version 2.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +// This version is adapted for Writer2LaTeX +// Version 1.0 (2008-11-22) + +package writer2latex.xmerge; + +import java.util.List; +import java.util.ListIterator; +import java.util.LinkedList; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.CRC32; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; +import java.io.ByteArrayOutputStream; + +//import org.openoffice.xmerge.util.Debug; + +/** + * Class used by {@link + * org.openoffice.xmerge.converter.OfficeDocument + * OfficeDocument} to handle reading and writing + * from a ZIP file, as well as storing ZIP entries. + * + * @author Herbie Ong + */ +class OfficeZip { + + /** File name of the XML file in a zipped document. */ + private final static String CONTENTXML = "content.xml"; + + private final static String STYLEXML = "styles.xml"; + private final static String METAXML = "meta.xml"; + private final static String SETTINGSXML = "settings.xml"; + private final static String MANIFESTXML = "META-INF/manifest.xml"; + + private final static int BUFFERSIZE = 1024; + + private List entryList = null; + + private int contentIndex = -1; + private int styleIndex = -1; + private int metaIndex = -1; + private int settingsIndex = -1; + private int manifestIndex = -1; + + /** Default constructor. */ + OfficeZip() { + + entryList = new LinkedList(); + } + + + /** + * <p>Read each zip entry in the <code>InputStream</code> object + * and store in entryList both the <code>ZipEntry</code> object + * as well as the bits of each entry. Call this method before + * calling the <code>getContentXMLBytes</code> method or the + * <code>getStyleXMLBytes</code> method.</p> + * + * <p>Keep track of the CONTENTXML and STYLEXML using + * contentIndex and styleIndex, respectively.</p> + * + * @param is <code>InputStream</code> object to read. + * + * @throws IOException If any I/O error occurs. + */ + void read(InputStream is) throws IOException { + + ZipInputStream zis = new ZipInputStream(is); + ZipEntry ze = null; + int i = -1; + + while ((ze = zis.getNextEntry()) != null) { + + String name = ze.getName(); + //System.out.println("Found "+name); + + // Debug.log(Debug.TRACE, "reading entry: " + name); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + int len = 0; + byte buffer[] = new byte[BUFFERSIZE]; + + while ((len = zis.read(buffer)) > 0) { + baos.write(buffer, 0, len); + } + + byte bytes[] = baos.toByteArray(); + Entry entry = new Entry(ze,bytes); + + entryList.add(entry); + + i++; + + if (name.equalsIgnoreCase(CONTENTXML)) { + contentIndex = i; + } + else if (name.equalsIgnoreCase(STYLEXML)) { + styleIndex = i; + } + else if (name.equalsIgnoreCase(METAXML)) { + metaIndex = i; + } + else if (name.equalsIgnoreCase(SETTINGSXML)) { + settingsIndex = i; + } + else if (name.equalsIgnoreCase(MANIFESTXML)) { + manifestIndex = i; + } + + } + + zis.close(); + } + + + /** + * This method returns the CONTENTXML file in a + * <code>byte</code> array. It returns null if there is no + * CONTENTXML in this zip file. + * + * @return CONTENTXML in a <code>byte</code> array. + */ + byte[] getContentXMLBytes() { + + return getEntryBytes(contentIndex); + } + + + /** + * This method returns the STYLEXML file in a + * <code>byte</code> array. It returns null if there is + * no STYLEXML in this zip file. + * + * @return STYLEXML in a <code>byte</code> array. + */ + byte[] getStyleXMLBytes() { + + return getEntryBytes(styleIndex); + } + + /** + * This method returns the METAXML file in a + * <code>byte</code> array. It returns null if there is + * no METAXML in this zip file. + * + * @return METAXML in a <code>byte</code> array. + */ + byte[] getMetaXMLBytes() { + return getEntryBytes(metaIndex); + } + + /** + * This method returns the SETTINGSXML file in a + * <code>byte</code> array. It returns null if there is + * no SETTINGSXML in this zip file. + * + * @return SETTINGSXML in a <code>byte</code> array. + */ + byte[] getSettingsXMLBytes() { + return getEntryBytes(settingsIndex); + } + + /** + * This method returns the MANIFESTXML file in a <code>byte</code> array. + * It returns null if there is no MANIFESTXML in this zip file. + * + * @return MANIFESTXML in a <code>byte</code> array. + */ + byte[] getManifestXMLBytes() { + return getEntryBytes(manifestIndex); + } + + /** + * This method returns the bytes corresponding to the entry named in the + * parameter. + * + * @param name The name of the entry in the Zip file to retrieve. + * + * @return The data for the named entry in a <code>byte</code> array or + * <code>null</code> if no entry is found. + */ + byte[] getNamedBytes(String name) { + + // The list is not sorted, and sorting it for a binary search would + // invalidate the indices stored for the main files. + + // Could improve performance by caching the name and index when + // iterating through the ZipFile in read(). + for (int i = 0; i < entryList.size(); i++) { + Entry e = (Entry)entryList.get(i); + + if (e.zipEntry.getName().equals(name)) { + return getEntryBytes(i); + } + } + + return null; + } + + + /** + * This method sets the bytes for the named entry. It searches for a + * matching entry in the LinkedList. If no entry is found, a new one is + * created. + * + * Writing of data is defferred to setEntryBytes(). + * + * @param name The name of the entry to search for. + * @param bytes The new data to write. + */ + void setNamedBytes(String name, byte[] bytes) { + for (int i = 0; i < entryList.size(); i++) { + Entry e = (Entry)entryList.get(i); + + if (e.zipEntry.getName().equals(name)) { + setEntryBytes(i, bytes, name); + return; + } + } + + // If we're here, no entry was found. Call setEntryBytes with an index + // of -1 to insert a new entry. + setEntryBytes(-1, bytes, name); + } + + /** + * Used by the <code>getContentXMLBytes</code> method and the + * <code>getStyleXMLBytes</code> method to return the + * <code>byte</code> array from the corresponding + * <code>entry</code> in the <code>entryList</code>. + * + * @param index Index of <code>Entry</code> object in + * <code>entryList</code>. + * + * @return <code>byte</code> array associated in that + * <code>Entry</code> object or null, if there is + * not such <code>Entry</code>. + */ + private byte[] getEntryBytes(int index) { + + byte[] bytes = null; + + if (index > -1) { + Entry entry = (Entry) entryList.get(index); + bytes = entry.bytes; + } + return bytes; + } + + + /** + * Set or replace the <code>byte</code> array for the + * CONTENTXML file. + * + * @param bytes <code>byte</code> array for the + * CONTENTXML file. + */ + void setContentXMLBytes(byte bytes[]) { + + contentIndex = setEntryBytes(contentIndex, bytes, CONTENTXML); + } + + + /** + * Set or replace the <code>byte</code> array for the + * STYLEXML file. + * + * @param bytes <code>byte</code> array for the + * STYLEXML file. + */ + void setStyleXMLBytes(byte bytes[]) { + + styleIndex = setEntryBytes(styleIndex, bytes, STYLEXML); + } + + + /** + * Set or replace the <code>byte</code> array for the + * METAXML file. + * + * @param bytes <code>byte</code> array for the + * METAXML file. + */ + void setMetaXMLBytes(byte bytes[]) { + + metaIndex = setEntryBytes(metaIndex, bytes, METAXML); + } + + + /** + * Set or replace the <code>byte</code> array for the + * SETTINGSXML file. + * + * @param bytes <code>byte</code> array for the + * SETTINGSXML file. + */ + void setSettingsXMLBytes(byte bytes[]) { + + settingsIndex = setEntryBytes(settingsIndex, bytes, SETTINGSXML); + } + + + /** + * Set or replace the <code>byte</code> array for the MANIFESTXML file. + * + * @param bytes <code>byte</code> array for the MANIFESTXML file. + */ + void setManifestXMLBytes(byte bytes[]) { + manifestIndex = setEntryBytes(manifestIndex, bytes, MANIFESTXML); + } + + /** + * <p>Used by the <code>setContentXMLBytes</code> method and + * the <code>setStyleXMLBytes</code> to either replace an + * existing <code>Entry</code>, or create a new entry in + * <code>entryList</code>.</p> + * + * <p>If there is an <code>Entry</code> object within + * entryList that corresponds to the index, replace the + * <code>ZipEntry</code> info.</p> + * + * @param index Index of <code>Entry</code> to modify. + * @param bytes <code>Entry</code> value. + * @param name Name of <code>Entry</code>. + * + * @return Index of value added or modified in entryList + */ + private int setEntryBytes(int index, byte bytes[], String name) { + + if (index > -1) { + + // replace existing entry in entryList + + Entry entry = (Entry) entryList.get(index); + name = entry.zipEntry.getName(); + int method = entry.zipEntry.getMethod(); + + ZipEntry ze = createZipEntry(name, bytes, method); + + entry.zipEntry = ze; + entry.bytes= bytes; + + } else { + + // add a new entry into entryList + ZipEntry ze = createZipEntry(name, bytes, ZipEntry.DEFLATED); + Entry entry = new Entry(ze, bytes); + entryList.add(entry); + index = entryList.size() - 1; + } + + return index; + } + + + /** + * Write out the ZIP entries into the <code>OutputStream</code> + * object. + * + * @param os <code>OutputStream</code> object to write ZIP. + * + * @throws IOException If any ZIP I/O error occurs. + */ + void write(OutputStream os) throws IOException { + + // Debug.log(Debug.TRACE, "Writing out the following entries into zip."); + + ZipOutputStream zos = new ZipOutputStream(os); + + ListIterator iterator = entryList.listIterator(); + + while (iterator.hasNext()) { + + Entry entry = (Entry) iterator.next(); + ZipEntry ze = entry.zipEntry; + + //String name = ze.getName(); + + // Debug.log(Debug.TRACE, "... " + name); + + zos.putNextEntry(ze); + zos.write(entry.bytes); + } + + zos.close(); + } + + + /** + * Creates a <code>ZipEntry</code> object based on the given params. + * + * @param name Name for the <code>ZipEntry</code>. + * @param bytes <code>byte</code> array for <code>ZipEntry</code>. + * @param method ZIP method to be used for <code>ZipEntry</code>. + * + * @return A <code>ZipEntry</code> object. + */ + private ZipEntry createZipEntry(String name, byte bytes[], int method) { + + ZipEntry ze = new ZipEntry(name); + + ze.setMethod(method); + ze.setSize(bytes.length); + + CRC32 crc = new CRC32(); + crc.reset(); + crc.update(bytes); + ze.setCrc(crc.getValue()); + + ze.setTime(System.currentTimeMillis()); + + return ze; + } + + /** + * This inner class is used as a data structure for holding + * a <code>ZipEntry</code> info and its corresponding bytes. + * These are stored in entryList. + */ + private class Entry { + + ZipEntry zipEntry = null; + byte bytes[] = null; + + Entry(ZipEntry zipEntry, byte bytes[]) { + this.zipEntry = zipEntry; + this.bytes = bytes; + } + } +} + diff --git a/source/java/writer2latex/xmerge/Package.html b/source/java/writer2latex/xmerge/Package.html new file mode 100644 index 0000000..835cc3c --- /dev/null +++ b/source/java/writer2latex/xmerge/Package.html @@ -0,0 +1,15 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> + +<html> +<head> + <title>The package writer2latex.xmerge</title> +</head> + +<body> +<p>Classes originating from OOo's xmerge.</p> +<p>Previously, Writer2LaTeX was based on xmerge, but this is not the case +anymore. The classes in this packages are reminiscent of that.</p> +<p>This package is supposed to go away in a future version and be replaced by +something else (probably ODFDOM)</p> +</body> +</html> diff --git a/source/oxt/writer2latex/META-INF/manifest.xml b/source/oxt/writer2latex/META-INF/manifest.xml new file mode 100644 index 0000000..a0e086c --- /dev/null +++ b/source/oxt/writer2latex/META-INF/manifest.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd"> +<manifest:manifest> + <manifest:file-entry + manifest:full-path="writer2latex-filter.jar" + manifest:media-type="application/vnd.sun.star.uno-component;type=Java"/> + + <manifest:file-entry + manifest:full-path="w2l_types.xcu" + manifest:media-type="application/vnd.sun.star.configuration-data"/> + + <manifest:file-entry + manifest:full-path="w2l_filters.xcu" + manifest:media-type="application/vnd.sun.star.configuration-data"/> + + <manifest:file-entry + manifest:full-path="Options.xcs" + manifest:media-type="application/vnd.sun.star.configuration-schema"/> + + <manifest:file-entry + manifest:full-path="Options.xcu" + manifest:media-type="application/vnd.sun.star.configuration-data"/> + + <manifest:file-entry + manifest:full-path="W2LDialogs/" + manifest:media-type="application/vnd.sun.star.basic-library"/> + + <manifest:file-entry + manifest:full-path="writer2latex.rdb" + manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB"/> + +</manifest:manifest> + \ No newline at end of file diff --git a/source/oxt/writer2latex/Options.xcs b/source/oxt/writer2latex/Options.xcs new file mode 100644 index 0000000..b068de9 --- /dev/null +++ b/source/oxt/writer2latex/Options.xcs @@ -0,0 +1,59 @@ +<?xml version='1.0' encoding='UTF-8'?> +<oor:component-schema oor:name="Options" + oor:package="org.openoffice.da.Writer2LaTeX" + xml:lang="en-US" + xmlns:oor="http://openoffice.org/2001/registry" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <templates> + <group oor:name="Configuration"> + <prop oor:name="DisplayName" oor:type="xs:string" oor:localized="true" /> + <prop oor:name="LockedOptions" oor:type="xs:string" /> + <prop oor:name="ConfigURL" oor:type="xs:string" /> + <!--<prop oor:name="TargetTemplateURL" oor:type="xs:string" />--> + </group> + <group oor:name="Template"> + <prop oor:name="TemplateName" oor:type="xs:string" /> + <prop oor:name="ConfigName" oor:type="xs:string" /> + </group> + </templates> + <component> + <group oor:name="LaTeXOptions"> + <!-- General --> + <prop oor:name="Config" oor:type="xs:short" /> + <prop oor:name="ConfigName" oor:type="xs:string" /> + <prop oor:name="Backend" oor:type="xs:short" /> + <prop oor:name="Inputencoding" oor:type="xs:short" /> + <prop oor:name="Multilingual" oor:type="xs:boolean" /> + <prop oor:name="GreekMath" oor:type="xs:boolean" /> + <prop oor:name="AdditionalSymbols" oor:type="xs:boolean" /> + <!-- Bibliography --> + <prop oor:name="UseBibtex" oor:type="xs:boolean" /> + <prop oor:name="BibtexStyle" oor:type="xs:string" /> + <!-- Files --> + <prop oor:name="WrapLines" oor:type="xs:boolean" /> + <prop oor:name="WrapLinesAfter" oor:type="xs:int" /> + <prop oor:name="SplitLinkedSections" oor:type="xs:boolean" /> + <prop oor:name="SplitToplevelSections" oor:type="xs:boolean" /> + <prop oor:name="SaveImagesInSubdir" oor:type="xs:boolean" /> + <!--Special content --> + <prop oor:name="Notes" oor:type="xs:short" /> + <prop oor:name="Metadata" oor:type="xs:boolean" /> + <!-- Figures and tables --> + <prop oor:name="OriginalImageSize" oor:type="xs:boolean"/> + <prop oor:name="OptimizeSimpleTables" oor:type="xs:boolean"/> + <prop oor:name="SimpleTableLimit" oor:type="xs:int"/> + <prop oor:name="FloatTables" oor:type="xs:boolean"/> + <prop oor:name="FloatFigures" oor:type="xs:boolean"/> + <prop oor:name="FloatOptions" oor:type="xs:short"/> + <!-- AutoCorrect --> + <prop oor:name="IgnoreHardPageBreaks" oor:type="xs:boolean" /> + <prop oor:name="IgnoreHardLineBreaks" oor:type="xs:boolean" /> + <prop oor:name="IgnoreEmptyParagraphs" oor:type="xs:boolean" /> + <prop oor:name="IgnoreDoubleSpaces" oor:type="xs:boolean" /> + <!-- Configurations --> + <set oor:name="Configurations" oor:node-type="Configuration" /> + <set oor:name="Templates" oor:node-type="Template" /> + </group> + </component> +</oor:component-schema> \ No newline at end of file diff --git a/source/oxt/writer2latex/Options.xcu b/source/oxt/writer2latex/Options.xcu new file mode 100644 index 0000000..9c63f38 --- /dev/null +++ b/source/oxt/writer2latex/Options.xcu @@ -0,0 +1,90 @@ +<?xml version='1.0' encoding='UTF-8'?> +<oor:component-data oor:name="Options" + oor:package="org.openoffice.da.Writer2LaTeX" + xml:lang="en-US" + xmlns:oor="http://openoffice.org/2001/registry" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <node oor:name="LaTeXOptions"> + <prop oor:name="Config" oor:type="xs:short"> + <value>2</value> <!-- Default --> + </prop> + <prop oor:name="ConfigName" oor:type="xs:string"> + <value></value> + </prop> + <prop oor:name="Backend" oor:type="xs:short"> + <value>1</value> <!-- pdftex --> + </prop> + <prop oor:name="Inputencoding" oor:type="xs:short"> + <value>1</value> <!-- latin1 --> + </prop> + <prop oor:name="Multilingual" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="GreekMath" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="AdditionalSymbols" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="UseBibtex" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="BibtexStyle" oor:type="xs:string"> + <value>plain</value> + </prop> + <prop oor:name="WrapLines" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="WrapLinesAfter" oor:type="xs:int"> + <value>72</value> + </prop> + <prop oor:name="SplitLinkedSections" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="SplitToplevelSections" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="SaveImagesInSubdir" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="Notes" oor:type="xs:short"> + <value>1</value><!-- comment --> + </prop> + <prop oor:name="Metadata" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="OriginalImageSize" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="OptimizeSimpleTables" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="SimpleTableLimit" oor:type="xs:int"> + <value>0</value> + </prop> + <prop oor:name="FloatTables" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="FloatFigures" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="FloatOptions" oor:type="xs:short"> + <value>0</value><!-- default (tbp) --> + </prop> + <prop oor:name="IgnoreHardPageBreaks" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="IgnoreHardLineBreaks" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="IgnoreEmptyParagraphs" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="IgnoreDoubleSpaces" oor:type="xs:boolean"> + <value>false</value> + </prop> + </node> +</oor:component-data> + + + \ No newline at end of file diff --git a/source/oxt/writer2latex/W2LDialogs/DialogStrings_da_DK.properties b/source/oxt/writer2latex/W2LDialogs/DialogStrings_da_DK.properties new file mode 100644 index 0000000..369233c --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/DialogStrings_da_DK.properties @@ -0,0 +1,111 @@ +# LaTeXOptions strings +0.LaTeXOptions.HelpText= +1.LaTeXOptions.Title=LaTeX-indstillinger (Writer2LaTeX) +2.LaTeXOptions.FixedLine1.HelpText= +3.LaTeXOptions.FixedLine1.Label= +4.LaTeXOptions.GeneralLabel.HelpText= +5.LaTeXOptions.GeneralLabel.Label=Generelt +6.LaTeXOptions.ConfigLabel.HelpText= +7.LaTeXOptions.ConfigLabel.Label=LaTeX-format +8.LaTeXOptions.Config.HelpText= +137.Config.StringItemList=Ekstra ren artikel +138.Config.StringItemList=Ren artikel +139.Config.StringItemList=Standard +140.Config.StringItemList=Udskriftsoptimeret +141.Config.StringItemList=Sk\u00e6rmoptimeret +142.Config.StringItemList=Brugerdefineret +15.LaTeXOptions.BackendLabel.HelpText= +16.LaTeXOptions.BackendLabel.Label=Slutformat +17.LaTeXOptions.Backend.HelpText= +116.Backend.StringItemList=Standard +117.Backend.StringItemList=Pdf (pdfTeX) +118.Backend.StringItemList=Postscript (dvips) +119.Backend.StringItemList=Ikke angivet +22.LaTeXOptions.InputencodingLabel.HelpText= +23.LaTeXOptions.InputencodingLabel.Label=Tegns\u00e6t +24.LaTeXOptions.Inputencoding.HelpText= +120.Inputencoding.StringItemList=US ASCII +121.Inputencoding.StringItemList=Vesteurop\u00e6isk (ISO 8859-1) +122.Inputencoding.StringItemList=\u00d8steurop\u00e6isk (ISO 8859-2) +123.Inputencoding.StringItemList=Latin/Gr\u00e6sk (ISO 8859-7) +124.Inputencoding.StringItemList=Microsoft \u00f8steurop\u00e6isk (Cp1250) +125.Inputencoding.StringItemList=Microsoft kyrillisk (Cp1251) +126.Inputencoding.StringItemList=Russisk (koi8-r) +127.Inputencoding.StringItemList=Unicode (UTF8) +33.LaTeXOptions.Multilingual.HelpText= +34.LaTeXOptions.Multilingual.Label=Underst\u00f8t dokumenter p\u00e5 flere sprog +35.LaTeXOptions.GreekMath.HelpText= +36.LaTeXOptions.GreekMath.Label=Brug gr\u00e6ske bogstaver som symboler +37.LaTeXOptions.AdditionalSymbols.HelpText= +38.LaTeXOptions.AdditionalSymbols.Label=Underst\u00f8t yderligere symboler +39.LaTeXOptions.BibliographyLabel.HelpText= +40.LaTeXOptions.BibliographyLabel.Label=Litteraturliste +41.LaTeXOptions.UseBibtex.HelpText= +42.LaTeXOptions.UseBibtex.Label=Brug BibTeX til litteraturlisten +43.LaTeXOptions.BibtexStyleLabel.HelpText= +44.LaTeXOptions.BibtexStyleLabel.Label=BibTeX-stil +45.LaTeXOptions.BibtexStyle.HelpText= +46.BibtexStyle.StringItemList=plain +47.BibtexStyle.StringItemList=unsrt +48.BibtexStyle.StringItemList=alpha +49.BibtexStyle.StringItemList=abbrv +50.LaTeXOptions.BibtexStyle.Text= +51.LaTeXOptions.FilesLabel.HelpText= +52.LaTeXOptions.FilesLabel.Label=Filer +53.LaTeXOptions.WrapLines.HelpText= +54.LaTeXOptions.WrapLines.Label=Ombryd lange linjer +55.LaTeXOptions.WrapLinesAfterLabel.HelpText= +56.LaTeXOptions.WrapLinesAfterLabel.Label=Efter antal tegn +57.LaTeXOptions.WrapLinesAfter.HelpText= +58.LaTeXOptions.SplitLinkedSections.HelpText= +59.LaTeXOptions.SplitLinkedSections.Label=Del dokumentet ved k\u00e6dede sektioner +60.LaTeXOptions.SplitToplevelSections.HelpText= +61.LaTeXOptions.SplitToplevelSections.Label=Del dokumentet ved sektioner p\u00e5 \u00f8verste niveau +62.LaTeXOptions.SaveImagesInSubdir.HelpText= +63.LaTeXOptions.SaveImagesInSubdir.Label=Gem billeder i undermappe +64.LaTeXOptions.SpecialContentLabel.HelpText= +65.LaTeXOptions.SpecialContentLabel.Label=Specielt indhold +66.LaTeXOptions.NotesLabel.HelpText= +67.LaTeXOptions.NotesLabel.Label=Kommentarer +68.LaTeXOptions.Notes.HelpText= +128.Notes.StringItemList=Eksporter ikke +129.Notes.StringItemList=Eksporter som kommentarer +130.Notes.StringItemList=Eksporter som margin-noter +131.Notes.StringItemList=Eksporter som pdf-noter +73.LaTeXOptions.Metadata.HelpText= +74.LaTeXOptions.Metadata.Label=Eksporter dokumentegenskaber (metadata) +75.LaTeXOptions.FiguresAndTablesLabel.HelpText= +76.LaTeXOptions.FiguresAndTablesLabel.Label=Figurer og tabeller +77.LaTeXOptions.OriginalImageSize.HelpText= +78.LaTeXOptions.OriginalImageSize.Label=Brug oprindelig billedst\u00f8rrelse +79.LaTeXOptions.OptimizeSimpleTables.HelpText= +80.LaTeXOptions.OptimizeSimpleTables.Label=Optimer simple tabeller +81.LaTeXOptions.SimpleTableLimitLabel.HelpText= +82.LaTeXOptions.SimpleTableLimitLabel.Label=H\u00f8jeste antal tegn i bredden +83.LaTeXOptions.SimpleTableLimit.HelpText= +84.LaTeXOptions.FloatTables.HelpText= +85.LaTeXOptions.FloatTables.Label=Lad tabeller flyde +86.LaTeXOptions.FloatFigures.HelpText= +87.LaTeXOptions.FloatFigures.Label=Lad figurer flyde +88.LaTeXOptions.FloatOptionsLabel.HelpText= +89.LaTeXOptions.FloatOptionsLabel.Label=Placering +90.LaTeXOptions.FloatOptions.HelpText= +132.FloatOptions.StringItemList=\u00d8verst eller nederst p\u00e5 siden +133.FloatOptions.StringItemList=\u00d8verst p\u00e5 siden +134.FloatOptions.StringItemList=Nederst p\u00e5 siden +135.FloatOptions.StringItemList=Her eller \u00f8verst p\u00e5 siden +136.FloatOptions.StringItemList=Her eller nederst p\u00e5 siden +96.LaTeXOptions.AutoCorrectLabel.HelpText= +97.LaTeXOptions.AutoCorrectLabel.Label=Autokorrektur +98.LaTeXOptions.IgnoreHardPageBreaks.HelpText= +99.LaTeXOptions.IgnoreHardPageBreaks.Label=Ignorer manuelle sideskift +100.LaTeXOptions.IgnoreHardLineBreaks.HelpText= +101.LaTeXOptions.IgnoreHardLineBreaks.Label=Ignorer manuelle linjeskift +102.LaTeXOptions.IgnoreEmptyParagraphs.HelpText= +103.LaTeXOptions.IgnoreEmptyParagraphs.Label=Ignorer tomme afsnit +104.LaTeXOptions.IgnoreDoubleSpaces.HelpText= +105.LaTeXOptions.IgnoreDoubleSpaces.Label=Ignorer dobbelte mellemrum +106.LaTeXOptions.ExportButton.HelpText= +107.LaTeXOptions.ExportButton.Label=Eksporter +108.LaTeXOptions.CancelButton.HelpText= +109.LaTeXOptions.CancelButton.Label=Afbryd diff --git a/source/oxt/writer2latex/W2LDialogs/DialogStrings_de_DE.properties b/source/oxt/writer2latex/W2LDialogs/DialogStrings_de_DE.properties new file mode 100644 index 0000000..17a8c9f --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/DialogStrings_de_DE.properties @@ -0,0 +1,111 @@ +# Strings for Dialog Library W2LDialogs +0.LaTeXOptions.HelpText= +1.LaTeXOptions.Title=LaTeX Options (Writer2LaTeX) +2.LaTeXOptions.FixedLine1.HelpText= +3.LaTeXOptions.FixedLine1.Label= +4.LaTeXOptions.GeneralLabel.HelpText= +5.LaTeXOptions.GeneralLabel.Label=Allgemein +6.LaTeXOptions.ConfigLabel.HelpText= +7.LaTeXOptions.ConfigLabel.Label=LaTeX Format +8.LaTeXOptions.Config.HelpText= +15.LaTeXOptions.BackendLabel.HelpText= +16.LaTeXOptions.BackendLabel.Label=Backend +17.LaTeXOptions.Backend.HelpText= +22.LaTeXOptions.InputencodingLabel.HelpText= +23.LaTeXOptions.InputencodingLabel.Label=Encoding +24.LaTeXOptions.Inputencoding.HelpText= +33.LaTeXOptions.Multilingual.HelpText= +34.LaTeXOptions.Multilingual.Label=Unterst\u00fctzung f\u00fcr Mehrsprachigkeit +35.LaTeXOptions.GreekMath.HelpText= +36.LaTeXOptions.GreekMath.Label=Benutze griechische Buchstaben als Symbole +37.LaTeXOptions.AdditionalSymbols.HelpText= +38.LaTeXOptions.AdditionalSymbols.Label=Unterst\u00fctze weitere Symbole +39.LaTeXOptions.BibliographyLabel.HelpText= +40.LaTeXOptions.BibliographyLabel.Label=Literaturverzeichnis +41.LaTeXOptions.UseBibtex.HelpText= +42.LaTeXOptions.UseBibtex.Label=Benutze BibTeX f\u00fcr das Literaturverzeichnis +43.LaTeXOptions.BibtexStyleLabel.HelpText= +44.LaTeXOptions.BibtexStyleLabel.Label=BibTeX Stil +45.LaTeXOptions.BibtexStyle.HelpText= +46.BibtexStyle.StringItemList=plain +47.BibtexStyle.StringItemList=unsrt +48.BibtexStyle.StringItemList=alpha +49.BibtexStyle.StringItemList=abbrv +50.LaTeXOptions.BibtexStyle.Text= +51.LaTeXOptions.FilesLabel.HelpText= +52.LaTeXOptions.FilesLabel.Label=Dateien +53.LaTeXOptions.WrapLines.HelpText= +54.LaTeXOptions.WrapLines.Label=Umbruch von langen Zeilen +55.LaTeXOptions.WrapLinesAfterLabel.HelpText= +56.LaTeXOptions.WrapLinesAfterLabel.Label=nach Buchstaben +57.LaTeXOptions.WrapLinesAfter.HelpText= +58.LaTeXOptions.SplitLinkedSections.HelpText= +59.LaTeXOptions.SplitLinkedSections.Label=Teilung des Dokumentes in verlinkte Bereiche +60.LaTeXOptions.SplitToplevelSections.HelpText= +61.LaTeXOptions.SplitToplevelSections.Label=Teile das Dokument in Hauptbereiche +62.LaTeXOptions.SaveImagesInSubdir.HelpText= +63.LaTeXOptions.SaveImagesInSubdir.Label=Speichere Bilder in einem Unterverzeichnis +64.LaTeXOptions.SpecialContentLabel.HelpText= +65.LaTeXOptions.SpecialContentLabel.Label=Erweiterte Einstellungen +66.LaTeXOptions.NotesLabel.HelpText= +67.LaTeXOptions.NotesLabel.Label=Bemerkungen +68.LaTeXOptions.Notes.HelpText= +73.LaTeXOptions.Metadata.HelpText= +74.LaTeXOptions.Metadata.Label=Exportiere Dokumenteigenschaften (Metadata) +75.LaTeXOptions.FiguresAndTablesLabel.HelpText= +76.LaTeXOptions.FiguresAndTablesLabel.Label=Abbildungen und Tabellen +77.LaTeXOptions.OriginalImageSize.HelpText= +78.LaTeXOptions.OriginalImageSize.Label=Verwende originale Bildgr\u00f6\u00dfe +79.LaTeXOptions.OptimizeSimpleTables.HelpText= +80.LaTeXOptions.OptimizeSimpleTables.Label=Optimiere einfache Tabellen +81.LaTeXOptions.SimpleTableLimitLabel.HelpText= +82.LaTeXOptions.SimpleTableLimitLabel.Label=Maximale Breite in Buchstaben +83.LaTeXOptions.SimpleTableLimit.HelpText= +84.LaTeXOptions.FloatTables.HelpText= +85.LaTeXOptions.FloatTables.Label=Float Tabellen +86.LaTeXOptions.FloatFigures.HelpText= +87.LaTeXOptions.FloatFigures.Label=Float Abbildungen +88.LaTeXOptions.FloatOptionsLabel.HelpText= +89.LaTeXOptions.FloatOptionsLabel.Label=Float Ausrichtung +90.LaTeXOptions.FloatOptions.HelpText= +96.LaTeXOptions.AutoCorrectLabel.HelpText= +97.LaTeXOptions.AutoCorrectLabel.Label=Automatische Korrektur +98.LaTeXOptions.IgnoreHardPageBreaks.HelpText= +99.LaTeXOptions.IgnoreHardPageBreaks.Label=Ignoriere harte Seitenumbr\u00fcche +100.LaTeXOptions.IgnoreHardLineBreaks.HelpText= +101.LaTeXOptions.IgnoreHardLineBreaks.Label=Ignoriere harte Zeilenumbr\u00fcche +102.LaTeXOptions.IgnoreEmptyParagraphs.HelpText= +103.LaTeXOptions.IgnoreEmptyParagraphs.Label=Ignoriere leere Abschnitte +104.LaTeXOptions.IgnoreDoubleSpaces.HelpText= +105.LaTeXOptions.IgnoreDoubleSpaces.Label=Ignoriere doppelte Leerzeichen +106.LaTeXOptions.ExportButton.HelpText= +107.LaTeXOptions.ExportButton.Label=Export +108.LaTeXOptions.CancelButton.HelpText= +109.LaTeXOptions.CancelButton.Label=Cancel +137.Config.StringItemList=Sehr aufger\u00e4umter Artikel +138.Config.StringItemList=Aufger\u00e4umter Artikel +139.Config.StringItemList=Voreinstellung +140.Config.StringItemList=Druck optimiert +141.Config.StringItemList=Monitor optimiert (pdf) +142.Config.StringItemList=Angepasst +116.Backend.StringItemList=generisch +117.Backend.StringItemList=Pdf (pdfTeX) +118.Backend.StringItemList=Postscript (dvips) +119.Backend.StringItemList=unspezifisch +120.Inputencoding.StringItemList=US ASCII +121.Inputencoding.StringItemList=Westeurop\u00e4isch (ISO 8859-1) +122.Inputencoding.StringItemList=Osteurop\u00e4isch (ISO 8859-2) +123.Inputencoding.StringItemList=Lateinisch/Griechisch (ISO 8859-7) +124.Inputencoding.StringItemList=Microsoft Osteurop\u00e4isch (Cp1250) +125.Inputencoding.StringItemList=Microsoft Cyrillic (Cp1251) +126.Inputencoding.StringItemList=Russisch (koi8-r) +127.Inputencoding.StringItemList=Unicode (UTF8) +128.Notes.StringItemList=Nicht exportieren +129.Notes.StringItemList=Als Kommentare exportieren +130.Notes.StringItemList=Als Randnotizen exportieren +131.Notes.StringItemList=Als pdf Bemerkungen exportieren +132.FloatOptions.StringItemList=Anfang oder Ende der Seite +133.FloatOptions.StringItemList=Anfang der Seite +134.FloatOptions.StringItemList=Ende der Seite +135.FloatOptions.StringItemList=Hier oder Anfang der Seite +136.FloatOptions.StringItemList=Hier oder Ende der Seite diff --git a/source/oxt/writer2latex/W2LDialogs/DialogStrings_en_US.default b/source/oxt/writer2latex/W2LDialogs/DialogStrings_en_US.default new file mode 100644 index 0000000..e69de29 diff --git a/source/oxt/writer2latex/W2LDialogs/DialogStrings_en_US.properties b/source/oxt/writer2latex/W2LDialogs/DialogStrings_en_US.properties new file mode 100644 index 0000000..054b2a6 --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/DialogStrings_en_US.properties @@ -0,0 +1,111 @@ +# LaTeXOptions strings +0.LaTeXOptions.HelpText= +1.LaTeXOptions.Title=LaTeX Options (Writer2LaTeX) +2.LaTeXOptions.FixedLine1.HelpText= +3.LaTeXOptions.FixedLine1.Label= +4.LaTeXOptions.GeneralLabel.HelpText= +5.LaTeXOptions.GeneralLabel.Label=General +6.LaTeXOptions.ConfigLabel.HelpText= +7.LaTeXOptions.ConfigLabel.Label=LaTeX format +8.LaTeXOptions.Config.HelpText= +137.Config.StringItemList=Ultra-clean article +138.Config.StringItemList=Clean article +139.Config.StringItemList=Default +140.Config.StringItemList=Print optimized +141.Config.StringItemList=Screen optimized (pdf) +142.Config.StringItemList=Custom +15.LaTeXOptions.BackendLabel.HelpText= +16.LaTeXOptions.BackendLabel.Label=Backend +17.LaTeXOptions.Backend.HelpText= +116.Backend.StringItemList=Generic +117.Backend.StringItemList=Pdf (pdfTeX) +118.Backend.StringItemList=Postscript (dvips) +119.Backend.StringItemList=Unspecified +22.LaTeXOptions.InputencodingLabel.HelpText= +23.LaTeXOptions.InputencodingLabel.Label=Encoding +24.LaTeXOptions.Inputencoding.HelpText= +120.Inputencoding.StringItemList=US ASCII +121.Inputencoding.StringItemList=West European (ISO 8859-1) +122.Inputencoding.StringItemList=East European (ISO 8859-2) +123.Inputencoding.StringItemList=Latin/Greek (ISO 8859-7) +124.Inputencoding.StringItemList=Microsoft East European (Cp1250) +125.Inputencoding.StringItemList=Microsoft Cyrillic (Cp1251) +126.Inputencoding.StringItemList=Russian (koi8-r) +127.Inputencoding.StringItemList=Unicode (UTF8) +33.LaTeXOptions.Multilingual.HelpText= +34.LaTeXOptions.Multilingual.Label=Enable multilingual support +35.LaTeXOptions.GreekMath.HelpText= +36.LaTeXOptions.GreekMath.Label=Use greek letters as symbols +37.LaTeXOptions.AdditionalSymbols.HelpText= +38.LaTeXOptions.AdditionalSymbols.Label=Support additional symbols +39.LaTeXOptions.BibliographyLabel.HelpText= +40.LaTeXOptions.BibliographyLabel.Label=Bibliography +41.LaTeXOptions.UseBibtex.HelpText= +42.LaTeXOptions.UseBibtex.Label=Use BibTeX for bibliography +43.LaTeXOptions.BibtexStyleLabel.HelpText= +44.LaTeXOptions.BibtexStyleLabel.Label=BibTeX style +45.LaTeXOptions.BibtexStyle.HelpText= +46.BibtexStyle.StringItemList=plain +47.BibtexStyle.StringItemList=unsrt +48.BibtexStyle.StringItemList=alpha +49.BibtexStyle.StringItemList=abbrv +50.LaTeXOptions.BibtexStyle.Text= +51.LaTeXOptions.FilesLabel.HelpText= +52.LaTeXOptions.FilesLabel.Label=Files +53.LaTeXOptions.WrapLines.HelpText= +54.LaTeXOptions.WrapLines.Label=Wrap long lines +55.LaTeXOptions.WrapLinesAfterLabel.HelpText= +56.LaTeXOptions.WrapLinesAfterLabel.Label=After characters +57.LaTeXOptions.WrapLinesAfter.HelpText= +58.LaTeXOptions.SplitLinkedSections.HelpText= +59.LaTeXOptions.SplitLinkedSections.Label=Split document at linked sections +60.LaTeXOptions.SplitToplevelSections.HelpText= +61.LaTeXOptions.SplitToplevelSections.Label=Split document at top level sections +62.LaTeXOptions.SaveImagesInSubdir.HelpText= +63.LaTeXOptions.SaveImagesInSubdir.Label=Save images in subdirectory +64.LaTeXOptions.SpecialContentLabel.HelpText= +65.LaTeXOptions.SpecialContentLabel.Label=Special content +66.LaTeXOptions.NotesLabel.HelpText= +67.LaTeXOptions.NotesLabel.Label=Export notes +68.LaTeXOptions.Notes.HelpText= +128.Notes.StringItemList=Do not export +129.Notes.StringItemList=As comments +130.Notes.StringItemList=As marginal notes +131.Notes.StringItemList=As pdf annotations +73.LaTeXOptions.Metadata.HelpText= +74.LaTeXOptions.Metadata.Label=Export document properties (metadata) +75.LaTeXOptions.FiguresAndTablesLabel.HelpText= +76.LaTeXOptions.FiguresAndTablesLabel.Label=Figures and tables +77.LaTeXOptions.OriginalImageSize.HelpText= +78.LaTeXOptions.OriginalImageSize.Label=Use original image size +79.LaTeXOptions.OptimizeSimpleTables.HelpText= +80.LaTeXOptions.OptimizeSimpleTables.Label=Optimize simple tables +81.LaTeXOptions.SimpleTableLimitLabel.HelpText= +82.LaTeXOptions.SimpleTableLimitLabel.Label=Maximum width in characters +83.LaTeXOptions.SimpleTableLimit.HelpText= +84.LaTeXOptions.FloatTables.HelpText= +85.LaTeXOptions.FloatTables.Label=Float tables +86.LaTeXOptions.FloatFigures.HelpText= +87.LaTeXOptions.FloatFigures.Label=Float figures +88.LaTeXOptions.FloatOptionsLabel.HelpText= +89.LaTeXOptions.FloatOptionsLabel.Label=Float placement +90.LaTeXOptions.FloatOptions.HelpText= +132.FloatOptions.StringItemList=Top or bottom of page +133.FloatOptions.StringItemList=Top of page +134.FloatOptions.StringItemList=Bottom of page +135.FloatOptions.StringItemList=Here or top of page +136.FloatOptions.StringItemList=Here or bottom of page +96.LaTeXOptions.AutoCorrectLabel.HelpText= +97.LaTeXOptions.AutoCorrectLabel.Label=AutoCorrect +98.LaTeXOptions.IgnoreHardPageBreaks.HelpText= +99.LaTeXOptions.IgnoreHardPageBreaks.Label=Ignore hard page breaks +100.LaTeXOptions.IgnoreHardLineBreaks.HelpText= +101.LaTeXOptions.IgnoreHardLineBreaks.Label=Ignore hard line breaks +102.LaTeXOptions.IgnoreEmptyParagraphs.HelpText= +103.LaTeXOptions.IgnoreEmptyParagraphs.Label=Ignore empty paragraphs +104.LaTeXOptions.IgnoreDoubleSpaces.HelpText= +105.LaTeXOptions.IgnoreDoubleSpaces.Label=Ignore double spaces +106.LaTeXOptions.ExportButton.HelpText= +107.LaTeXOptions.ExportButton.Label=Export +108.LaTeXOptions.CancelButton.HelpText= +109.LaTeXOptions.CancelButton.Label=Cancel diff --git a/source/oxt/writer2latex/W2LDialogs/DialogStrings_fr_FR.properties b/source/oxt/writer2latex/W2LDialogs/DialogStrings_fr_FR.properties new file mode 100644 index 0000000..f87ff4b --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/DialogStrings_fr_FR.properties @@ -0,0 +1,111 @@ +# LaTeXOptions strings= +0.LaTeXOptions.HelpText= +1.LaTeXOptions.Title=Options LaTeX (Writer2LaTeX) +2.LaTeXOptions.FixedLine1.HelpText= +3.LaTeXOptions.FixedLine1.Label= +4.LaTeXOptions.GeneralLabel.HelpText= +5.LaTeXOptions.GeneralLabel.Label=G\u00e9n\u00e9ral +6.LaTeXOptions.ConfigLabel.HelpText= +7.LaTeXOptions.ConfigLabel.Label=Format LaTeX +8.LaTeXOptions.Config.HelpText= +137.Config.StringItemList=Article \u00e9pur\u00e9 +138.Config.StringItemList=Article propre +139.Config.StringItemList=Par d\u00e9faut +140.Config.StringItemList=Adapt\u00e9 pour une impression +141.Config.StringItemList=Adapt\u00e9 pour un \u00e9cran (pdf) +142.Config.StringItemList=Configuration personnalis\u00e9e +15.LaTeXOptions.BackendLabel.HelpText= +16.LaTeXOptions.BackendLabel.Label=T\u00e2che de fond +17.LaTeXOptions.Backend.HelpText= +116.Backend.StringItemList=G\u00e9n\u00e9rique +117.Backend.StringItemList=Pdf (pdfTeX) +118.Backend.StringItemList=Postscript (dvips) +119.Backend.StringItemList=Non sp\u00e9cifi\u00e9 +22.LaTeXOptions.InputencodingLabel.HelpText= +23.LaTeXOptions.InputencodingLabel.Label=Encodage +24.LaTeXOptions.Inputencoding.HelpText= +120.Inputencoding.StringItemList=ASCII US +121.Inputencoding.StringItemList=Europe de l'Ouest (ISO 8859-1) +122.Inputencoding.StringItemList=Europe de l'Est (ISO 8859-2) +123.Inputencoding.StringItemList=Latin/Grec (ISO 8859-7) +124.Inputencoding.StringItemList=Microsoft Europe de l'Est (Cp1250) +125.Inputencoding.StringItemList=Microsoft Cyrillique (Cp1251) +126.Inputencoding.StringItemList=Russe (koi8-r) +127.Inputencoding.StringItemList=Unicode (UTF8) +33.LaTeXOptions.Multilingual.HelpText= +34.LaTeXOptions.Multilingual.Label=Activer le support multilingue +35.LaTeXOptions.GreekMath.HelpText= +36.LaTeXOptions.GreekMath.Label=Utiliser les lettres grecques comme symboles +37.LaTeXOptions.AdditionalSymbols.HelpText= +38.LaTeXOptions.AdditionalSymbols.Label=Prise en charge de symboles additionels +39.LaTeXOptions.BibliographyLabel.HelpText= +40.LaTeXOptions.BibliographyLabel.Label=Bibliographie +41.LaTeXOptions.UseBibtex.HelpText= +42.LaTeXOptions.UseBibtex.Label=Utiliser BibTeX pour la bibliographie +43.LaTeXOptions.BibtexStyleLabel.HelpText= +44.LaTeXOptions.BibtexStyleLabel.Label=Style BibTeX +45.LaTeXOptions.BibtexStyle.HelpText= +46.BibtexStyle.StringItemList=plain +47.BibtexStyle.StringItemList=unsrt +48.BibtexStyle.StringItemList=alpha +49.BibtexStyle.StringItemList=abbrv +50.LaTeXOptions.BibtexStyle.Text= +51.LaTeXOptions.FilesLabel.HelpText= +52.LaTeXOptions.FilesLabel.Label=Fichiers +53.LaTeXOptions.WrapLines.HelpText= +54.LaTeXOptions.WrapLines.Label=Retour automatique \u00e0 la ligne +55.LaTeXOptions.WrapLinesAfterLabel.HelpText= +56.LaTeXOptions.WrapLinesAfterLabel.Label=Apr\u00e8s les caract\u00e8res +57.LaTeXOptions.WrapLinesAfter.HelpText= +58.LaTeXOptions.SplitLinkedSections.HelpText= +59.LaTeXOptions.SplitLinkedSections.Label=S\u00e9parer le document aux sections li\u00e9es +60.LaTeXOptions.SplitToplevelSections.HelpText= +61.LaTeXOptions.SplitToplevelSections.Label=S\u00e9parer le document aux sections principales +62.LaTeXOptions.SaveImagesInSubdir.HelpText= +63.LaTeXOptions.SaveImagesInSubdir.Label=Sauver les images dans un sous-r\u00e9pertoire +64.LaTeXOptions.SpecialContentLabel.HelpText= +65.LaTeXOptions.SpecialContentLabel.Label=Contenu sp\u00e9cifique +66.LaTeXOptions.NotesLabel.HelpText= +67.LaTeXOptions.NotesLabel.Label=Exporter les notes +68.LaTeXOptions.Notes.HelpText= +128.Notes.StringItemList=Ne pas exporter +129.Notes.StringItemList=Comme commentaires +130.Notes.StringItemList=Comme notes de marges +131.Notes.StringItemList=Comme annotations pdf +73.LaTeXOptions.Metadata.HelpText= +74.LaTeXOptions.Metadata.Label=Exporter les propri\u00e9t\u00e9s du document (metadonn\u00e9es) +75.LaTeXOptions.FiguresAndTablesLabel.HelpText= +76.LaTeXOptions.FiguresAndTablesLabel.Label=Tableaux et figures +77.LaTeXOptions.OriginalImageSize.HelpText= +78.LaTeXOptions.OriginalImageSize.Label=Utiliser la taille orginale de l'image +79.LaTeXOptions.OptimizeSimpleTables.HelpText= +80.LaTeXOptions.OptimizeSimpleTables.Label=Optimiser les tableaux simples +81.LaTeXOptions.SimpleTableLimitLabel.HelpText= +82.LaTeXOptions.SimpleTableLimitLabel.Label=Largeur maximum par caract\u00e8re +83.LaTeXOptions.SimpleTableLimit.HelpText= +84.LaTeXOptions.FloatTables.HelpText= +85.LaTeXOptions.FloatTables.Label=Tableaux flottants +86.LaTeXOptions.FloatFigures.HelpText= +87.LaTeXOptions.FloatFigures.Label=Figures flottantes +88.LaTeXOptions.FloatOptionsLabel.HelpText= +89.LaTeXOptions.FloatOptionsLabel.Label=Placement flottant +90.LaTeXOptions.FloatOptions.HelpText= +132.FloatOptions.StringItemList=Haut ou bas de page +133.FloatOptions.StringItemList=Haut de page +134.FloatOptions.StringItemList=Bas de page +135.FloatOptions.StringItemList=Ici ou en haut de la page +136.FloatOptions.StringItemList=Ici ou en bas de la page +96.LaTeXOptions.AutoCorrectLabel.HelpText= +97.LaTeXOptions.AutoCorrectLabel.Label=Correction automatique +98.LaTeXOptions.IgnoreHardPageBreaks.HelpText= +99.LaTeXOptions.IgnoreHardPageBreaks.Label=Ignorer les sauts de page +100.LaTeXOptions.IgnoreHardLineBreaks.HelpText= +101.LaTeXOptions.IgnoreHardLineBreaks.Label=Ignorer les sauts de ligne +102.LaTeXOptions.IgnoreEmptyParagraphs.HelpText= +103.LaTeXOptions.IgnoreEmptyParagraphs.Label=Ignorer les paragraphes vides +104.LaTeXOptions.IgnoreDoubleSpaces.HelpText= +105.LaTeXOptions.IgnoreDoubleSpaces.Label=Ignoerer les doubles espaces +106.LaTeXOptions.ExportButton.HelpText= +107.LaTeXOptions.ExportButton.Label=Exporter +108.LaTeXOptions.CancelButton.HelpText= +109.LaTeXOptions.CancelButton.Label=Annuler diff --git a/source/oxt/writer2latex/W2LDialogs/DialogStrings_ru_RU.properties b/source/oxt/writer2latex/W2LDialogs/DialogStrings_ru_RU.properties new file mode 100644 index 0000000..71756e6 --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/DialogStrings_ru_RU.properties @@ -0,0 +1,111 @@ +# LaTeXOptions strings=(RUSSIAN) +0.LaTeXOptions.HelpText= +1.LaTeXOptions.Title=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b LaTeX (Writer2LaTeX) +2.LaTeXOptions.FixedLine1.HelpText= +3.LaTeXOptions.FixedLine1.Label= +4.LaTeXOptions.GeneralLabel.HelpText= +5.LaTeXOptions.GeneralLabel.Label=\u041e\u0431\u0449\u0438\u0435 +6.LaTeXOptions.ConfigLabel.HelpText= +7.LaTeXOptions.ConfigLabel.Label=\u0424\u043e\u0440\u043c\u0430\u0442 LaTeX +8.LaTeXOptions.Config.HelpText= +137.Config.StringItemList=\u0421\u0432\u0435\u0440\u0445\u0447\u0438\u0441\u0442\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f +138.Config.StringItemList=\u0427\u0438\u0441\u0442\u0430\u044f \u0441\u0442\u0430\u0442\u044c\u044f +139.Config.StringItemList=\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e +140.Config.StringItemList=\u041e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043b\u044f \u043f\u0435\u0447\u0430\u0442\u0438 +141.Config.StringItemList=\u041e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0434\u043b\u044f \u044d\u043a\u0440\u0430\u043d\u0430 (pdf) +142.Config.StringItemList=\u0412\u044b\u0431\u043e\u0440\u043e\u0447\u043d\u044b\u0439 +15.LaTeXOptions.BackendLabel.HelpText= +16.LaTeXOptions.BackendLabel.Label=\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 +17.LaTeXOptions.Backend.HelpText= +116.Backend.StringItemList=\u0412\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 +117.Backend.StringItemList=Pdf (pdfTeX) +118.Backend.StringItemList=Postscript (dvips) +119.Backend.StringItemList=\u041d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d +22.LaTeXOptions.InputencodingLabel.HelpText= +23.LaTeXOptions.InputencodingLabel.Label=\u041a\u043e\u0434\u0438\u0440\u043e\u0432\u043a\u0430 +24.LaTeXOptions.Inputencoding.HelpText= +120.Inputencoding.StringItemList=\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442 ASCII \u0421\u0428\u0410 +121.Inputencoding.StringItemList=\u0417\u0430\u043f\u0430\u0434\u043d\u043e \u0435\u0432\u0440\u043e\u043f\u0435\u0439\u0441\u043a\u0430\u044f (ISO 8859-1) +122.Inputencoding.StringItemList=\u0412\u043e\u0441\u0442\u043e\u0447\u043d\u043e \u0435\u0432\u0440\u043e\u043f\u0435\u0439\u0441\u043a\u0430\u044f (ISO 8859-2) +123.Inputencoding.StringItemList=\u041b\u0430\u0442\u0438\u043d\u0438\u0446\u0430/\u0413\u0440\u0435\u0447\u0435\u0441\u043a\u0438\u0439 (ISO 8859-7) +124.Inputencoding.StringItemList=\u0412\u043e\u0441\u0442\u043e\u0447\u043d\u043e \u0435\u0432\u0440\u043e\u043f\u0435\u0439\u0441\u043a\u0430\u044f \u043e\u0442 Microsoft (CP1250) +125.Inputencoding.StringItemList=\u041a\u0438\u0440\u0438\u043b\u043b\u0438\u0446\u0430 \u043e\u0442 Microsoft (CP1251) +126.Inputencoding.StringItemList=\u0420\u043e\u0441\u0441\u0438\u0439\u0441\u043a\u0430\u044f (KOI8-R) +127.Inputencoding.StringItemList=\u042e\u043d\u0438\u043a\u043e\u0434 (UTF8) +33.LaTeXOptions.Multilingual.HelpText= +34.LaTeXOptions.Multilingual.Label=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u044f\u0437\u044b\u043a\u043e\u0432\u0443\u044e \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0443 +35.LaTeXOptions.GreekMath.HelpText= +36.LaTeXOptions.GreekMath.Label=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0433\u0440\u0435\u0447\u0435\u0441\u043a\u0438\u0435 \u0431\u0443\u043a\u0432\u044b \u043a\u0430\u043a \u0441\u0438\u043c\u0432\u043e\u043b\u044b +37.LaTeXOptions.AdditionalSymbols.HelpText= +38.LaTeXOptions.AdditionalSymbols.Label=\u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0441\u0438\u043c\u0432\u043e\u043b\u044b +39.LaTeXOptions.BibliographyLabel.HelpText= +40.LaTeXOptions.BibliographyLabel.Label=\u0411\u0438\u0431\u043b\u0438\u043e\u0433\u0440\u0430\u0444\u0438\u044f +41.LaTeXOptions.UseBibtex.HelpText= +42.LaTeXOptions.UseBibtex.Label=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c BibTeX \u0434\u043b\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0433\u0440\u0430\u0444\u0438\u0438 +43.LaTeXOptions.BibtexStyleLabel.HelpText= +44.LaTeXOptions.BibtexStyleLabel.Label=\u0421\u0442\u0438\u043b\u044c BibTeX +45.LaTeXOptions.BibtexStyle.HelpText= +46.BibtexStyle.StringItemList=plain +47.BibtexStyle.StringItemList=unsrt +48.BibtexStyle.StringItemList=alpha +49.BibtexStyle.StringItemList=abbrv +50.LaTeXOptions.BibtexStyle.Text= +51.LaTeXOptions.FilesLabel.HelpText= +52.LaTeXOptions.FilesLabel.Label=\u0424\u0430\u0439\u043b\u044b +53.LaTeXOptions.WrapLines.HelpText= +54.LaTeXOptions.WrapLines.Label=\u041f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u0442\u044c \u0434\u043b\u0438\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 +55.LaTeXOptions.WrapLinesAfterLabel.HelpText= +56.LaTeXOptions.WrapLinesAfterLabel.Label=\u041d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 \u0434\u043b\u0438\u043d\u044b +57.LaTeXOptions.WrapLinesAfter.HelpText= +58.LaTeXOptions.SplitLinkedSections.HelpText= +59.LaTeXOptions.SplitLinkedSections.Label=\u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u043c \u0441\u0435\u043a\u0446\u0438\u044f\u043c +60.LaTeXOptions.SplitToplevelSections.HelpText= +61.LaTeXOptions.SplitToplevelSections.Label=\u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u0441\u0435\u043a\u0446\u0438\u044f\u043c \u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f +62.LaTeXOptions.SaveImagesInSubdir.HelpText= +63.LaTeXOptions.SaveImagesInSubdir.Label=\u0421\u043e\u0445\u0440\u0430\u043d\u044f\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u043f\u043e\u0434\u0434\u0438\u0440\u0435\u043a\u0442\u043e\u0440\u0438\u044e +64.LaTeXOptions.SpecialContentLabel.HelpText= +65.LaTeXOptions.SpecialContentLabel.Label=\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 +66.LaTeXOptions.NotesLabel.HelpText= +67.LaTeXOptions.NotesLabel.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u0439 +68.LaTeXOptions.Notes.HelpText= +128.Notes.StringItemList=\u041d\u0435 \u044d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c +129.Notes.StringItemList=\u041a\u0430\u043a \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 +130.Notes.StringItemList=\u041a\u0430\u043a \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u044f \u0441 \u043a\u0440\u0430\u044e +131.Notes.StringItemList=\u041a\u0430\u043a \u0441\u0441\u044b\u043b\u043a\u0438 \u0432 PDF +73.LaTeXOptions.Metadata.HelpText= +74.LaTeXOptions.Metadata.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442 \u0441\u0432\u043e\u0439\u0441\u0442\u0432 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 (\u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435) +75.LaTeXOptions.FiguresAndTablesLabel.HelpText= +76.LaTeXOptions.FiguresAndTablesLabel.Label=\u0418\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u0438 \u0442\u0430\u0431\u043b\u0438\u0446\u044b +77.LaTeXOptions.OriginalImageSize.HelpText= +78.LaTeXOptions.OriginalImageSize.Label=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f +79.LaTeXOptions.OptimizeSimpleTables.HelpText= +80.LaTeXOptions.OptimizeSimpleTables.Label=\u041e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u044b\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b +81.LaTeXOptions.SimpleTableLimitLabel.HelpText= +82.LaTeXOptions.SimpleTableLimitLabel.Label=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430\u044f \u0448\u0438\u0440\u0438\u043d\u0430 \u0432 \u0441\u0438\u043c\u0432\u043e\u043b\u0430\u0445 +83.LaTeXOptions.SimpleTableLimit.HelpText= +84.LaTeXOptions.FloatTables.HelpText= +85.LaTeXOptions.FloatTables.Label=\u041f\u043b\u0430\u0432\u0430\u044e\u0449\u0438\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b +86.LaTeXOptions.FloatFigures.HelpText= +87.LaTeXOptions.FloatFigures.Label=\u041f\u043b\u0430\u0432\u0430\u044e\u0449\u0438\u0435 \u0438\u043b\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0438\u0438 +88.LaTeXOptions.FloatOptionsLabel.HelpText= +89.LaTeXOptions.FloatOptionsLabel.Label=\u041f\u043b\u0430\u0432\u0430\u044e\u0449\u0435\u0435 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 +90.LaTeXOptions.FloatOptions.HelpText= +132.FloatOptions.StringItemList=\u0412\u0435\u0440\u0445 \u0438\u043b\u0438 \u043d\u0438\u0437 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b +133.FloatOptions.StringItemList=\u0412\u0435\u0440\u0445 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b +134.FloatOptions.StringItemList=\u041d\u0438\u0437 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b +135.FloatOptions.StringItemList=\u0422\u0443\u0442 \u0438\u043b\u0438 \u0432\u0432\u0435\u0440\u0445\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b +136.FloatOptions.StringItemList=\u0422\u0443\u0442 \u0438\u043b\u0438 \u0432\u043d\u0438\u0437\u0443 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b +96.LaTeXOptions.AutoCorrectLabel.HelpText= +97.LaTeXOptions.AutoCorrectLabel.Label=\u0410\u0432\u0442\u043e\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u043a\u0430 +98.LaTeXOptions.IgnoreHardPageBreaks.HelpText= +99.LaTeXOptions.IgnoreHardPageBreaks.Label=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0440\u044b\u0432\u044b \u0441\u0442\u0440\u0430\u043d\u0438\u0446 +100.LaTeXOptions.IgnoreHardLineBreaks.HelpText= +101.LaTeXOptions.IgnoreHardLineBreaks.Label=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0440\u044b\u0432\u044b \u0441\u0442\u0440\u043e\u043a +102.LaTeXOptions.IgnoreEmptyParagraphs.HelpText= +103.LaTeXOptions.IgnoreEmptyParagraphs.Label=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u0435 \u043f\u0430\u0440\u0430\u0433\u0440\u0430\u0444\u044b +104.LaTeXOptions.IgnoreDoubleSpaces.HelpText= +105.LaTeXOptions.IgnoreDoubleSpaces.Label=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u043e\u0439\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u0435\u043b\u044b +106.LaTeXOptions.ExportButton.HelpText= +107.LaTeXOptions.ExportButton.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c +108.LaTeXOptions.CancelButton.HelpText= +109.LaTeXOptions.CancelButton.Label=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c diff --git a/source/oxt/writer2latex/W2LDialogs/DialogStrings_uk_UA.properties b/source/oxt/writer2latex/W2LDialogs/DialogStrings_uk_UA.properties new file mode 100644 index 0000000..9297508 --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/DialogStrings_uk_UA.properties @@ -0,0 +1,111 @@ +# LaTeXOptions strings=(UKRAINIAN) +0.LaTeXOptions.HelpText= +1.LaTeXOptions.Title=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438 LaTeX (Writer2LaTeX) +2.LaTeXOptions.FixedLine1.HelpText= +3.LaTeXOptions.FixedLine1.Label= +4.LaTeXOptions.GeneralLabel.HelpText= +5.LaTeXOptions.GeneralLabel.Label=\u0417\u0430\u0433\u0430\u043b\u044c\u043d\u0456 +6.LaTeXOptions.ConfigLabel.HelpText= +7.LaTeXOptions.ConfigLabel.Label=\u0424\u043e\u0440\u043c\u0430\u0442 LaTeX +8.LaTeXOptions.Config.HelpText= +137.Config.StringItemList=\u041d\u0430\u0434\u0447\u0438\u0441\u0442\u0430 \u0441\u0442\u0430\u0442\u0442\u044f +138.Config.StringItemList=\u0427\u0438\u0441\u0442\u0430 \u0441\u0442\u0430\u0442\u0442\u044f +139.Config.StringItemList=\u0417\u0430 \u0437\u0430\u043c\u043e\u0432\u0447\u0443\u0432\u0430\u043d\u043d\u044f\u043c +140.Config.StringItemList=\u041e\u043f\u0442\u0438\u043c\u0456\u0437\u043e\u0432\u0430\u043d\u043d\u0438\u0439 \u0434\u043b\u044f \u0434\u0440\u0443\u043a\u0443 +141.Config.StringItemList=\u041e\u043f\u0442\u0438\u043c\u0456\u0437\u043e\u0432\u0430\u043d\u0438\u0439 \u0434\u043b\u044f \u0435\u043a\u0440\u0430\u043d\u0443 (pdf) +142.Config.StringItemList=\u0412\u0438\u0431\u0456\u0440\u043a\u043e\u0432\u0438\u0439 +15.LaTeXOptions.BackendLabel.HelpText= +16.LaTeXOptions.BackendLabel.Label=\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 +17.LaTeXOptions.Backend.HelpText= +116.Backend.StringItemList=\u0412\u0431\u0443\u0434\u043e\u0432\u0430\u043d\u0438\u0439 +117.Backend.StringItemList=Pdf (pdfTeX) +118.Backend.StringItemList=Postscript (dvips) +119.Backend.StringItemList=\u041d\u0435 \u0432\u043a\u0430\u0437\u0430\u043d\u043e +22.LaTeXOptions.InputencodingLabel.HelpText= +23.LaTeXOptions.InputencodingLabel.Label=\u041a\u043e\u0434\u0443\u0432\u0430\u043d\u043d\u044f +24.LaTeXOptions.Inputencoding.HelpText= +120.Inputencoding.StringItemList=\u0421\u0442\u0430\u043d\u0434\u0430\u0440\u0442 ASCII \u0421\u0428\u0410 +121.Inputencoding.StringItemList=\u0417\u0430\u043f\u0445\u0456\u0434\u043d\u043e \u0454\u0432\u0440\u043e\u043f\u0435\u0439\u0441\u044c\u043a\u0435 (ISO 8859-1) +122.Inputencoding.StringItemList=\u0421\u0445\u0456\u0434\u043d\u043e \u0454\u0432\u0440\u043e\u043f\u0435\u0439\u0441\u044c\u043a\u0435 (ISO 8859-2) +123.Inputencoding.StringItemList=\u041b\u0430\u0442\u0438\u043d\u0438\u0446\u044f/\u0413\u0440\u0435\u0446\u044c\u043a\u0430 (ISO 8859-7) +124.Inputencoding.StringItemList=\u0421\u0445\u0456\u0434\u043d\u043e \u0454\u0432\u0440\u043e\u043f\u0435\u0439\u0441\u044c\u043a\u0435 \u0432\u0456\u0434 Microsoft (CP1250) +125.Inputencoding.StringItemList=\u041a\u0438\u0440\u0438\u043b\u0438\u0446\u044f \u043e\u0442 Microsoft (CP1251) +126.Inputencoding.StringItemList=\u0420\u043e\u0441\u0456\u0439\u0441\u044c\u043a\u0435 (KOI8-R) +127.Inputencoding.StringItemList=\u042e\u043d\u0456\u043a\u043e\u0434 (UTF8) +33.LaTeXOptions.Multilingual.HelpText= +34.LaTeXOptions.Multilingual.Label=\u0414\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u0438 \u0431\u0430\u0433\u0430\u0442\u043e\u043c\u043e\u0432\u043d\u0443 \u043f\u0456\u0434\u0442\u0440\u0438\u043c\u043a\u0443 +35.LaTeXOptions.GreekMath.HelpText= +36.LaTeXOptions.GreekMath.Label=\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u0433\u0440\u0435\u0446\u044c\u043a\u0456 \u043b\u0456\u0442\u0435\u0440\u0438 \u044f\u043a \u0441\u0438\u043c\u0432\u043e\u043b\u0438 +37.LaTeXOptions.AdditionalSymbols.HelpText= +38.LaTeXOptions.AdditionalSymbols.Label=\u041f\u0456\u0434\u0442\u0440\u0438\u043c\u0443\u0432\u0430\u0442\u0438 \u0434\u043e\u0434\u0430\u0442\u043a\u043e\u0432\u0456 \u0441\u0438\u043c\u0432\u043e\u043b\u0438 +39.LaTeXOptions.BibliographyLabel.HelpText= +40.LaTeXOptions.BibliographyLabel.Label=\u0411\u0456\u0431\u0456\u043b\u043e\u0433\u0440\u0430\u0444\u0456\u044f +41.LaTeXOptions.UseBibtex.HelpText= +42.LaTeXOptions.UseBibtex.Label=\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 BibTeX \u0434\u043b\u044f \u0431\u0456\u0431\u043b\u0456\u043e\u0433\u0440\u0430\u0444\u0456\u0457 +43.LaTeXOptions.BibtexStyleLabel.HelpText= +44.LaTeXOptions.BibtexStyleLabel.Label=\u0421\u0442\u0438\u043b\u044c BibTeX +45.LaTeXOptions.BibtexStyle.HelpText= +46.BibtexStyle.StringItemList=plain +47.BibtexStyle.StringItemList=unsrt +48.BibtexStyle.StringItemList=alpha +49.BibtexStyle.StringItemList=abbrv +50.LaTeXOptions.BibtexStyle.Text= +51.LaTeXOptions.FilesLabel.HelpText= +52.LaTeXOptions.FilesLabel.Label=\u0424\u0430\u0439\u043b\u0438 +53.LaTeXOptions.WrapLines.HelpText= +54.LaTeXOptions.WrapLines.Label=\u041f\u0435\u0440\u0435\u043d\u043e\u0441\u0438\u0442\u0438 \u0434\u043e\u0432\u0433\u0456 \u0440\u044f\u0434\u043a\u0438 +55.LaTeXOptions.WrapLinesAfterLabel.HelpText= +56.LaTeXOptions.WrapLinesAfterLabel.Label=\u041f\u043e\u0447\u0438\u043d\u0430\u044e\u0447\u0438 \u0456\u0437 \u0434\u043e\u0432\u0436\u0438\u043d\u0438 +57.LaTeXOptions.WrapLinesAfter.HelpText= +58.LaTeXOptions.SplitLinkedSections.HelpText= +59.LaTeXOptions.SplitLinkedSections.Label=\u0420\u043e\u0437\u0431\u0438\u0432\u0430\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u0437\u0432'\u044f\u0437\u0430\u043d\u0438\u043c \u0441\u0435\u043a\u0446\u0456\u044f\u043c +60.LaTeXOptions.SplitToplevelSections.HelpText= +61.LaTeXOptions.SplitToplevelSections.Label=\u0420\u043e\u0437\u0431\u0438\u0432\u0430\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u0441\u0435\u043a\u0446\u0456\u044f\u043c \u0432\u0435\u0440\u0445\u043d\u044c\u043e\u0433\u043e \u0440\u0456\u0432\u043d\u044f +62.LaTeXOptions.SaveImagesInSubdir.HelpText= +63.LaTeXOptions.SaveImagesInSubdir.Label=\u0417\u0431\u0435\u0440\u0456\u0433\u0430\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f \u0432 \u043f\u0456\u0434-\u0442\u0435\u043a\u0430\u0445 +64.LaTeXOptions.SpecialContentLabel.HelpText= +65.LaTeXOptions.SpecialContentLabel.Label=\u0421\u043f\u0435\u0446\u0456\u0430\u043b\u044c\u043d\u0438\u0439 \u0432\u043c\u0456\u0441\u0442 +66.LaTeXOptions.NotesLabel.HelpText= +67.LaTeXOptions.NotesLabel.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0437\u0430\u043c\u0456\u0442\u043e\u043a +68.LaTeXOptions.Notes.HelpText= +128.Notes.StringItemList=\u041d\u0435 \u0435\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 +129.Notes.StringItemList=\u042f\u043a \u043a\u043e\u043c\u0435\u043d\u0442\u0430\u0440\u0456 +130.Notes.StringItemList=\u042f\u043a \u043f\u0440\u0438\u043c\u0456\u0442\u043a\u0438 \u0437\u0431\u043e\u043a\u0443 +131.Notes.StringItemList=\u042f\u043a \u043f\u043e\u0441\u0438\u043b\u0430\u043d\u043d\u044f \u0432 PDF +73.LaTeXOptions.Metadata.HelpText= +74.LaTeXOptions.Metadata.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442 \u0432\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0435\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443 (\u043c\u0435\u0442\u0430\u0434\u0430\u043d\u0456) +75.LaTeXOptions.FiguresAndTablesLabel.HelpText= +76.LaTeXOptions.FiguresAndTablesLabel.Label=\u0406\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0456\u0457 \u0442\u0430 \u0442\u0430\u0431\u043b\u0438\u0446\u0456 +77.LaTeXOptions.OriginalImageSize.HelpText= +78.LaTeXOptions.OriginalImageSize.Label=\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u043e\u0440\u0438\u0433\u0456\u043d\u0430\u043b\u044c\u043d\u0438\u0439 \u0440\u043e\u0437\u043c\u0456\u0440 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u044c +79.LaTeXOptions.OptimizeSimpleTables.HelpText= +80.LaTeXOptions.OptimizeSimpleTables.Label=\u041e\u043f\u0442\u0438\u043c\u0456\u0437\u0443\u0432\u0430\u0442\u0438 \u043f\u0440\u043e\u0441\u0442\u0456 \u0442\u0430\u0431\u043b\u0438\u0446\u0456 +81.LaTeXOptions.SimpleTableLimitLabel.HelpText= +82.LaTeXOptions.SimpleTableLimitLabel.Label=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u0430 \u0448\u0438\u0440\u0438\u043d\u0430 \u0443 \u0441\u0438\u043c\u0432\u043e\u043b\u0430\u0445 +83.LaTeXOptions.SimpleTableLimit.HelpText= +84.LaTeXOptions.FloatTables.HelpText= +85.LaTeXOptions.FloatTables.Label=\u041f\u043b\u0430\u0432\u0430\u044e\u0447\u0456 \u0442\u0430\u0431\u043b\u0438\u0446\u0456 +86.LaTeXOptions.FloatFigures.HelpText= +87.LaTeXOptions.FloatFigures.Label=\u041f\u043b\u0430\u0432\u0430\u044e\u0447\u0456 \u0456\u043b\u044e\u0441\u0442\u0440\u0430\u0446\u0456\u0457 +88.LaTeXOptions.FloatOptionsLabel.HelpText= +89.LaTeXOptions.FloatOptionsLabel.Label=\u041f\u043b\u0430\u0432\u0430\u044e\u0447\u0435 \u0440\u043e\u0437\u043c\u0456\u0449\u0435\u043d\u043d\u044f +90.LaTeXOptions.FloatOptions.HelpText= +132.FloatOptions.StringItemList=\u0417\u0433\u043e\u0440\u0438 \u0430\u0431\u043e \u0437\u043d\u0438\u0437\u0443 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0438 +133.FloatOptions.StringItemList=\u0417\u0433\u043e\u0440\u0438 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0438 +134.FloatOptions.StringItemList=\u0417\u043d\u0438\u0437\u0443 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0438 +135.FloatOptions.StringItemList=\u0422\u0443\u0442 \u0430\u0431\u043e \u0437\u0433\u043e\u0440\u0438 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0438 +136.FloatOptions.StringItemList=\u0422\u0443\u0442 \u0430\u0431\u043e \u0437\u043d\u0438\u0437\u0443 \u0441\u0442\u043e\u0440\u0456\u043d\u043a\u0438 +96.LaTeXOptions.AutoCorrectLabel.HelpText= +97.LaTeXOptions.AutoCorrectLabel.Label=\u0410\u0432\u0442\u043e\u043a\u043e\u0440\u0435\u0433\u0443\u0432\u0430\u043d\u043d\u044f +98.LaTeXOptions.IgnoreHardPageBreaks.HelpText= +99.LaTeXOptions.IgnoreHardPageBreaks.Label=\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u0440\u043e\u0437\u0440\u0438\u0432\u0438 \u0441\u0442\u043e\u0440\u0456\u043d\u043e\u043a +100.LaTeXOptions.IgnoreHardLineBreaks.HelpText= +101.LaTeXOptions.IgnoreHardLineBreaks.Label=\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u0440\u043e\u0437\u0440\u0438\u0432\u0438 \u0440\u044f\u0434\u043a\u0456\u0432 +102.LaTeXOptions.IgnoreEmptyParagraphs.HelpText= +103.LaTeXOptions.IgnoreEmptyParagraphs.Label=\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u0440\u043e\u0437\u0440\u0438\u0432\u0438 \u0440\u044f\u0434\u043a\u0456\u0432 +104.LaTeXOptions.IgnoreDoubleSpaces.HelpText= +105.LaTeXOptions.IgnoreDoubleSpaces.Label=\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u043f\u043e\u0434\u0432\u0456\u0439\u043d\u0456 \u043f\u0440\u043e\u0431\u0456\u043b\u0438 +106.LaTeXOptions.ExportButton.HelpText= +107.LaTeXOptions.ExportButton.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 +108.LaTeXOptions.CancelButton.HelpText= +109.LaTeXOptions.CancelButton.Label=\u0412\u0456\u0434\u043c\u0456\u043d\u0438\u0442\u0438 diff --git a/source/oxt/writer2latex/W2LDialogs/LaTeXOptions.xdl b/source/oxt/writer2latex/W2LDialogs/LaTeXOptions.xdl new file mode 100644 index 0000000..14d8eda --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/LaTeXOptions.xdl @@ -0,0 +1,108 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd"> +<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="LaTeXOptions" dlg:left="153" dlg:top="95" dlg:width="360" dlg:height="262" dlg:help-text="&0.LaTeXOptions.HelpText" dlg:closeable="true" dlg:moveable="true" dlg:title="&1.LaTeXOptions.Title"> + <dlg:bulletinboard> + <dlg:fixedline dlg:id="FixedLine1" dlg:tab-index="0" dlg:left="179" dlg:top="8" dlg:width="2" dlg:height="222" dlg:help-text="&2.LaTeXOptions.FixedLine1.HelpText" dlg:value="&3.LaTeXOptions.FixedLine1.Label" dlg:align="vertical"/> + <dlg:text dlg:id="GeneralLabel" dlg:tab-index="1" dlg:left="5" dlg:top="8" dlg:width="156" dlg:height="12" dlg:help-text="&4.LaTeXOptions.GeneralLabel.HelpText" dlg:value="&5.LaTeXOptions.GeneralLabel.Label"/> + <dlg:text dlg:id="ConfigLabel" dlg:tab-index="2" dlg:left="12" dlg:top="22" dlg:width="47" dlg:height="12" dlg:help-text="&6.LaTeXOptions.ConfigLabel.HelpText" dlg:value="&7.LaTeXOptions.ConfigLabel.Label"/> + <dlg:menulist dlg:id="Config" dlg:tab-index="3" dlg:left="65" dlg:top="20" dlg:width="96" dlg:height="12" dlg:help-text="&8.LaTeXOptions.Config.HelpText" dlg:spin="true" dlg:linecount="6"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&137.Config.StringItemList"/> + <dlg:menuitem dlg:value="&138.Config.StringItemList"/> + <dlg:menuitem dlg:value="&139.Config.StringItemList"/> + <dlg:menuitem dlg:value="&140.Config.StringItemList"/> + <dlg:menuitem dlg:value="&141.Config.StringItemList"/> + <dlg:menuitem dlg:value="&142.Config.StringItemList"/> + </dlg:menupopup> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:ConfigChange" script:language="UNO"/> + </dlg:menulist> + <dlg:text dlg:id="BackendLabel" dlg:tab-index="4" dlg:left="12" dlg:top="36" dlg:width="47" dlg:height="12" dlg:help-text="&15.LaTeXOptions.BackendLabel.HelpText" dlg:value="&16.LaTeXOptions.BackendLabel.Label"/> + <dlg:menulist dlg:id="Backend" dlg:tab-index="5" dlg:left="65" dlg:top="34" dlg:width="96" dlg:height="12" dlg:help-text="&17.LaTeXOptions.Backend.HelpText" dlg:spin="true"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&116.Backend.StringItemList"/> + <dlg:menuitem dlg:value="&117.Backend.StringItemList"/> + <dlg:menuitem dlg:value="&118.Backend.StringItemList"/> + <dlg:menuitem dlg:value="&119.Backend.StringItemList"/> + </dlg:menupopup> + </dlg:menulist> + <dlg:text dlg:id="InputencodingLabel" dlg:tab-index="6" dlg:left="12" dlg:top="50" dlg:width="47" dlg:height="12" dlg:help-text="&22.LaTeXOptions.InputencodingLabel.HelpText" dlg:value="&23.LaTeXOptions.InputencodingLabel.Label"/> + <dlg:menulist dlg:id="Inputencoding" dlg:tab-index="7" dlg:left="65" dlg:top="48" dlg:width="96" dlg:height="12" dlg:help-text="&24.LaTeXOptions.Inputencoding.HelpText" dlg:spin="true" dlg:linecount="8"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&120.Inputencoding.StringItemList"/> + <dlg:menuitem dlg:value="&121.Inputencoding.StringItemList"/> + <dlg:menuitem dlg:value="&122.Inputencoding.StringItemList"/> + <dlg:menuitem dlg:value="&123.Inputencoding.StringItemList"/> + <dlg:menuitem dlg:value="&124.Inputencoding.StringItemList"/> + <dlg:menuitem dlg:value="&125.Inputencoding.StringItemList"/> + <dlg:menuitem dlg:value="&126.Inputencoding.StringItemList"/> + <dlg:menuitem dlg:value="&127.Inputencoding.StringItemList"/> + </dlg:menupopup> + </dlg:menulist> + <dlg:checkbox dlg:id="Multilingual" dlg:tab-index="8" dlg:left="12" dlg:top="64" dlg:width="149" dlg:height="12" dlg:help-text="&33.LaTeXOptions.Multilingual.HelpText" dlg:value="&34.LaTeXOptions.Multilingual.Label" dlg:checked="true"/> + <dlg:checkbox dlg:id="GreekMath" dlg:tab-index="9" dlg:left="12" dlg:top="78" dlg:width="149" dlg:height="12" dlg:help-text="&35.LaTeXOptions.GreekMath.HelpText" dlg:value="&36.LaTeXOptions.GreekMath.Label" dlg:checked="true"/> + <dlg:checkbox dlg:id="AdditionalSymbols" dlg:tab-index="10" dlg:left="12" dlg:top="92" dlg:width="149" dlg:height="12" dlg:help-text="&37.LaTeXOptions.AdditionalSymbols.HelpText" dlg:value="&38.LaTeXOptions.AdditionalSymbols.Label" dlg:checked="false"/> + <dlg:text dlg:id="BibliographyLabel" dlg:tab-index="11" dlg:left="5" dlg:top="106" dlg:width="156" dlg:height="12" dlg:help-text="&39.LaTeXOptions.BibliographyLabel.HelpText" dlg:value="&40.LaTeXOptions.BibliographyLabel.Label"/> + <dlg:checkbox dlg:id="UseBibtex" dlg:tab-index="12" dlg:left="12" dlg:top="120" dlg:width="149" dlg:height="12" dlg:help-text="&41.LaTeXOptions.UseBibtex.HelpText" dlg:value="&42.LaTeXOptions.UseBibtex.Label" dlg:checked="true"> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:UseBibtexChange" script:language="UNO"/> + </dlg:checkbox> + <dlg:text dlg:id="BibtexStyleLabel" dlg:tab-index="13" dlg:left="22" dlg:top="134" dlg:width="37" dlg:height="12" dlg:help-text="&43.LaTeXOptions.BibtexStyleLabel.HelpText" dlg:value="&44.LaTeXOptions.BibtexStyleLabel.Label"/> + <dlg:combobox dlg:id="BibtexStyle" dlg:tab-index="14" dlg:left="65" dlg:top="132" dlg:width="96" dlg:height="12" dlg:help-text="&45.LaTeXOptions.BibtexStyle.HelpText" dlg:value="&50.LaTeXOptions.BibtexStyle.Text" dlg:spin="true"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&46.BibtexStyle.StringItemList"/> + <dlg:menuitem dlg:value="&47.BibtexStyle.StringItemList"/> + <dlg:menuitem dlg:value="&48.BibtexStyle.StringItemList"/> + <dlg:menuitem dlg:value="&49.BibtexStyle.StringItemList"/> + </dlg:menupopup> + </dlg:combobox> + <dlg:text dlg:id="FilesLabel" dlg:tab-index="15" dlg:left="5" dlg:top="148" dlg:width="156" dlg:height="12" dlg:help-text="&51.LaTeXOptions.FilesLabel.HelpText" dlg:value="&52.LaTeXOptions.FilesLabel.Label"/> + <dlg:checkbox dlg:id="WrapLines" dlg:tab-index="16" dlg:left="12" dlg:top="162" dlg:width="139" dlg:height="12" dlg:help-text="&53.LaTeXOptions.WrapLines.HelpText" dlg:value="&54.LaTeXOptions.WrapLines.Label" dlg:checked="false"> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:WrapLinesChange" script:language="UNO"/> + </dlg:checkbox> + <dlg:text dlg:id="WrapLinesAfterLabel" dlg:tab-index="17" dlg:left="22" dlg:top="176" dlg:width="90" dlg:height="12" dlg:help-text="&55.LaTeXOptions.WrapLinesAfterLabel.HelpText" dlg:value="&56.LaTeXOptions.WrapLinesAfterLabel.Label"/> + <dlg:numericfield dlg:id="WrapLinesAfter" dlg:tab-index="18" dlg:left="128" dlg:top="174" dlg:width="32" dlg:height="12" dlg:help-text="&57.LaTeXOptions.WrapLinesAfter.HelpText" dlg:strict-format="true" dlg:decimal-accuracy="0" dlg:value="72" dlg:value-min="1" dlg:value-max="1000" dlg:spin="true" dlg:repeat="50"/> + <dlg:checkbox dlg:id="SplitLinkedSections" dlg:tab-index="19" dlg:left="12" dlg:top="190" dlg:width="149" dlg:height="12" dlg:help-text="&58.LaTeXOptions.SplitLinkedSections.HelpText" dlg:value="&59.LaTeXOptions.SplitLinkedSections.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="SplitToplevelSections" dlg:tab-index="20" dlg:left="12" dlg:top="204" dlg:width="149" dlg:height="12" dlg:help-text="&60.LaTeXOptions.SplitToplevelSections.HelpText" dlg:value="&61.LaTeXOptions.SplitToplevelSections.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="SaveImagesInSubdir" dlg:tab-index="21" dlg:left="12" dlg:top="218" dlg:width="149" dlg:height="12" dlg:help-text="&62.LaTeXOptions.SaveImagesInSubdir.HelpText" dlg:value="&63.LaTeXOptions.SaveImagesInSubdir.Label" dlg:checked="false"/> + <dlg:text dlg:id="SpecialContentLabel" dlg:tab-index="22" dlg:left="186" dlg:top="8" dlg:width="156" dlg:height="12" dlg:help-text="&64.LaTeXOptions.SpecialContentLabel.HelpText" dlg:value="&65.LaTeXOptions.SpecialContentLabel.Label"/> + <dlg:text dlg:id="NotesLabel" dlg:tab-index="23" dlg:left="193" dlg:top="22" dlg:width="47" dlg:height="12" dlg:help-text="&66.LaTeXOptions.NotesLabel.HelpText" dlg:value="&67.LaTeXOptions.NotesLabel.Label"/> + <dlg:menulist dlg:id="Notes" dlg:tab-index="24" dlg:left="246" dlg:top="20" dlg:width="96" dlg:height="12" dlg:help-text="&68.LaTeXOptions.Notes.HelpText" dlg:spin="true" dlg:linecount="4"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&128.Notes.StringItemList"/> + <dlg:menuitem dlg:value="&129.Notes.StringItemList"/> + <dlg:menuitem dlg:value="&130.Notes.StringItemList"/> + <dlg:menuitem dlg:value="&131.Notes.StringItemList"/> + </dlg:menupopup> + </dlg:menulist> + <dlg:checkbox dlg:id="Metadata" dlg:tab-index="25" dlg:left="193" dlg:top="36" dlg:width="149" dlg:height="12" dlg:help-text="&73.LaTeXOptions.Metadata.HelpText" dlg:value="&74.LaTeXOptions.Metadata.Label" dlg:checked="true"/> + <dlg:text dlg:id="FiguresAndTablesLabel" dlg:tab-index="26" dlg:left="186" dlg:top="50" dlg:width="156" dlg:height="12" dlg:help-text="&75.LaTeXOptions.FiguresAndTablesLabel.HelpText" dlg:value="&76.LaTeXOptions.FiguresAndTablesLabel.Label"/> + <dlg:checkbox dlg:id="OriginalImageSize" dlg:tab-index="27" dlg:left="193" dlg:top="64" dlg:width="149" dlg:height="12" dlg:help-text="&77.LaTeXOptions.OriginalImageSize.HelpText" dlg:value="&78.LaTeXOptions.OriginalImageSize.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="OptimizeSimpleTables" dlg:tab-index="28" dlg:left="193" dlg:top="78" dlg:width="149" dlg:height="12" dlg:help-text="&79.LaTeXOptions.OptimizeSimpleTables.HelpText" dlg:value="&80.LaTeXOptions.OptimizeSimpleTables.Label" dlg:checked="false"> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:OptimizeSimpleTablesChange" script:language="UNO"/> + </dlg:checkbox> + <dlg:text dlg:id="SimpleTableLimitLabel" dlg:tab-index="29" dlg:left="203" dlg:top="92" dlg:width="95" dlg:height="12" dlg:help-text="&81.LaTeXOptions.SimpleTableLimitLabel.HelpText" dlg:value="&82.LaTeXOptions.SimpleTableLimitLabel.Label"/> + <dlg:numericfield dlg:id="SimpleTableLimit" dlg:tab-index="30" dlg:left="310" dlg:top="90" dlg:width="32" dlg:height="12" dlg:help-text="&83.LaTeXOptions.SimpleTableLimit.HelpText" dlg:decimal-accuracy="0" dlg:value="40" dlg:spin="true"/> + <dlg:checkbox dlg:id="FloatTables" dlg:tab-index="31" dlg:left="193" dlg:top="106" dlg:width="149" dlg:height="12" dlg:help-text="&84.LaTeXOptions.FloatTables.HelpText" dlg:value="&85.LaTeXOptions.FloatTables.Label" dlg:checked="true"> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:FloatTablesChange" script:language="UNO"/> + </dlg:checkbox> + <dlg:checkbox dlg:id="FloatFigures" dlg:tab-index="32" dlg:left="193" dlg:top="120" dlg:width="149" dlg:height="12" dlg:help-text="&86.LaTeXOptions.FloatFigures.HelpText" dlg:value="&87.LaTeXOptions.FloatFigures.Label" dlg:checked="true"> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:FloatFiguresChange" script:language="UNO"/> + </dlg:checkbox> + <dlg:text dlg:id="FloatOptionsLabel" dlg:tab-index="33" dlg:left="193" dlg:top="134" dlg:width="47" dlg:height="12" dlg:help-text="&88.LaTeXOptions.FloatOptionsLabel.HelpText" dlg:value="&89.LaTeXOptions.FloatOptionsLabel.Label"/> + <dlg:menulist dlg:id="FloatOptions" dlg:tab-index="34" dlg:left="246" dlg:top="132" dlg:width="96" dlg:height="12" dlg:help-text="&90.LaTeXOptions.FloatOptions.HelpText" dlg:spin="true"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&132.FloatOptions.StringItemList"/> + <dlg:menuitem dlg:value="&133.FloatOptions.StringItemList"/> + <dlg:menuitem dlg:value="&134.FloatOptions.StringItemList"/> + <dlg:menuitem dlg:value="&135.FloatOptions.StringItemList"/> + <dlg:menuitem dlg:value="&136.FloatOptions.StringItemList"/> + </dlg:menupopup> + </dlg:menulist> + <dlg:text dlg:id="AutoCorrectLabel" dlg:tab-index="35" dlg:left="186" dlg:top="148" dlg:width="156" dlg:height="12" dlg:help-text="&96.LaTeXOptions.AutoCorrectLabel.HelpText" dlg:value="&97.LaTeXOptions.AutoCorrectLabel.Label"/> + <dlg:checkbox dlg:id="IgnoreHardPageBreaks" dlg:tab-index="36" dlg:left="193" dlg:top="162" dlg:width="149" dlg:height="12" dlg:help-text="&98.LaTeXOptions.IgnoreHardPageBreaks.HelpText" dlg:value="&99.LaTeXOptions.IgnoreHardPageBreaks.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="IgnoreHardLineBreaks" dlg:tab-index="37" dlg:left="193" dlg:top="176" dlg:width="149" dlg:height="12" dlg:help-text="&100.LaTeXOptions.IgnoreHardLineBreaks.HelpText" dlg:value="&101.LaTeXOptions.IgnoreHardLineBreaks.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="IgnoreEmptyParagraphs" dlg:tab-index="38" dlg:left="193" dlg:top="190" dlg:width="149" dlg:height="12" dlg:help-text="&102.LaTeXOptions.IgnoreEmptyParagraphs.HelpText" dlg:value="&103.LaTeXOptions.IgnoreEmptyParagraphs.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="IgnoreDoubleSpaces" dlg:tab-index="39" dlg:left="193" dlg:top="204" dlg:width="149" dlg:height="12" dlg:help-text="&104.LaTeXOptions.IgnoreDoubleSpaces.HelpText" dlg:value="&105.LaTeXOptions.IgnoreDoubleSpaces.Label" dlg:checked="false"/> + <dlg:button dlg:id="ExportButton" dlg:tab-index="40" dlg:left="5" dlg:top="242" dlg:width="60" dlg:height="13" dlg:help-text="&106.LaTeXOptions.ExportButton.HelpText" dlg:value="&107.LaTeXOptions.ExportButton.Label" dlg:button-type="ok"/> + <dlg:button dlg:id="CancelButton" dlg:tab-index="41" dlg:left="75" dlg:top="242" dlg:width="60" dlg:height="13" dlg:help-text="&108.LaTeXOptions.CancelButton.HelpText" dlg:value="&109.LaTeXOptions.CancelButton.Label" dlg:button-type="cancel"/> + </dlg:bulletinboard> +</dlg:window> \ No newline at end of file diff --git a/source/oxt/writer2latex/W2LDialogs/Module1.xba b/source/oxt/writer2latex/W2LDialogs/Module1.xba new file mode 100644 index 0000000..1aa18e8 --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/Module1.xba @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd"> +<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic">REM ***** BASIC ***** + +Sub Main + +End Sub</script:module> \ No newline at end of file diff --git a/source/oxt/writer2latex/W2LDialogs/dialog.xlb b/source/oxt/writer2latex/W2LDialogs/dialog.xlb new file mode 100644 index 0000000..7c0c251 --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/dialog.xlb @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="W2LDialogs" library:readonly="false" library:passwordprotected="false"> + <library:element library:name="LaTeXOptions"/> +</library:library> \ No newline at end of file diff --git a/source/oxt/writer2latex/W2LDialogs/script.xlb b/source/oxt/writer2latex/W2LDialogs/script.xlb new file mode 100644 index 0000000..55efe26 --- /dev/null +++ b/source/oxt/writer2latex/W2LDialogs/script.xlb @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="W2LDialogs" library:readonly="false" library:passwordprotected="false"> + <library:element library:name="Module1"/> +</library:library> \ No newline at end of file diff --git a/source/oxt/writer2latex/desc_da.txt b/source/oxt/writer2latex/desc_da.txt new file mode 100644 index 0000000..7550cc1 --- /dev/null +++ b/source/oxt/writer2latex/desc_da.txt @@ -0,0 +1 @@ +Writer2LaTeX installerer eksportfiltre til LaTeX og BibTeX i Writer \ No newline at end of file diff --git a/source/oxt/writer2latex/desc_en.txt b/source/oxt/writer2latex/desc_en.txt new file mode 100644 index 0000000..5710fc6 --- /dev/null +++ b/source/oxt/writer2latex/desc_en.txt @@ -0,0 +1 @@ +Writer2LaTeX provides Writer export filters for LaTeX and BibTeX \ No newline at end of file diff --git a/source/oxt/writer2latex/desc_fr.txt b/source/oxt/writer2latex/desc_fr.txt new file mode 100644 index 0000000..587ad2e --- /dev/null +++ b/source/oxt/writer2latex/desc_fr.txt @@ -0,0 +1 @@ +Writer2LaTeX exporte les documents Writer vers LaTeX et BibTeX \ No newline at end of file diff --git a/source/oxt/writer2latex/desc_ru.txt b/source/oxt/writer2latex/desc_ru.txt new file mode 100644 index 0000000..8867786 --- /dev/null +++ b/source/oxt/writer2latex/desc_ru.txt @@ -0,0 +1 @@ +Writer2LaTeX обеспечивает Writer фильтрами экспорта в LaTeX и BibTeX diff --git a/source/oxt/writer2latex/desc_uk.txt b/source/oxt/writer2latex/desc_uk.txt new file mode 100644 index 0000000..d23dd65 --- /dev/null +++ b/source/oxt/writer2latex/desc_uk.txt @@ -0,0 +1 @@ +Writer2LaTeX забезпечує Writer фільтрами експорту до LaTeX та BibTeX diff --git a/source/oxt/writer2latex/description.xml b/source/oxt/writer2latex/description.xml new file mode 100644 index 0000000..a27ccad --- /dev/null +++ b/source/oxt/writer2latex/description.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8"?> +<description xmlns="http://openoffice.org/extensions/description/2006" + xmlns:d="http://openoffice.org/extensions/description/2006" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <identifier value="org.openoffice.da.writer2latex.oxt"/> + + <version value="0.9.4" /> + + <dependencies> + <OpenOffice.org-minimal-version value="2.2" d:name="OpenOffice.org 2.2"/> + </dependencies> + + <display-name> + <name lang="da">Writer2LaTeX eksportfiltre</name> + <name lang="en">Writer2LaTeX export filters</name> + <name lang="de">Filtres d'exportation Writer2LaTeX</name> + <name lang="ru">Фильтры экспорта Writer2LaTeX</name> + <name lang="uk">Фільтри експорту Writer2LaTeX</name> + </display-name> + + <extension-description> + <src xlink:href="description/desc_da.txt" lang="da" /> + <src xlink:href="description/desc_en.txt" lang="en" /> + <src xlink:href="description/desc_fr.txt" lang="fr" /> + <src xlink:href="description/desc_ru.txt" lang="ru" /> + <src xlink:href="description/desc_uk.txt" lang="uk" /> + </extension-description> + +</description> diff --git a/source/oxt/writer2latex/w2l_filters.xcu b/source/oxt/writer2latex/w2l_filters.xcu new file mode 100644 index 0000000..3dc47ec --- /dev/null +++ b/source/oxt/writer2latex/w2l_filters.xcu @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:package="org.openoffice.TypeDetection" oor:name="Filter"> + + <node oor:name="Filters"> + + <!-- hide integrated Writer2LaTeX 0.4 from OOo 2.0.4 --> + <node oor:name="LaTeX_Writer" oor:op="replace"> + <prop oor:name="Flags"><value>NOTINFILEDIALOG NOTINCHOOSER</value></prop> + </node> + <node oor:name="BibTeX_Writer" oor:op="replace"> + <prop oor:name="Flags"><value>NOTINFILEDIALOG NOTINCHOOSER</value></prop> + </node> + + <node oor:name="org.openoffice.da.writer2latex" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>writer_latex_File</value></prop> + <prop oor:name="DocumentService"><value>com.sun.star.text.TextDocument</value></prop> + <prop oor:name="UIComponent"><value>org.openoffice.da.comp.writer2latex.LaTeXOptionsDialog</value></prop> + <prop oor:name="UserData"><value>org.openoffice.da.comp.writer2latex.W2LExportFilter unused com.sun.star.comp.Writer.XMLOasisImporter com.sun.star.comp.Writer.XMLOasisExporter staroffice/sxw application/x-latex</value></prop> + <prop oor:name="FilterService"><value>com.sun.star.comp.Writer.XmlFilterAdaptor</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="UIName"> + <value>LaTeX 2e</value> + </prop> + <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop> + </node> + + <node oor:name="org.openoffice.da.writer2bibtex" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>writer_bibtex_File</value></prop> + <prop oor:name="DocumentService"><value>com.sun.star.text.TextDocument</value></prop> + <prop oor:name="UIComponent"/> + <prop oor:name="UserData"><value>org.openoffice.da.comp.writer2latex.W2LExportFilter unused com.sun.star.comp.Writer.XMLOasisImporter com.sun.star.comp.Writer.XMLOasisExporter staroffice/sxw application/x-bibtex</value></prop> + <prop oor:name="FilterService"><value>com.sun.star.comp.Writer.XmlFilterAdaptor</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="UIName"> + <value>BibTeX</value> + </prop> + <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop> + </node> + + + </node> + +</oor:component-data> diff --git a/source/oxt/writer2latex/w2l_types.xcu b/source/oxt/writer2latex/w2l_types.xcu new file mode 100644 index 0000000..764c981 --- /dev/null +++ b/source/oxt/writer2latex/w2l_types.xcu @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:package="org.openoffice.TypeDetection" oor:name="Types"> + + <node oor:name="Types"> + + <node oor:name="writer_bibtex_File" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="DetectService"/> + <prop oor:name="URLPattern"/> + <prop oor:name="Extensions"><value>bib</value></prop> + <prop oor:name="MediaType"/> + <prop oor:name="Preferred"><value>false</value></prop> + <prop oor:name="UIName"> + <value>BibTeX Data File</value> + </prop> + <prop oor:name="ClipboardFormat"/> + </node> + + <node oor:name="writer_latex_File" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="DetectService"/> + <prop oor:name="URLPattern"/> + <prop oor:name="Extensions"><value>tex</value></prop> + <prop oor:name="MediaType"/> + <prop oor:name="Preferred"><value>false</value></prop> + <prop oor:name="UIName"> + <value>LaTeX 2e</value> + </prop> + <prop oor:name="ClipboardFormat"/> + </node> + + </node> + +</oor:component-data> diff --git a/source/oxt/writer2xhtml/META-INF/manifest.xml b/source/oxt/writer2xhtml/META-INF/manifest.xml new file mode 100644 index 0000000..4c3483e --- /dev/null +++ b/source/oxt/writer2xhtml/META-INF/manifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd"> +<manifest:manifest> + <manifest:file-entry + manifest:full-path="writer2xhtml-filter.jar" + manifest:media-type="application/vnd.sun.star.uno-component;type=Java"/> + + <manifest:file-entry + manifest:full-path="w2x_types.xcu" + manifest:media-type="application/vnd.sun.star.configuration-data"/> + + <manifest:file-entry + manifest:full-path="w2x_filters.xcu" + manifest:media-type="application/vnd.sun.star.configuration-data"/> + + <manifest:file-entry + manifest:full-path="Options.xcs" + manifest:media-type="application/vnd.sun.star.configuration-schema"/> + + <manifest:file-entry + manifest:full-path="Options.xcu" + manifest:media-type="application/vnd.sun.star.configuration-data"/> + + <manifest:file-entry + manifest:full-path="W2XDialogs/" + manifest:media-type="application/vnd.sun.star.basic-library"/> + + <manifest:file-entry + manifest:full-path="writer2xhtml.rdb" + manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB"/> + +</manifest:manifest> diff --git a/source/oxt/writer2xhtml/Options.xcs b/source/oxt/writer2xhtml/Options.xcs new file mode 100644 index 0000000..4204351 --- /dev/null +++ b/source/oxt/writer2xhtml/Options.xcs @@ -0,0 +1,72 @@ +<?xml version='1.0' encoding='UTF-8'?> +<oor:component-schema oor:name="Options" + oor:package="org.openoffice.da.Writer2xhtml" + xml:lang="en-US" + xmlns:oor="http://openoffice.org/2001/registry" + xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <templates> + <group oor:name="Configuration"> + <prop oor:name="DisplayName" oor:type="xs:string" oor:localized="true" /> + <prop oor:name="LockedOptions" oor:type="xs:string" /> + <prop oor:name="ConfigURL" oor:type="xs:string" /> + <prop oor:name="TargetTemplateURL" oor:type="xs:string" /> + </group> + <group oor:name="Template"> + <prop oor:name="TemplateName" oor:type="xs:string" /> + <prop oor:name="ConfigName" oor:type="xs:string" /> + </group> + </templates> + <component> + <group oor:name="XhtmlOptions"> + <!-- Style --> + <prop oor:name="Config" oor:type="xs:short" /> + <prop oor:name="ConfigName" oor:type="xs:string" /> + <prop oor:name="ConvertToPx" oor:type="xs:boolean" /> + <prop oor:name="Scaling" oor:type="xs:int" /> + <prop oor:name="ColumnScaling" oor:type="xs:int" /> + <prop oor:name="OriginalImageSize" oor:type="xs:boolean"/> + <!-- Special content --> + <prop oor:name="Notes" oor:type="xs:boolean" /> + <prop oor:name="UseDublinCore" oor:type="xs:boolean" /> + <!-- AutoCorrect --> + <prop oor:name="IgnoreEmptyParagraphs" oor:type="xs:boolean" /> + <prop oor:name="IgnoreHardLineBreaks" oor:type="xs:boolean" /> + <prop oor:name="IgnoreDoubleSpaces" oor:type="xs:boolean" /> + <!-- Files --> + <prop oor:name="Split" oor:type="xs:boolean" /> + <prop oor:name="SplitLevel" oor:type="xs:short" /> + <prop oor:name="RepeatLevels" oor:type="xs:short" /> + <prop oor:name="SaveImagesInSubdir" oor:type="xs:boolean" /> + <prop oor:name="XsltPath" oor:type="xs:string" /> + <!-- Configurations --> + <set oor:name="Configurations" oor:node-type="Configuration" /> + <set oor:name="Templates" oor:node-type="Template" /> + </group> + <group oor:name="XhtmlOptionsCalc" > + <!-- Style --> + <prop oor:name="Config" oor:type="xs:short" /> + <prop oor:name="ConfigName" oor:type="xs:string" /> + <prop oor:name="ConvertToPx" oor:type="xs:boolean" /> + <prop oor:name="Scaling" oor:type="xs:int" /> + <prop oor:name="ColumnScaling" oor:type="xs:int" /> + <prop oor:name="OriginalImageSize" oor:type="xs:boolean"/> + <!-- Content --> + <prop oor:name="Notes" oor:type="xs:boolean" /> + <prop oor:name="UseDublinCore" oor:type="xs:boolean" /> + <!-- Sheets --> + <prop oor:name="DisplayHiddenSheets" oor:type="xs:boolean" /> + <prop oor:name="DisplayHiddenRowsCols" oor:type="xs:boolean" /> + <prop oor:name="DisplayFilteredRowsCols" oor:type="xs:boolean" /> + <prop oor:name="ApplyPrintRanges" oor:type="xs:boolean" /> + <prop oor:name="UseTitleAsHeading" oor:type="xs:boolean" /> + <prop oor:name="UseSheetNamesAsHeadings" oor:type="xs:boolean" /> + <!-- Files --> + <prop oor:name="CalcSplit" oor:type="xs:boolean" /> + <prop oor:name="SaveImagesInSubdir" oor:type="xs:boolean" /> + <!-- Configurations --> + <set oor:name="Configurations" oor:node-type="Configuration" /> + <set oor:name="Templates" oor:node-type="Template" /> + </group> + </component> +</oor:component-schema> \ No newline at end of file diff --git a/source/oxt/writer2xhtml/Options.xcu b/source/oxt/writer2xhtml/Options.xcu new file mode 100644 index 0000000..ab5ed63 --- /dev/null +++ b/source/oxt/writer2xhtml/Options.xcu @@ -0,0 +1,110 @@ +<?xml version='1.0' encoding='UTF-8'?> +<oor:component-data oor:name="Options" + oor:package="org.openoffice.da.Writer2xhtml" + xml:lang="en-US" + xmlns:oor="http://openoffice.org/2001/registry" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <node oor:name="XhtmlOptions"> + <prop oor:name="Config" oor:type="xs:short"> + <value>0</value> + </prop> + <prop oor:name="ConfigName" oor:type="xs:string"> + <value></value> + </prop> + <prop oor:name="ConvertToPx" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="Scaling" oor:type="xs:int"> + <value>100</value> + </prop> + <prop oor:name="ColumnScaling" oor:type="xs:int"> + <value>100</value> + </prop> + <prop oor:name="OriginalImageSize" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="Notes" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="UseDublinCore" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="IgnoreEmptyParagraphs" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="IgnoreHardLineBreaks" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="IgnoreDoubleSpaces" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="Split" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="SplitLevel" oor:type="xs:short"> + <value>1</value> + </prop> + <prop oor:name="RepeatLevels" oor:type="xs:short"> + <value>5</value> + </prop> + <prop oor:name="SaveImagesInSubdir" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="XsltPath" oor:type="xs:string"> + <value></value> + </prop> + </node> + <node oor:name="XhtmlOptionsCalc"> + <prop oor:name="Config" oor:type="xs:short"> + <value>0</value> + </prop> + <prop oor:name="ConfigName" oor:type="xs:string"> + <value></value> + </prop> + <prop oor:name="ConvertToPx" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="Scaling" oor:type="xs:int"> + <value>100</value> + </prop> + <prop oor:name="ColumnScaling" oor:type="xs:int"> + <value>100</value> + </prop> + <prop oor:name="OriginalImageSize" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="Notes" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="UseDublinCore" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="DisplayHiddenSheets" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="DisplayHiddenRowsCols" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="DisplayFilteredRowsCols" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="ApplyPrintRanges" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="UseTitleAsHeading" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="UseSheetNamesAsHeadings" oor:type="xs:boolean"> + <value>true</value> + </prop> + <prop oor:name="CalcSplit" oor:type="xs:boolean"> + <value>false</value> + </prop> + <prop oor:name="SaveImagesInSubdir" oor:type="xs:boolean"> + <value>false</value> + </prop> + </node> +</oor:component-data> + + + \ No newline at end of file diff --git a/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_da_DK.properties b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_da_DK.properties new file mode 100644 index 0000000..76d16f4 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_da_DK.properties @@ -0,0 +1,131 @@ +# Strings for Dialog Library W2XDialogs +0.XhtmlOptions.HelpText= +1.XhtmlOptions.Title=XHTML-indstillinger (Writer2xhtml) +2.XhtmlOptions.StyleLabel.HelpText= +3.XhtmlOptions.StyleLabel.Label=Typografi +4.XhtmlOptions.ConfigLabel.HelpText= +5.XhtmlOptions.ConfigLabel.Label=Anvend typografi +6.XhtmlOptions.Config.HelpText= +17.XhtmlOptions.ScalingLabel.HelpText= +18.XhtmlOptions.ScalingLabel.Label=Skalering +19.XhtmlOptions.Scaling.HelpText= +20.XhtmlOptions.ScalingPercentLabel.HelpText= +21.XhtmlOptions.ScalingPercentLabel.Label=% +22.XhtmlOptions.ColumnScalingLabel.HelpText= +23.XhtmlOptions.ColumnScalingLabel.Label=Kolonneskalering +24.XhtmlOptions.ColumnScaling.HelpText= +25.XhtmlOptions.ColumnScalingPercentLabel.HelpText= +26.XhtmlOptions.ColumnScalingPercentLabel.Label=% +27.XhtmlOptions.ConvertToPx.HelpText= +28.XhtmlOptions.ConvertToPx.Label=Omregn enheder til px (pixels) +29.XhtmlOptions.OriginalImageSize.HelpText= +30.XhtmlOptions.OriginalImageSize.Label=Brug oprindelig billedst\u00f8rrelse +31.XhtmlOptions.SpecialContentLabel.HelpText= +32.XhtmlOptions.SpecialContentLabel.Label=Specielt indhold +33.XhtmlOptions.Notes.HelpText= +34.XhtmlOptions.Notes.Label=Eksporter noter +35.XhtmlOptions.UseDublinCore.HelpText= +36.XhtmlOptions.UseDublinCore.Label=Eksporter dokumentegenskaber (Dublin Core Metadata) +37.XhtmlOptions.AutoCorrectLabel.HelpText= +38.XhtmlOptions.AutoCorrectLabel.Label=Autokorrektur +39.XhtmlOptions.IgnoreHardLineBreaks.HelpText= +40.XhtmlOptions.IgnoreHardLineBreaks.Label=Ignorer manuelle linjeskift +41.XhtmlOptions.IgnoreEmptyParagraphs.HelpText= +42.XhtmlOptions.IgnoreEmptyParagraphs.Label=Ignorer tomme afsnit +43.XhtmlOptions.IgnoreDoubleSpaces.HelpText= +44.XhtmlOptions.IgnoreDoubleSpaces.Label=Ignorer dobbelte mellemrum +45.XhtmlOptions.FilesLabel.HelpText= +46.XhtmlOptions.FilesLabel.Label=Filer +47.XhtmlOptions.Split.HelpText= +48.XhtmlOptions.Split.Label=Del dokumentet ved overskrifter +49.XhtmlOptions.SplitLevelLabel.HelpText= +50.XhtmlOptions.SplitLevelLabel.Label=Overskriftsniveau +51.XhtmlOptions.SplitLevel.HelpText= +52.SplitLevel.StringItemList=1 +53.SplitLevel.StringItemList=2 +54.SplitLevel.StringItemList=3 +55.SplitLevel.StringItemList=4 +56.SplitLevel.StringItemList=5 +57.SplitLevel.StringItemList=6 +58.XhtmlOptions.RepeatLevelsLabel.HelpText= +59.XhtmlOptions.RepeatLevelsLabel.Label=Gentag overskriftsniveauer +60.XhtmlOptions.RepeatLevels.HelpText= +61.RepeatLevels.StringItemList=0 +62.RepeatLevels.StringItemList=1 +63.RepeatLevels.StringItemList=2 +64.RepeatLevels.StringItemList=3 +65.RepeatLevels.StringItemList=4 +66.RepeatLevels.StringItemList=5 +67.XhtmlOptions.SaveImagesInSubdir.HelpText= +68.XhtmlOptions.SaveImagesInSubdir.Label=Gem billeder i undermappe +69.XhtmlOptions.XsltPathLabel.HelpText= +70.XhtmlOptions.XsltPathLabel.Label=XSLT-sti +71.XhtmlOptions.XsltPath.HelpText= +72.XhtmlOptions.XsltPath.Text= +73.XhtmlOptions.ExportButton.HelpText= +74.XhtmlOptions.ExportButton.Label=Eksporter +75.XhtmlOptions.CancelButton.HelpText= +76.XhtmlOptions.CancelButton.Label=Afbryd +77.XhtmlOptionsCalc.HelpText= +78.XhtmlOptionsCalc.Title=XHTML-indstillinger (Calc2xhtml) +79.XhtmlOptionsCalc.StyleLabel.HelpText= +80.XhtmlOptionsCalc.StyleLabel.Label=Typografi +81.XhtmlOptionsCalc.ConfigLabel.HelpText= +82.XhtmlOptionsCalc.ConfigLabel.Label=Anvend typografi +83.XhtmlOptionsCalc.Config.HelpText= +86.XhtmlOptionsCalc.ScalingLabel.HelpText= +87.XhtmlOptionsCalc.ScalingLabel.Label=Skalering +88.XhtmlOptionsCalc.Scaling.HelpText= +89.XhtmlOptionsCalc.ScalingPercentLabel.HelpText= +90.XhtmlOptionsCalc.ScalingPercentLabel.Label=% +91.XhtmlOptionsCalc.ColumnScalingLabel.HelpText= +92.XhtmlOptionsCalc.ColumnScalingLabel.Label=Kolonneskalering +93.XhtmlOptionsCalc.ColumnScaling.HelpText= +94.XhtmlOptionsCalc.ColumnScalingPercentLabel.HelpText= +95.XhtmlOptionsCalc.ColumnScalingPercentLabel.Label=% +96.XhtmlOptionsCalc.ConvertToPx.HelpText= +97.XhtmlOptionsCalc.ConvertToPx.Label=Omregn enheder til px (pixels) +98.XhtmlOptionsCalc.OriginalImageSize.HelpText= +99.XhtmlOptionsCalc.OriginalImageSize.Label=Brug oprindelig billedst\u00f8rrelse +100.XhtmlOptionsCalc.SpecialContentLabel.HelpText= +101.XhtmlOptionsCalc.SpecialContentLabel.Label=Specielt indhold +102.XhtmlOptionsCalc.Notes.HelpText= +103.XhtmlOptionsCalc.Notes.Label=Eksporter noter +104.XhtmlOptionsCalc.UseDublinCore.HelpText= +105.XhtmlOptionsCalc.UseDublinCore.Label=Eksporter dokumentegenskaber (Dublin Core Metadata) +106.XhtmlOptionsCalc.SheetsLabel.HelpText= +107.XhtmlOptionsCalc.SheetsLabel.Label=Ark +108.XhtmlOptionsCalc.DisplayHiddenSheets.HelpText= +109.XhtmlOptionsCalc.DisplayHiddenSheets.Label=Vis skjulte ark +110.XhtmlOptionsCalc.DisplayHiddenRowsCols.HelpText= +111.XhtmlOptionsCalc.DisplayHiddenRowsCols.Label=Vis skjulte r\u00e6kker og kolonner +112.XhtmlOptionsCalc.DisplayFilteredRowsCols.HelpText= +113.XhtmlOptionsCalc.DisplayFilteredRowsCols.Label=Vis filtrerede r\u00e6kker og kolonner +114.XhtmlOptionsCalc.ApplyPrintRanges.HelpText= +115.XhtmlOptionsCalc.ApplyPrintRanges.Label=Anvend udskriftsomr\u00e5der +116.XhtmlOptionsCalc.UseTitleAsHeading.HelpText= +117.XhtmlOptionsCalc.UseTitleAsHeading.Label=Brug titel som overskrift +118.XhtmlOptionsCalc.UseSheetNamesAsHeadings.HelpText= +119.XhtmlOptionsCalc.UseSheetNamesAsHeadings.Label=Brug arknavne som overskrifter +120.XhtmlOptionsCalc.FilesLabel.HelpText= +121.XhtmlOptionsCalc.FilesLabel.Label=Filer +122.XhtmlOptionsCalc.CalcSplit.HelpText= +123.XhtmlOptionsCalc.CalcSplit.Label=Gem ark i separate filer +124.XhtmlOptionsCalc.SaveImagesInSubdir.HelpText= +125.XhtmlOptionsCalc.SaveImagesInSubdir.Label=Gem billeder i undermappe +126.XhtmlOptionsCalc.ExportButton.HelpText= +127.XhtmlOptionsCalc.ExportButton.Label=Eksporter +128.XhtmlOptionsCalc.CancelButton.HelpText= +129.XhtmlOptionsCalc.CancelButton.Label=Afbryd +130.Config.StringItemList=Oprindelig formatering +131.Config.StringItemList=Chocolade +132.Config.StringItemList=Midnat +133.Config.StringItemList=Modernistisk +134.Config.StringItemList=Gammeldags +135.Config.StringItemList=St\u00e5l +136.Config.StringItemList=Schweizisk +137.Config.StringItemList=Traditionelt +138.Config.StringItemList=Ultramarine +139.Config.StringItemList=Brugerdefineret +140.Config.StringItemList=Oprindelig formatering +141.Config.StringItemList=Brugerdefineret diff --git a/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_de_DE.properties b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_de_DE.properties new file mode 100644 index 0000000..a94c123 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_de_DE.properties @@ -0,0 +1,131 @@ +# Strings for Dialog Library W2XDialogs (untranslated) +0.XhtmlOptions.HelpText= +1.XhtmlOptions.Title=XHTML Options (Writer2xhtml) +2.XhtmlOptions.StyleLabel.HelpText= +3.XhtmlOptions.StyleLabel.Label=Style +4.XhtmlOptions.ConfigLabel.HelpText= +5.XhtmlOptions.ConfigLabel.Label=Use style +6.XhtmlOptions.Config.HelpText= +17.XhtmlOptions.ScalingLabel.HelpText= +18.XhtmlOptions.ScalingLabel.Label=Scaling +19.XhtmlOptions.Scaling.HelpText= +20.XhtmlOptions.ScalingPercentLabel.HelpText= +21.XhtmlOptions.ScalingPercentLabel.Label=% +22.XhtmlOptions.ColumnScalingLabel.HelpText= +23.XhtmlOptions.ColumnScalingLabel.Label=Column scaling +24.XhtmlOptions.ColumnScaling.HelpText= +25.XhtmlOptions.ColumnScalingPercentLabel.HelpText= +26.XhtmlOptions.ColumnScalingPercentLabel.Label=% +27.XhtmlOptions.ConvertToPx.HelpText= +28.XhtmlOptions.ConvertToPx.Label=Convert units to px (pixels) +29.XhtmlOptions.OriginalImageSize.HelpText= +30.XhtmlOptions.OriginalImageSize.Label=Use original image size +31.XhtmlOptions.SpecialContentLabel.HelpText= +32.XhtmlOptions.SpecialContentLabel.Label=Special content +33.XhtmlOptions.Notes.HelpText= +34.XhtmlOptions.Notes.Label=Export notes +35.XhtmlOptions.UseDublinCore.HelpText= +36.XhtmlOptions.UseDublinCore.Label=Export document properties (Dublin Core Metadata) +37.XhtmlOptions.AutoCorrectLabel.HelpText= +38.XhtmlOptions.AutoCorrectLabel.Label=AutoCorrect +39.XhtmlOptions.IgnoreHardLineBreaks.HelpText= +40.XhtmlOptions.IgnoreHardLineBreaks.Label=Ignore hard line breaks +41.XhtmlOptions.IgnoreEmptyParagraphs.HelpText= +42.XhtmlOptions.IgnoreEmptyParagraphs.Label=Ignore empty paragraphs +43.XhtmlOptions.IgnoreDoubleSpaces.HelpText= +44.XhtmlOptions.IgnoreDoubleSpaces.Label=Ignore double spaces +45.XhtmlOptions.FilesLabel.HelpText= +46.XhtmlOptions.FilesLabel.Label=Files +47.XhtmlOptions.Split.HelpText= +48.XhtmlOptions.Split.Label=Split document at headings +49.XhtmlOptions.SplitLevelLabel.HelpText= +50.XhtmlOptions.SplitLevelLabel.Label=Heading level +51.XhtmlOptions.SplitLevel.HelpText= +52.SplitLevel.StringItemList=1 +53.SplitLevel.StringItemList=2 +54.SplitLevel.StringItemList=3 +55.SplitLevel.StringItemList=4 +56.SplitLevel.StringItemList=5 +57.SplitLevel.StringItemList=6 +58.XhtmlOptions.RepeatLevelsLabel.HelpText= +59.XhtmlOptions.RepeatLevelsLabel.Label=Repeat heading levels +60.XhtmlOptions.RepeatLevels.HelpText= +61.RepeatLevels.StringItemList=0 +62.RepeatLevels.StringItemList=1 +63.RepeatLevels.StringItemList=2 +64.RepeatLevels.StringItemList=3 +65.RepeatLevels.StringItemList=4 +66.RepeatLevels.StringItemList=5 +67.XhtmlOptions.SaveImagesInSubdir.HelpText= +68.XhtmlOptions.SaveImagesInSubdir.Label=Save images in subdirectory +69.XhtmlOptions.XsltPathLabel.HelpText= +70.XhtmlOptions.XsltPathLabel.Label=XSLT path +71.XhtmlOptions.XsltPath.HelpText= +72.XhtmlOptions.XsltPath.Text= +73.XhtmlOptions.ExportButton.HelpText= +74.XhtmlOptions.ExportButton.Label=Export +75.XhtmlOptions.CancelButton.HelpText= +76.XhtmlOptions.CancelButton.Label=Cancel +77.XhtmlOptionsCalc.HelpText= +78.XhtmlOptionsCalc.Title=XHTML Options (Calc2xhtml) +79.XhtmlOptionsCalc.StyleLabel.HelpText= +80.XhtmlOptionsCalc.StyleLabel.Label=Style +81.XhtmlOptionsCalc.ConfigLabel.HelpText= +82.XhtmlOptionsCalc.ConfigLabel.Label=Use style +83.XhtmlOptionsCalc.Config.HelpText= +86.XhtmlOptionsCalc.ScalingLabel.HelpText= +87.XhtmlOptionsCalc.ScalingLabel.Label=Scaling +88.XhtmlOptionsCalc.Scaling.HelpText= +89.XhtmlOptionsCalc.ScalingPercentLabel.HelpText= +90.XhtmlOptionsCalc.ScalingPercentLabel.Label=% +91.XhtmlOptionsCalc.ColumnScalingLabel.HelpText= +92.XhtmlOptionsCalc.ColumnScalingLabel.Label=Column scaling +93.XhtmlOptionsCalc.ColumnScaling.HelpText= +94.XhtmlOptionsCalc.ColumnScalingPercentLabel.HelpText= +95.XhtmlOptionsCalc.ColumnScalingPercentLabel.Label=% +96.XhtmlOptionsCalc.ConvertToPx.HelpText= +97.XhtmlOptionsCalc.ConvertToPx.Label=Convert units to px (pixels) +98.XhtmlOptionsCalc.OriginalImageSize.HelpText= +99.XhtmlOptionsCalc.OriginalImageSize.Label=Use original image size +100.XhtmlOptionsCalc.SpecialContentLabel.HelpText= +101.XhtmlOptionsCalc.SpecialContentLabel.Label=Special content +102.XhtmlOptionsCalc.Notes.HelpText= +103.XhtmlOptionsCalc.Notes.Label=Export notes +104.XhtmlOptionsCalc.UseDublinCore.HelpText= +105.XhtmlOptionsCalc.UseDublinCore.Label=Export document properties (Dublin Core Metadata) +106.XhtmlOptionsCalc.SheetsLabel.HelpText= +107.XhtmlOptionsCalc.SheetsLabel.Label=Sheets +108.XhtmlOptionsCalc.DisplayHiddenSheets.HelpText= +109.XhtmlOptionsCalc.DisplayHiddenSheets.Label=Display hidden sheets +110.XhtmlOptionsCalc.DisplayHiddenRowsCols.HelpText= +111.XhtmlOptionsCalc.DisplayHiddenRowsCols.Label=Display hidden rows and columns +112.XhtmlOptionsCalc.DisplayFilteredRowsCols.HelpText= +113.XhtmlOptionsCalc.DisplayFilteredRowsCols.Label=Display filtered rows and columns +114.XhtmlOptionsCalc.ApplyPrintRanges.HelpText= +115.XhtmlOptionsCalc.ApplyPrintRanges.Label=Apply print ranges +116.XhtmlOptionsCalc.UseTitleAsHeading.HelpText= +117.XhtmlOptionsCalc.UseTitleAsHeading.Label=Use title as heading +118.XhtmlOptionsCalc.UseSheetNamesAsHeadings.HelpText= +119.XhtmlOptionsCalc.UseSheetNamesAsHeadings.Label=Use sheet names as headings +120.XhtmlOptionsCalc.FilesLabel.HelpText= +121.XhtmlOptionsCalc.FilesLabel.Label=Files +122.XhtmlOptionsCalc.CalcSplit.HelpText= +123.XhtmlOptionsCalc.CalcSplit.Label=Save sheets in separate files +124.XhtmlOptionsCalc.SaveImagesInSubdir.HelpText= +125.XhtmlOptionsCalc.SaveImagesInSubdir.Label=Save images in subdirectory +126.XhtmlOptionsCalc.ExportButton.HelpText= +127.XhtmlOptionsCalc.ExportButton.Label=Export +128.XhtmlOptionsCalc.CancelButton.HelpText= +129.XhtmlOptionsCalc.CancelButton.Label=Cancel +130.Config.StringItemList=Original formatting +131.Config.StringItemList=Chocolate +132.Config.StringItemList=Midnight +133.Config.StringItemList=Modernist +134.Config.StringItemList=Oldstyle +135.Config.StringItemList=Steely +136.Config.StringItemList=Swiss +137.Config.StringItemList=Traditional +138.Config.StringItemList=Ultramarine +139.Config.StringItemList=Custom +140.Config.StringItemList=Original formatting +141.Config.StringItemList=Custom diff --git a/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_en_US.default b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_en_US.default new file mode 100644 index 0000000..e69de29 diff --git a/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_en_US.properties b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_en_US.properties new file mode 100644 index 0000000..1b3b2d5 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_en_US.properties @@ -0,0 +1,131 @@ +# Strings for Dialog Library W2XDialogs +0.XhtmlOptions.HelpText= +1.XhtmlOptions.Title=XHTML Options (Writer2xhtml) +2.XhtmlOptions.StyleLabel.HelpText= +3.XhtmlOptions.StyleLabel.Label=Style +4.XhtmlOptions.ConfigLabel.HelpText= +5.XhtmlOptions.ConfigLabel.Label=Use style +6.XhtmlOptions.Config.HelpText= +17.XhtmlOptions.ScalingLabel.HelpText= +18.XhtmlOptions.ScalingLabel.Label=Scaling +19.XhtmlOptions.Scaling.HelpText= +20.XhtmlOptions.ScalingPercentLabel.HelpText= +21.XhtmlOptions.ScalingPercentLabel.Label=% +22.XhtmlOptions.ColumnScalingLabel.HelpText= +23.XhtmlOptions.ColumnScalingLabel.Label=Column scaling +24.XhtmlOptions.ColumnScaling.HelpText= +25.XhtmlOptions.ColumnScalingPercentLabel.HelpText= +26.XhtmlOptions.ColumnScalingPercentLabel.Label=% +27.XhtmlOptions.ConvertToPx.HelpText= +28.XhtmlOptions.ConvertToPx.Label=Convert units to px (pixels) +29.XhtmlOptions.OriginalImageSize.HelpText= +30.XhtmlOptions.OriginalImageSize.Label=Use original image size +31.XhtmlOptions.SpecialContentLabel.HelpText= +32.XhtmlOptions.SpecialContentLabel.Label=Special content +33.XhtmlOptions.Notes.HelpText= +34.XhtmlOptions.Notes.Label=Export notes +35.XhtmlOptions.UseDublinCore.HelpText= +36.XhtmlOptions.UseDublinCore.Label=Export document properties (Dublin Core Metadata) +37.XhtmlOptions.AutoCorrectLabel.HelpText= +38.XhtmlOptions.AutoCorrectLabel.Label=AutoCorrect +39.XhtmlOptions.IgnoreHardLineBreaks.HelpText= +40.XhtmlOptions.IgnoreHardLineBreaks.Label=Ignore hard line breaks +41.XhtmlOptions.IgnoreEmptyParagraphs.HelpText= +42.XhtmlOptions.IgnoreEmptyParagraphs.Label=Ignore empty paragraphs +43.XhtmlOptions.IgnoreDoubleSpaces.HelpText= +44.XhtmlOptions.IgnoreDoubleSpaces.Label=Ignore double spaces +45.XhtmlOptions.FilesLabel.HelpText= +46.XhtmlOptions.FilesLabel.Label=Files +47.XhtmlOptions.Split.HelpText= +48.XhtmlOptions.Split.Label=Split document at headings +49.XhtmlOptions.SplitLevelLabel.HelpText= +50.XhtmlOptions.SplitLevelLabel.Label=Heading level +51.XhtmlOptions.SplitLevel.HelpText= +52.SplitLevel.StringItemList=1 +53.SplitLevel.StringItemList=2 +54.SplitLevel.StringItemList=3 +55.SplitLevel.StringItemList=4 +56.SplitLevel.StringItemList=5 +57.SplitLevel.StringItemList=6 +58.XhtmlOptions.RepeatLevelsLabel.HelpText= +59.XhtmlOptions.RepeatLevelsLabel.Label=Repeat heading levels +60.XhtmlOptions.RepeatLevels.HelpText= +61.RepeatLevels.StringItemList=0 +62.RepeatLevels.StringItemList=1 +63.RepeatLevels.StringItemList=2 +64.RepeatLevels.StringItemList=3 +65.RepeatLevels.StringItemList=4 +66.RepeatLevels.StringItemList=5 +67.XhtmlOptions.SaveImagesInSubdir.HelpText= +68.XhtmlOptions.SaveImagesInSubdir.Label=Save images in subdirectory +69.XhtmlOptions.XsltPathLabel.HelpText= +70.XhtmlOptions.XsltPathLabel.Label=XSLT path +71.XhtmlOptions.XsltPath.HelpText= +72.XhtmlOptions.XsltPath.Text= +73.XhtmlOptions.ExportButton.HelpText= +74.XhtmlOptions.ExportButton.Label=Export +75.XhtmlOptions.CancelButton.HelpText= +76.XhtmlOptions.CancelButton.Label=Cancel +77.XhtmlOptionsCalc.HelpText= +78.XhtmlOptionsCalc.Title=XHTML Options (Calc2xhtml) +79.XhtmlOptionsCalc.StyleLabel.HelpText= +80.XhtmlOptionsCalc.StyleLabel.Label=Style +81.XhtmlOptionsCalc.ConfigLabel.HelpText= +82.XhtmlOptionsCalc.ConfigLabel.Label=Use style +83.XhtmlOptionsCalc.Config.HelpText= +86.XhtmlOptionsCalc.ScalingLabel.HelpText= +87.XhtmlOptionsCalc.ScalingLabel.Label=Scaling +88.XhtmlOptionsCalc.Scaling.HelpText= +89.XhtmlOptionsCalc.ScalingPercentLabel.HelpText= +90.XhtmlOptionsCalc.ScalingPercentLabel.Label=% +91.XhtmlOptionsCalc.ColumnScalingLabel.HelpText= +92.XhtmlOptionsCalc.ColumnScalingLabel.Label=Column scaling +93.XhtmlOptionsCalc.ColumnScaling.HelpText= +94.XhtmlOptionsCalc.ColumnScalingPercentLabel.HelpText= +95.XhtmlOptionsCalc.ColumnScalingPercentLabel.Label=% +96.XhtmlOptionsCalc.ConvertToPx.HelpText= +97.XhtmlOptionsCalc.ConvertToPx.Label=Convert units to px (pixels) +98.XhtmlOptionsCalc.OriginalImageSize.HelpText= +99.XhtmlOptionsCalc.OriginalImageSize.Label=Use original image size +100.XhtmlOptionsCalc.SpecialContentLabel.HelpText= +101.XhtmlOptionsCalc.SpecialContentLabel.Label=Special content +102.XhtmlOptionsCalc.Notes.HelpText= +103.XhtmlOptionsCalc.Notes.Label=Export notes +104.XhtmlOptionsCalc.UseDublinCore.HelpText= +105.XhtmlOptionsCalc.UseDublinCore.Label=Export document properties (Dublin Core Metadata) +106.XhtmlOptionsCalc.SheetsLabel.HelpText= +107.XhtmlOptionsCalc.SheetsLabel.Label=Sheets +108.XhtmlOptionsCalc.DisplayHiddenSheets.HelpText= +109.XhtmlOptionsCalc.DisplayHiddenSheets.Label=Display hidden sheets +110.XhtmlOptionsCalc.DisplayHiddenRowsCols.HelpText= +111.XhtmlOptionsCalc.DisplayHiddenRowsCols.Label=Display hidden rows and columns +112.XhtmlOptionsCalc.DisplayFilteredRowsCols.HelpText= +113.XhtmlOptionsCalc.DisplayFilteredRowsCols.Label=Display filtered rows and columns +114.XhtmlOptionsCalc.ApplyPrintRanges.HelpText= +115.XhtmlOptionsCalc.ApplyPrintRanges.Label=Apply print ranges +116.XhtmlOptionsCalc.UseTitleAsHeading.HelpText= +117.XhtmlOptionsCalc.UseTitleAsHeading.Label=Use title as heading +118.XhtmlOptionsCalc.UseSheetNamesAsHeadings.HelpText= +119.XhtmlOptionsCalc.UseSheetNamesAsHeadings.Label=Use sheet names as headings +120.XhtmlOptionsCalc.FilesLabel.HelpText= +121.XhtmlOptionsCalc.FilesLabel.Label=Files +122.XhtmlOptionsCalc.CalcSplit.HelpText= +123.XhtmlOptionsCalc.CalcSplit.Label=Save sheets in separate files +124.XhtmlOptionsCalc.SaveImagesInSubdir.HelpText= +125.XhtmlOptionsCalc.SaveImagesInSubdir.Label=Save images in subdirectory +126.XhtmlOptionsCalc.ExportButton.HelpText= +127.XhtmlOptionsCalc.ExportButton.Label=Export +128.XhtmlOptionsCalc.CancelButton.HelpText= +129.XhtmlOptionsCalc.CancelButton.Label=Cancel +130.Config.StringItemList=Original formatting +131.Config.StringItemList=Chocolate +132.Config.StringItemList=Midnight +133.Config.StringItemList=Modernist +134.Config.StringItemList=Oldstyle +135.Config.StringItemList=Steely +136.Config.StringItemList=Swiss +137.Config.StringItemList=Traditional +138.Config.StringItemList=Ultramarine +139.Config.StringItemList=Custom +140.Config.StringItemList=Original formatting +141.Config.StringItemList=Custom diff --git a/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_fr_FR.properties b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_fr_FR.properties new file mode 100644 index 0000000..befb2ac --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_fr_FR.properties @@ -0,0 +1,131 @@ +# Strings for Dialog Library W2XDialogs= +0.XhtmlOptions.HelpText= +1.XhtmlOptions.Title=Options XHTML (Writer2xhtml) +2.XhtmlOptions.StyleLabel.HelpText= +3.XhtmlOptions.StyleLabel.Label=Style +4.XhtmlOptions.ConfigLabel.HelpText= +5.XhtmlOptions.ConfigLabel.Label=Utiliser style +6.XhtmlOptions.Config.HelpText= +17.XhtmlOptions.ScalingLabel.HelpText= +18.XhtmlOptions.ScalingLabel.Label=Dimensions +19.XhtmlOptions.Scaling.HelpText= +20.XhtmlOptions.ScalingPercentLabel.HelpText= +21.XhtmlOptions.ScalingPercentLabel.Label=% +22.XhtmlOptions.ColumnScalingLabel.HelpText= +23.XhtmlOptions.ColumnScalingLabel.Label=Dimension des colonnes +24.XhtmlOptions.ColumnScaling.HelpText= +25.XhtmlOptions.ColumnScalingPercentLabel.HelpText= +26.XhtmlOptions.ColumnScalingPercentLabel.Label=% +27.XhtmlOptions.ConvertToPx.HelpText= +28.XhtmlOptions.ConvertToPx.Label=Convertir unit\u00e9s en pixels +29.XhtmlOptions.OriginalImageSize.HelpText= +30.XhtmlOptions.OriginalImageSize.Label=Utiliser la taille d'origine des images +31.XhtmlOptions.SpecialContentLabel.HelpText= +32.XhtmlOptions.SpecialContentLabel.Label=Contenu sp\u00e9cifique +33.XhtmlOptions.Notes.HelpText= +34.XhtmlOptions.Notes.Label=Exporter les notes +35.XhtmlOptions.UseDublinCore.HelpText= +36.XhtmlOptions.UseDublinCore.Label=Exporter les propri\u00e9t\u00e9s du document (Metadonn\u00e9es Dublin Core) +37.XhtmlOptions.AutoCorrectLabel.HelpText= +38.XhtmlOptions.AutoCorrectLabel.Label=Correction automatique +39.XhtmlOptions.IgnoreHardLineBreaks.HelpText= +40.XhtmlOptions.IgnoreHardLineBreaks.Label=Ignorer les sauts de ligne +41.XhtmlOptions.IgnoreEmptyParagraphs.HelpText= +42.XhtmlOptions.IgnoreEmptyParagraphs.Label=Ignorer les paragraphes vides +43.XhtmlOptions.IgnoreDoubleSpaces.HelpText= +44.XhtmlOptions.IgnoreDoubleSpaces.Label=Ignoerer les doubles espaces +45.XhtmlOptions.FilesLabel.HelpText= +46.XhtmlOptions.FilesLabel.Label=Fichiers +47.XhtmlOptions.Split.HelpText= +48.XhtmlOptions.Split.Label=S\u00e9parer le document au niveau des titres +49.XhtmlOptions.SplitLevelLabel.HelpText= +50.XhtmlOptions.SplitLevelLabel.Label=Niveau des titres +51.XhtmlOptions.SplitLevel.HelpText= +52.SplitLevel.StringItemList=1 +53.SplitLevel.StringItemList=2 +54.SplitLevel.StringItemList=3 +55.SplitLevel.StringItemList=4 +56.SplitLevel.StringItemList=5 +57.SplitLevel.StringItemList=6 +58.XhtmlOptions.RepeatLevelsLabel.HelpText= +59.XhtmlOptions.RepeatLevelsLabel.Label=R\u00e9p\u00e9ter le niveau des titres +60.XhtmlOptions.RepeatLevels.HelpText= +61.RepeatLevels.StringItemList=0 +62.RepeatLevels.StringItemList=1 +63.RepeatLevels.StringItemList=2 +64.RepeatLevels.StringItemList=3 +65.RepeatLevels.StringItemList=4 +66.RepeatLevels.StringItemList=5 +67.XhtmlOptions.SaveImagesInSubdir.HelpText= +68.XhtmlOptions.SaveImagesInSubdir.Label=Sauver les images dans un sous-r\u00e9pertoire +69.XhtmlOptions.XsltPathLabel.HelpText= +70.XhtmlOptions.XsltPathLabel.Label=Chemin XSLT +71.XhtmlOptions.XsltPath.HelpText= +72.XhtmlOptions.XsltPath.Text= +73.XhtmlOptions.ExportButton.HelpText= +74.XhtmlOptions.ExportButton.Label=Exporter +75.XhtmlOptions.CancelButton.HelpText= +76.XhtmlOptions.CancelButton.Label=Annuler +77.XhtmlOptionsCalc.HelpText= +78.XhtmlOptionsCalc.Title=Options XHTML(Calc2xhtml) +79.XhtmlOptionsCalc.StyleLabel.HelpText= +80.XhtmlOptionsCalc.StyleLabel.Label=Style +81.XhtmlOptionsCalc.ConfigLabel.HelpText= +82.XhtmlOptionsCalc.ConfigLabel.Label=Utiliser style +83.XhtmlOptionsCalc.Config.HelpText= +86.XhtmlOptionsCalc.ScalingLabel.HelpText= +87.XhtmlOptionsCalc.ScalingLabel.Label=Dimensions +88.XhtmlOptionsCalc.Scaling.HelpText= +89.XhtmlOptionsCalc.ScalingPercentLabel.HelpText= +90.XhtmlOptionsCalc.ScalingPercentLabel.Label=% +91.XhtmlOptionsCalc.ColumnScalingLabel.HelpText= +92.XhtmlOptionsCalc.ColumnScalingLabel.Label=Dimension des colonnes +93.XhtmlOptionsCalc.ColumnScaling.HelpText= +94.XhtmlOptionsCalc.ColumnScalingPercentLabel.HelpText= +95.XhtmlOptionsCalc.ColumnScalingPercentLabel.Label=% +96.XhtmlOptionsCalc.ConvertToPx.HelpText= +97.XhtmlOptionsCalc.ConvertToPx.Label=Convertir unit\u00e9s en pixels +98.XhtmlOptionsCalc.OriginalImageSize.HelpText= +99.XhtmlOptionsCalc.OriginalImageSize.Label=Utiliser la taille d'origine des images +100.XhtmlOptionsCalc.SpecialContentLabel.HelpText= +101.XhtmlOptionsCalc.SpecialContentLabel.Label=Contenu specifique +102.XhtmlOptionsCalc.Notes.HelpText= +103.XhtmlOptionsCalc.Notes.Label=Exporter les notes +104.XhtmlOptionsCalc.UseDublinCore.HelpText= +105.XhtmlOptionsCalc.UseDublinCore.Label=Exporter les propri\u00e9t\u00e9s du document (Metadonn\u00e9es Dublin Core) +106.XhtmlOptionsCalc.SheetsLabel.HelpText= +107.XhtmlOptionsCalc.SheetsLabel.Label=Feuilles +108.XhtmlOptionsCalc.DisplayHiddenSheets.HelpText= +109.XhtmlOptionsCalc.DisplayHiddenSheets.Label=Afficher les feuilles cach\u00e9es +110.XhtmlOptionsCalc.DisplayHiddenRowsCols.HelpText= +111.XhtmlOptionsCalc.DisplayHiddenRowsCols.Label=Afficher les lignes et colonnes cach\u00e9es +112.XhtmlOptionsCalc.DisplayFilteredRowsCols.HelpText= +113.XhtmlOptionsCalc.DisplayFilteredRowsCols.Label=Afficher les lignes et colonnes filtr\u00e9es +114.XhtmlOptionsCalc.ApplyPrintRanges.HelpText= +115.XhtmlOptionsCalc.ApplyPrintRanges.Label=Appliquer les zones d'impression +116.XhtmlOptionsCalc.UseTitleAsHeading.HelpText= +117.XhtmlOptionsCalc.UseTitleAsHeading.Label=Utiliser le titre comme en-t\u00eate +118.XhtmlOptionsCalc.UseSheetNamesAsHeadings.HelpText= +119.XhtmlOptionsCalc.UseSheetNamesAsHeadings.Label=Utiliser le nom des feuilles comme en-t\u00eate +120.XhtmlOptionsCalc.FilesLabel.HelpText= +121.XhtmlOptionsCalc.FilesLabel.Label=Fichiers +122.XhtmlOptionsCalc.CalcSplit.HelpText= +123.XhtmlOptionsCalc.CalcSplit.Label=Sauver les feuilles dans des fichiers s\u00e9par\u00e9s +124.XhtmlOptionsCalc.SaveImagesInSubdir.HelpText= +125.XhtmlOptionsCalc.SaveImagesInSubdir.Label=Sauver les images dans un sous-r\u00e9pertoire +126.XhtmlOptionsCalc.ExportButton.HelpText= +127.XhtmlOptionsCalc.ExportButton.Label=Exporter +128.XhtmlOptionsCalc.CancelButton.HelpText= +129.XhtmlOptionsCalc.CancelButton.Label=Annuler +130.Config.StringItemList=Format d'origine +131.Config.StringItemList=Chocolat +132.Config.StringItemList=Minuit +133.Config.StringItemList=Contemporain +134.Config.StringItemList=Vieux style +135.Config.StringItemList=M\u00e9tallis\u00e9 +136.Config.StringItemList=Suisse +137.Config.StringItemList=Traditionnel +138.Config.StringItemList=Ultramarine +139.Config.StringItemList=Personnalis\u00e9 +140.Config.StringItemList=Format d'origine +141.Config.StringItemList=Personnalis\u00e9 diff --git a/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_ru_RU.properties b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_ru_RU.properties new file mode 100644 index 0000000..776e6a6 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_ru_RU.properties @@ -0,0 +1,131 @@ +# Strings for Dialog Library W2XDialogs=(Russian) +0.XhtmlOptions.HelpText= +1.XhtmlOptions.Title=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b XHTML (Writer2xhtml) +2.XhtmlOptions.StyleLabel.HelpText= +3.XhtmlOptions.StyleLabel.Label=\u0421\u0442\u0438\u043b\u044c +4.XhtmlOptions.ConfigLabel.HelpText= +5.XhtmlOptions.ConfigLabel.Label=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0438\u043b\u044c +6.XhtmlOptions.Config.HelpText= +17.XhtmlOptions.ScalingLabel.HelpText= +18.XhtmlOptions.ScalingLabel.Label=\u041c\u0430\u0441\u0448\u0442\u0430\u0431 +19.XhtmlOptions.Scaling.HelpText= +20.XhtmlOptions.ScalingPercentLabel.HelpText= +21.XhtmlOptions.ScalingPercentLabel.Label=% +22.XhtmlOptions.ColumnScalingLabel.HelpText= +23.XhtmlOptions.ColumnScalingLabel.Label=\u0420\u0430\u0437\u043c\u0435\u0440 \u043a\u043e\u043b\u043e\u043d\u043e\u043a +24.XhtmlOptions.ColumnScaling.HelpText= +25.XhtmlOptions.ColumnScalingPercentLabel.HelpText= +26.XhtmlOptions.ColumnScalingPercentLabel.Label=% +27.XhtmlOptions.ConvertToPx.HelpText= +28.XhtmlOptions.ConvertToPx.Label=\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043f\u0438\u043a\u0441\u0435\u043b\u0438 (px) +29.XhtmlOptions.OriginalImageSize.HelpText= +30.XhtmlOptions.OriginalImageSize.Label=\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0440\u0438\u0441\u0443\u043d\u043a\u043e\u0432 +31.XhtmlOptions.SpecialContentLabel.HelpText= +32.XhtmlOptions.SpecialContentLabel.Label=\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 +33.XhtmlOptions.Notes.HelpText= +34.XhtmlOptions.Notes.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043c\u0435\u0442\u043a\u0438 +35.XhtmlOptions.UseDublinCore.HelpText= +36.XhtmlOptions.UseDublinCore.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442. \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 (Dublin Core Metadata) +37.XhtmlOptions.AutoCorrectLabel.HelpText= +38.XhtmlOptions.AutoCorrectLabel.Label=\u0410\u0432\u0442\u043e\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u043a\u0430 +39.XhtmlOptions.IgnoreHardLineBreaks.HelpText= +40.XhtmlOptions.IgnoreHardLineBreaks.Label=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u0440\u044b\u0432\u044b \u0441\u0442\u0440\u043e\u043a +41.XhtmlOptions.IgnoreEmptyParagraphs.HelpText= +42.XhtmlOptions.IgnoreEmptyParagraphs.Label=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u0435 \u043f\u0430\u0440\u0430\u0433\u0440\u0430\u0444\u044b +43.XhtmlOptions.IgnoreDoubleSpaces.HelpText= +44.XhtmlOptions.IgnoreDoubleSpaces.Label=\u0418\u0433\u043d\u043e\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0432\u043e\u0439\u043d\u044b\u0435 \u043f\u0440\u043e\u0431\u0435\u043b\u044b +45.XhtmlOptions.FilesLabel.HelpText= +46.XhtmlOptions.FilesLabel.Label=\u0424\u0430\u0439\u043b\u044b +47.XhtmlOptions.Split.HelpText= +48.XhtmlOptions.Split.Label=\u0420\u0430\u0437\u0431\u0438\u0432\u0430\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u0437\u0430\u043a\u043e\u043b\u043e\u0432\u043a\u0430\u043c +49.XhtmlOptions.SplitLevelLabel.HelpText= +50.XhtmlOptions.SplitLevelLabel.Label=\u0423\u0440\u043e\u0432\u0435\u043d\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432 +51.XhtmlOptions.SplitLevel.HelpText= +52.SplitLevel.StringItemList=1 +53.SplitLevel.StringItemList=2 +54.SplitLevel.StringItemList=3 +55.SplitLevel.StringItemList=4 +56.SplitLevel.StringItemList=5 +57.SplitLevel.StringItemList=6 +58.XhtmlOptions.RepeatLevelsLabel.HelpText= +59.XhtmlOptions.RepeatLevelsLabel.Label=\u041f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 +60.XhtmlOptions.RepeatLevels.HelpText= +61.RepeatLevels.StringItemList=0 +62.RepeatLevels.StringItemList=1 +63.RepeatLevels.StringItemList=2 +64.RepeatLevels.StringItemList=3 +65.RepeatLevels.StringItemList=4 +66.RepeatLevels.StringItemList=5 +67.XhtmlOptions.SaveImagesInSubdir.HelpText= +68.XhtmlOptions.SaveImagesInSubdir.Label=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u043f\u043e\u0434\u043a\u0430\u0442\u0430\u043b\u043e\u0433 +69.XhtmlOptions.XsltPathLabel.HelpText= +70.XhtmlOptions.XsltPathLabel.Label=\u041f\u0443\u0442\u044c \u043a XSLT +71.XhtmlOptions.XsltPath.HelpText= +72.XhtmlOptions.XsltPath.Text= +73.XhtmlOptions.ExportButton.HelpText= +74.XhtmlOptions.ExportButton.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c +75.XhtmlOptions.CancelButton.HelpText= +76.XhtmlOptions.CancelButton.Label=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c +77.XhtmlOptionsCalc.HelpText= +78.XhtmlOptionsCalc.Title=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b XHTML (Calc2xhtml) +79.XhtmlOptionsCalc.StyleLabel.HelpText= +80.XhtmlOptionsCalc.StyleLabel.Label=\u0421\u0442\u0438\u043b\u044c +81.XhtmlOptionsCalc.ConfigLabel.HelpText= +82.XhtmlOptionsCalc.ConfigLabel.Label=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u0438\u043b\u044c +83.XhtmlOptionsCalc.Config.HelpText= +86.XhtmlOptionsCalc.ScalingLabel.HelpText= +87.XhtmlOptionsCalc.ScalingLabel.Label=\u041c\u0430\u0441\u0448\u0442\u0430\u0431 +88.XhtmlOptionsCalc.Scaling.HelpText= +89.XhtmlOptionsCalc.ScalingPercentLabel.HelpText= +90.XhtmlOptionsCalc.ScalingPercentLabel.Label=% +91.XhtmlOptionsCalc.ColumnScalingLabel.HelpText= +92.XhtmlOptionsCalc.ColumnScalingLabel.Label=\u0420\u0430\u0437\u043c\u0435\u0440 \u043a\u043e\u043b\u043e\u043d\u043e\u043a +93.XhtmlOptionsCalc.ColumnScaling.HelpText= +94.XhtmlOptionsCalc.ColumnScalingPercentLabel.HelpText= +95.XhtmlOptionsCalc.ColumnScalingPercentLabel.Label=% +96.XhtmlOptionsCalc.ConvertToPx.HelpText= +97.XhtmlOptionsCalc.ConvertToPx.Label=\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043f\u0438\u043a\u0441\u0435\u043b\u0438 (px) +98.XhtmlOptionsCalc.OriginalImageSize.HelpText= +99.XhtmlOptionsCalc.OriginalImageSize.Label=\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0440\u0438\u0441\u0443\u043d\u043a\u043e\u0432 +100.XhtmlOptionsCalc.SpecialContentLabel.HelpText= +101.XhtmlOptionsCalc.SpecialContentLabel.Label=\u0421\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 +102.XhtmlOptionsCalc.Notes.HelpText= +103.XhtmlOptionsCalc.Notes.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0437\u0430\u043c\u0435\u0442\u043a\u0438 +104.XhtmlOptionsCalc.UseDublinCore.HelpText= +105.XhtmlOptionsCalc.UseDublinCore.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430 (Dublin Core Metadata) +106.XhtmlOptionsCalc.SheetsLabel.HelpText= +107.XhtmlOptionsCalc.SheetsLabel.Label=\u041b\u0438\u0441\u0442\u044b +108.XhtmlOptionsCalc.DisplayHiddenSheets.HelpText= +109.XhtmlOptionsCalc.DisplayHiddenSheets.Label=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u043f\u0440\u044f\u0442\u0430\u043d\u044b\u0435 \u043b\u0438\u0441\u0442\u044b +110.XhtmlOptionsCalc.DisplayHiddenRowsCols.HelpText= +111.XhtmlOptionsCalc.DisplayHiddenRowsCols.Label=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u0441\u043f\u0440\u044f\u0442\u0430\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 +112.XhtmlOptionsCalc.DisplayFilteredRowsCols.HelpText= +113.XhtmlOptionsCalc.DisplayFilteredRowsCols.Label=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u044c \u043e\u0442\u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u0442\u0440\u043e\u043a\u0438 \u0438 \u043a\u043e\u043b\u043e\u043d\u043a\u0438 +114.XhtmlOptionsCalc.ApplyPrintRanges.HelpText= +115.XhtmlOptionsCalc.ApplyPrintRanges.Label=\u041f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u043f\u0435\u0447\u0430\u0442\u0438 +116.XhtmlOptionsCalc.UseTitleAsHeading.HelpText= +117.XhtmlOptionsCalc.UseTitleAsHeading.Label=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a +118.XhtmlOptionsCalc.UseSheetNamesAsHeadings.HelpText= +119.XhtmlOptionsCalc.UseSheetNamesAsHeadings.Label=\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f \u043b\u0438\u0441\u0442\u043e\u0432 \u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 +120.XhtmlOptionsCalc.FilesLabel.HelpText= +121.XhtmlOptionsCalc.FilesLabel.Label=\u0424\u0430\u0439\u043b\u044b +122.XhtmlOptionsCalc.CalcSplit.HelpText= +123.XhtmlOptionsCalc.CalcSplit.Label=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043b\u0438\u0441\u0442\u044b \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b +124.XhtmlOptionsCalc.SaveImagesInSubdir.HelpText= +125.XhtmlOptionsCalc.SaveImagesInSubdir.Label=\u0421\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0432 \u043f\u043e\u0434\u043a\u0430\u0442\u0430\u043b\u043e\u0433 +126.XhtmlOptionsCalc.ExportButton.HelpText= +127.XhtmlOptionsCalc.ExportButton.Label=\u042d\u043a\u0441\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c +128.XhtmlOptionsCalc.CancelButton.HelpText= +129.XhtmlOptionsCalc.CancelButton.Label=\u041e\u0442\u043c\u0435\u043d\u0438\u0442\u044c +130.Config.StringItemList=\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 +131.Config.StringItemList=\u0428\u043e\u043a\u043e\u043b\u0430\u0434 +132.Config.StringItemList=\u041f\u043e\u043b\u0443\u043d\u043e\u0447\u044c +133.Config.StringItemList=\u041c\u043e\u0434\u0435\u0440\u043d +134.Config.StringItemList=\u0420\u0435\u0442\u0440\u043e +135.Config.StringItemList=\u0421\u0442\u0430\u043b\u044c +136.Config.StringItemList=\u0428\u0432\u0435\u0446\u0438\u044f +137.Config.StringItemList=\u0422\u0440\u0430\u0434\u0438\u0446\u0438\u043e\u043d\u043d\u044b\u0439 +138.Config.StringItemList=\u0423\u043b\u044c\u0442\u0440\u0430\u043c\u0430\u0440\u0438\u043d +139.Config.StringItemList=\u0412\u044b\u0431\u043e\u0440\u043e\u0447\u043d\u044b\u0439 +140.Config.StringItemList=\u041e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 +141.Config.StringItemList=\u0412\u044b\u0431\u043e\u0440\u043e\u0447\u043d\u044b\u0439 diff --git a/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_uk_UA.properties b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_uk_UA.properties new file mode 100644 index 0000000..c23a9f1 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/DialogStrings_uk_UA.properties @@ -0,0 +1,131 @@ +# Strings for Dialog Library W2XDialogs (Ukrainian) +0.XhtmlOptions.HelpText= +1.XhtmlOptions.Title=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b XHTML (Writer2xhtml) +2.XhtmlOptions.StyleLabel.HelpText= +3.XhtmlOptions.StyleLabel.Label=\u0421\u0442\u0438\u043b\u044c +4.XhtmlOptions.ConfigLabel.HelpText= +5.XhtmlOptions.ConfigLabel.Label=\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u0441\u0442\u0438\u043b\u044c +6.XhtmlOptions.Config.HelpText= +17.XhtmlOptions.ScalingLabel.HelpText= +18.XhtmlOptions.ScalingLabel.Label=\u041c\u0456\u0440\u0438\u043b\u043e +19.XhtmlOptions.Scaling.HelpText= +20.XhtmlOptions.ScalingPercentLabel.HelpText= +21.XhtmlOptions.ScalingPercentLabel.Label=% +22.XhtmlOptions.ColumnScalingLabel.HelpText= +23.XhtmlOptions.ColumnScalingLabel.Label=\u0420\u043e\u0437\u043c\u0456\u0440 \u0441\u0442\u043e\u0432\u0431\u0446\u0456\u0432 +24.XhtmlOptions.ColumnScaling.HelpText= +25.XhtmlOptions.ColumnScalingPercentLabel.HelpText= +26.XhtmlOptions.ColumnScalingPercentLabel.Label=% +27.XhtmlOptions.ConvertToPx.HelpText= +28.XhtmlOptions.ConvertToPx.Label=\u041f\u0435\u0440\u0435\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0443 \u043f\u0456\u043a\u0441\u0435\u043b\u0456 (px) +29.XhtmlOptions.OriginalImageSize.HelpText= +30.XhtmlOptions.OriginalImageSize.Label=\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0447\u043d\u0438\u0439 \u0440\u043e\u0437\u043c\u0456\u0440 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u044c +31.XhtmlOptions.SpecialContentLabel.HelpText= +32.XhtmlOptions.SpecialContentLabel.Label=\u0421\u043f\u0435\u0446\u0456\u0430\u043b\u044c\u043d\u0438\u0439 \u0432\u043c\u0456\u0441\u0442 +33.XhtmlOptions.Notes.HelpText= +34.XhtmlOptions.Notes.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 \u0437\u0430\u043c\u0456\u0442\u043a\u0438 +35.XhtmlOptions.UseDublinCore.HelpText= +36.XhtmlOptions.UseDublinCore.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 \u0432\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443 (Dublin Core Metadata) +37.XhtmlOptions.AutoCorrectLabel.HelpText= +38.XhtmlOptions.AutoCorrectLabel.Label=\u0410\u0432\u0442\u043e\u043a\u043e\u0440\u0435\u0433\u0443\u0432\u0430\u043d\u043d\u044f +39.XhtmlOptions.IgnoreHardLineBreaks.HelpText= +40.XhtmlOptions.IgnoreHardLineBreaks.Label=\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u0440\u043e\u043e\u0437\u0440\u0438\u0432\u0438 \u0440\u044f\u0434\u043a\u0456\u0432 +41.XhtmlOptions.IgnoreEmptyParagraphs.HelpText= +42.XhtmlOptions.IgnoreEmptyParagraphs.Label=\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u043f\u043e\u0440\u043e\u0436\u043d\u0456 \u043f\u0430\u0440\u0430\u0433\u0440\u0430\u0444\u0438 +43.XhtmlOptions.IgnoreDoubleSpaces.HelpText= +44.XhtmlOptions.IgnoreDoubleSpaces.Label=\u0406\u0433\u043d\u043e\u0440\u0443\u0432\u0430\u0442\u0438 \u043f\u043e\u0434\u0432\u0456\u0439\u043d\u0456 \u043f\u0440\u043e\u0431\u0456\u043b\u0438 +45.XhtmlOptions.FilesLabel.HelpText= +46.XhtmlOptions.FilesLabel.Label=\u0424\u0430\u0439\u043b\u0438 +47.XhtmlOptions.Split.HelpText= +48.XhtmlOptions.Split.Label=\u0420\u043e\u0437\u0431\u0438\u0432\u0430\u0442\u0438 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u043f\u043e \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0430\u0445 +49.XhtmlOptions.SplitLevelLabel.HelpText= +50.XhtmlOptions.SplitLevelLabel.Label=\u0420\u0456\u0432\u043d\u0435\u044c \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0443 +51.XhtmlOptions.SplitLevel.HelpText= +52.SplitLevel.StringItemList=1 +53.SplitLevel.StringItemList=2 +54.SplitLevel.StringItemList=3 +55.SplitLevel.StringItemList=4 +56.SplitLevel.StringItemList=5 +57.SplitLevel.StringItemList=6 +58.XhtmlOptions.RepeatLevelsLabel.HelpText= +59.XhtmlOptions.RepeatLevelsLabel.Label=\u041f\u043e\u0432\u0442\u043e\u0440\u044e\u0432\u0430\u0442\u0438 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u043d\u0430 \u0440\u0456\u0432\u043d\u0456 +60.XhtmlOptions.RepeatLevels.HelpText= +61.RepeatLevels.StringItemList=0 +62.RepeatLevels.StringItemList=1 +63.RepeatLevels.StringItemList=2 +64.RepeatLevels.StringItemList=3 +65.RepeatLevels.StringItemList=4 +66.RepeatLevels.StringItemList=5 +67.XhtmlOptions.SaveImagesInSubdir.HelpText= +68.XhtmlOptions.SaveImagesInSubdir.Label=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f \u0443 \u043f\u0456\u0434\u0442\u0435\u043a\u0443 +69.XhtmlOptions.XsltPathLabel.HelpText= +70.XhtmlOptions.XsltPathLabel.Label=\u0428\u043b\u044f\u0445 \u0434\u043e XSLT +71.XhtmlOptions.XsltPath.HelpText= +72.XhtmlOptions.XsltPath.Text= +73.XhtmlOptions.ExportButton.HelpText= +74.XhtmlOptions.ExportButton.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 +75.XhtmlOptions.CancelButton.HelpText= +76.XhtmlOptions.CancelButton.Label=\u0412\u0456\u0434\u043c\u0456\u043d\u0438\u0442\u0438 +77.XhtmlOptionsCalc.HelpText= +78.XhtmlOptionsCalc.Title=\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b XHTML (Calc2xhtml) +79.XhtmlOptionsCalc.StyleLabel.HelpText= +80.XhtmlOptionsCalc.StyleLabel.Label=\u0421\u0442\u0438\u043b\u044c +81.XhtmlOptionsCalc.ConfigLabel.HelpText= +82.XhtmlOptionsCalc.ConfigLabel.Label=\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u0441\u0442\u0438\u043b\u044c +83.XhtmlOptionsCalc.Config.HelpText= +86.XhtmlOptionsCalc.ScalingLabel.HelpText= +87.XhtmlOptionsCalc.ScalingLabel.Label=\u041c\u0456\u0440\u0438\u043b\u043e +88.XhtmlOptionsCalc.Scaling.HelpText= +89.XhtmlOptionsCalc.ScalingPercentLabel.HelpText= +90.XhtmlOptionsCalc.ScalingPercentLabel.Label=% +91.XhtmlOptionsCalc.ColumnScalingLabel.HelpText= +92.XhtmlOptionsCalc.ColumnScalingLabel.Label=\u0420\u043e\u0437\u043c\u0456\u0440 \u0441\u0442\u043e\u0432\u0431\u0446\u0456\u0432 +93.XhtmlOptionsCalc.ColumnScaling.HelpText= +94.XhtmlOptionsCalc.ColumnScalingPercentLabel.HelpText= +95.XhtmlOptionsCalc.ColumnScalingPercentLabel.Label=% +96.XhtmlOptionsCalc.ConvertToPx.HelpText= +97.XhtmlOptionsCalc.ConvertToPx.Label=\u041f\u0435\u0440\u0435\u0442\u0432\u043e\u0440\u0438\u0442\u0438 \u0443 \u043f\u0456\u043a\u0441\u0435\u043b\u0456 (px) +98.XhtmlOptionsCalc.OriginalImageSize.HelpText= +99.XhtmlOptionsCalc.OriginalImageSize.Label=\u041e\u0440\u0438\u0433\u0456\u043d\u0430\u043b\u044c\u043d\u0438\u0439 \u0440\u043e\u0437\u043c\u0456\u0440 \u043c\u0430\u043b\u044e\u043d\u043a\u0456\u0432 +100.XhtmlOptionsCalc.SpecialContentLabel.HelpText= +101.XhtmlOptionsCalc.SpecialContentLabel.Label=\u0421\u043f\u0435\u0446\u0456\u0430\u043b\u044c\u043d\u0438\u0439 \u0432\u043c\u0456\u0441\u0442 +102.XhtmlOptionsCalc.Notes.HelpText= +103.XhtmlOptionsCalc.Notes.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 \u0437\u0430\u043c\u0456\u0442\u043a\u0438 +104.XhtmlOptionsCalc.UseDublinCore.HelpText= +105.XhtmlOptionsCalc.UseDublinCore.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 \u0432\u043b\u0430\u0441\u0442\u0438\u0432\u043e\u0441\u0442\u0456 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0443 (Dublin Core Metadata) +106.XhtmlOptionsCalc.SheetsLabel.HelpText= +107.XhtmlOptionsCalc.SheetsLabel.Label=\u0410\u0440\u043a\u0443\u0448\u0456 +108.XhtmlOptionsCalc.DisplayHiddenSheets.HelpText= +109.XhtmlOptionsCalc.DisplayHiddenSheets.Label=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u0437\u0430\u0445\u043e\u0432\u0430\u043d\u0456 \u0430\u0440\u043a\u0443\u0448\u0456 +110.XhtmlOptionsCalc.DisplayHiddenRowsCols.HelpText= +111.XhtmlOptionsCalc.DisplayHiddenRowsCols.Label=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u0437\u0430\u0445\u043e\u0432\u0430\u043d\u0456 \u0440\u044f\u0434\u043a\u0438 \u0442\u0430 \u0441\u0442\u043e\u0432\u0431\u0446\u0456 +112.XhtmlOptionsCalc.DisplayFilteredRowsCols.HelpText= +113.XhtmlOptionsCalc.DisplayFilteredRowsCols.Label=\u041f\u043e\u043a\u0430\u0437\u0430\u0442\u0438 \u0432\u0456\u0434\u0444\u0456\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u043d\u0456 \u0440\u044f\u0434\u043a\u0438 \u0442\u0430 \u0441\u0442\u043e\u0432\u0431\u0446\u0456 +114.XhtmlOptionsCalc.ApplyPrintRanges.HelpText= +115.XhtmlOptionsCalc.ApplyPrintRanges.Label=\u0417\u0430\u0441\u0442\u043e\u0441\u0443\u0432\u0430\u0442\u0438 \u043e\u0431\u043c\u0435\u0436\u0435\u043d\u043d\u044f \u043e\u0431\u043b\u0430\u0441\u0442\u0456 \u0434\u0440\u0443\u043a\u0443 +116.XhtmlOptionsCalc.UseTitleAsHeading.HelpText= +117.XhtmlOptionsCalc.UseTitleAsHeading.Label=\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u043d\u0430\u0437\u0432\u0443 \u0442\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a +118.XhtmlOptionsCalc.UseSheetNamesAsHeadings.HelpText= +119.XhtmlOptionsCalc.UseSheetNamesAsHeadings.Label=\u0412\u0438\u043a\u043e\u0440\u0438\u0441\u0442\u043e\u0432\u0443\u0432\u0430\u0442\u0438 \u043d\u0430\u0437\u0432\u0443 \u0430\u0440\u043a\u0443\u0448\u0456\u0432 \u0442\u0430 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 +120.XhtmlOptionsCalc.FilesLabel.HelpText= +121.XhtmlOptionsCalc.FilesLabel.Label=\u0424\u0430\u0439\u043b\u0438 +122.XhtmlOptionsCalc.CalcSplit.HelpText= +123.XhtmlOptionsCalc.CalcSplit.Label=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0430\u0440\u043a\u0443\u0448\u0456 \u0432 \u043e\u043a\u0440\u0435\u043c\u0456 \u0444\u0430\u0439\u043b\u0438 +124.XhtmlOptionsCalc.SaveImagesInSubdir.HelpText= +125.XhtmlOptionsCalc.SaveImagesInSubdir.Label=\u0417\u0431\u0435\u0440\u0435\u0433\u0442\u0438 \u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043d\u044f \u0443 \u043f\u0456\u0434\u0442\u0435\u043a\u0443 +126.XhtmlOptionsCalc.ExportButton.HelpText= +127.XhtmlOptionsCalc.ExportButton.Label=\u0415\u043a\u0441\u043f\u043e\u0440\u0442\u0443\u0432\u0430\u0442\u0438 +128.XhtmlOptionsCalc.CancelButton.HelpText= +129.XhtmlOptionsCalc.CancelButton.Label=\u0412\u0456\u0434\u043c\u0456\u043d\u0438\u0442\u0438 +130.Config.StringItemList=\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0447\u043d\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0443\u0432\u0430\u043d\u043d\u044f +131.Config.StringItemList=\u0428\u043e\u043a\u043e\u043b\u0430\u0434 +132.Config.StringItemList=\u041f\u0456\u0432\u043d\u0456\u0447 +133.Config.StringItemList=\u041c\u043e\u0434\u0435\u0440\u043d +134.Config.StringItemList=\u0420\u0435\u0442\u0440\u043e +135.Config.StringItemList=\u0421\u0442\u0430\u043b\u044c +136.Config.StringItemList=\u0428\u0432\u0435\u0446\u0456\u044f +137.Config.StringItemList=\u0422\u0440\u0430\u0434\u0438\u0446\u0456\u0439\u043d\u0438\u0439 +138.Config.StringItemList=\u0423\u043b\u044c\u0442\u0440\u0430\u043c\u0430\u0440\u0438\u043d +139.Config.StringItemList=\u0412\u0438\u0431\u0456\u0440\u043a\u043e\u0432\u0438\u0439 +140.Config.StringItemList=\u0410\u0432\u0442\u0435\u043d\u0442\u0438\u0447\u043d\u0435 \u0444\u043e\u0440\u043c\u0430\u0442\u0443\u0432\u0430\u043d\u043d\u044f +141.Config.StringItemList=\u0412\u0438\u0431\u0456\u0440\u043a\u043e\u0432\u0438\u0439 diff --git a/source/oxt/writer2xhtml/W2XDialogs/Module1.xba b/source/oxt/writer2xhtml/W2XDialogs/Module1.xba new file mode 100644 index 0000000..d7e2398 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/Module1.xba @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd"> +<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic">REM ***** BASIC ***** + +Sub Main + +End Sub</script:module> \ No newline at end of file diff --git a/source/oxt/writer2xhtml/W2XDialogs/XhtmlOptions.xdl b/source/oxt/writer2xhtml/W2XDialogs/XhtmlOptions.xdl new file mode 100644 index 0000000..5e6172a --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/XhtmlOptions.xdl @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd"> +<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="XhtmlOptions" dlg:left="139" dlg:top="84" dlg:width="203" dlg:height="306" dlg:help-text="&0.XhtmlOptions.HelpText" dlg:closeable="true" dlg:moveable="true" dlg:title="&1.XhtmlOptions.Title"> + <dlg:bulletinboard> + <dlg:text dlg:id="StyleLabel" dlg:tab-index="0" dlg:left="5" dlg:top="8" dlg:width="183" dlg:height="12" dlg:help-text="&2.XhtmlOptions.StyleLabel.HelpText" dlg:value="&3.XhtmlOptions.StyleLabel.Label"/> + <dlg:text dlg:id="ConfigLabel" dlg:tab-index="1" dlg:left="10" dlg:top="22" dlg:width="76" dlg:height="12" dlg:help-text="&4.XhtmlOptions.ConfigLabel.HelpText" dlg:value="&5.XhtmlOptions.ConfigLabel.Label"/> + <dlg:menulist dlg:id="Config" dlg:tab-index="2" dlg:left="97" dlg:top="20" dlg:width="92" dlg:height="12" dlg:help-text="&6.XhtmlOptions.Config.HelpText" dlg:spin="true" dlg:linecount="10"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&130.Config.StringItemList"/> + <dlg:menuitem dlg:value="&131.Config.StringItemList"/> + <dlg:menuitem dlg:value="&132.Config.StringItemList"/> + <dlg:menuitem dlg:value="&133.Config.StringItemList"/> + <dlg:menuitem dlg:value="&134.Config.StringItemList"/> + <dlg:menuitem dlg:value="&135.Config.StringItemList"/> + <dlg:menuitem dlg:value="&136.Config.StringItemList"/> + <dlg:menuitem dlg:value="&137.Config.StringItemList"/> + <dlg:menuitem dlg:value="&138.Config.StringItemList"/> + <dlg:menuitem dlg:value="&139.Config.StringItemList"/> + </dlg:menupopup> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:ConfigChange" script:language="UNO"/> + </dlg:menulist> + <dlg:text dlg:id="ScalingLabel" dlg:tab-index="3" dlg:left="10" dlg:top="36" dlg:width="76" dlg:height="12" dlg:help-text="&17.XhtmlOptions.ScalingLabel.HelpText" dlg:value="&18.XhtmlOptions.ScalingLabel.Label"/> + <dlg:numericfield dlg:id="Scaling" dlg:tab-index="4" dlg:left="144" dlg:top="34" dlg:width="30" dlg:height="12" dlg:help-text="&19.XhtmlOptions.Scaling.HelpText" dlg:strict-format="true" dlg:decimal-accuracy="0" dlg:value="100" dlg:value-min="1" dlg:value-max="1000" dlg:value-step="10" dlg:spin="true"/> + <dlg:text dlg:id="ScalingPercentLabel" dlg:tab-index="5" dlg:left="179" dlg:top="36" dlg:width="10" dlg:height="12" dlg:help-text="&20.XhtmlOptions.ScalingPercentLabel.HelpText" dlg:value="&21.XhtmlOptions.ScalingPercentLabel.Label"/> + <dlg:text dlg:id="ColumnScalingLabel" dlg:tab-index="6" dlg:left="10" dlg:top="50" dlg:width="76" dlg:height="12" dlg:help-text="&22.XhtmlOptions.ColumnScalingLabel.HelpText" dlg:value="&23.XhtmlOptions.ColumnScalingLabel.Label"/> + <dlg:numericfield dlg:id="ColumnScaling" dlg:tab-index="7" dlg:left="144" dlg:top="48" dlg:width="30" dlg:height="12" dlg:help-text="&24.XhtmlOptions.ColumnScaling.HelpText" dlg:strict-format="true" dlg:decimal-accuracy="0" dlg:value="100" dlg:value-min="1" dlg:value-max="1000" dlg:value-step="10" dlg:spin="true"/> + <dlg:text dlg:id="ColumnScalingPercentLabel" dlg:tab-index="8" dlg:left="179" dlg:top="50" dlg:width="10" dlg:height="12" dlg:help-text="&25.XhtmlOptions.ColumnScalingPercentLabel.HelpText" dlg:value="&26.XhtmlOptions.ColumnScalingPercentLabel.Label"/> + <dlg:checkbox dlg:id="ConvertToPx" dlg:tab-index="9" dlg:left="10" dlg:top="64" dlg:width="177" dlg:height="12" dlg:help-text="&27.XhtmlOptions.ConvertToPx.HelpText" dlg:value="&28.XhtmlOptions.ConvertToPx.Label" dlg:checked="true"/> + <dlg:checkbox dlg:id="OriginalImageSize" dlg:tab-index="10" dlg:left="10" dlg:top="78" dlg:width="177" dlg:height="12" dlg:help-text="&29.XhtmlOptions.OriginalImageSize.HelpText" dlg:value="&30.XhtmlOptions.OriginalImageSize.Label" dlg:checked="false"/> + <dlg:text dlg:id="SpecialContentLabel" dlg:tab-index="11" dlg:left="5" dlg:top="92" dlg:width="184" dlg:height="12" dlg:help-text="&31.XhtmlOptions.SpecialContentLabel.HelpText" dlg:value="&32.XhtmlOptions.SpecialContentLabel.Label"/> + <dlg:checkbox dlg:id="Notes" dlg:tab-index="12" dlg:left="10" dlg:top="106" dlg:width="179" dlg:height="12" dlg:help-text="&33.XhtmlOptions.Notes.HelpText" dlg:value="&34.XhtmlOptions.Notes.Label" dlg:checked="true"/> + <dlg:checkbox dlg:id="UseDublinCore" dlg:tab-index="13" dlg:left="10" dlg:top="120" dlg:width="179" dlg:height="12" dlg:help-text="&35.XhtmlOptions.UseDublinCore.HelpText" dlg:value="&36.XhtmlOptions.UseDublinCore.Label" dlg:checked="true"/> + <dlg:text dlg:id="AutoCorrectLabel" dlg:tab-index="14" dlg:left="5" dlg:top="134" dlg:width="184" dlg:height="12" dlg:help-text="&37.XhtmlOptions.AutoCorrectLabel.HelpText" dlg:value="&38.XhtmlOptions.AutoCorrectLabel.Label"/> + <dlg:checkbox dlg:id="IgnoreHardLineBreaks" dlg:tab-index="15" dlg:left="10" dlg:top="148" dlg:width="175" dlg:height="12" dlg:help-text="&39.XhtmlOptions.IgnoreHardLineBreaks.HelpText" dlg:value="&40.XhtmlOptions.IgnoreHardLineBreaks.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="IgnoreEmptyParagraphs" dlg:tab-index="16" dlg:left="10" dlg:top="162" dlg:width="175" dlg:height="12" dlg:help-text="&41.XhtmlOptions.IgnoreEmptyParagraphs.HelpText" dlg:value="&42.XhtmlOptions.IgnoreEmptyParagraphs.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="IgnoreDoubleSpaces" dlg:tab-index="17" dlg:left="10" dlg:top="176" dlg:width="175" dlg:height="12" dlg:help-text="&43.XhtmlOptions.IgnoreDoubleSpaces.HelpText" dlg:value="&44.XhtmlOptions.IgnoreDoubleSpaces.Label" dlg:checked="false"/> + <dlg:text dlg:id="FilesLabel" dlg:tab-index="18" dlg:left="5" dlg:top="190" dlg:width="184" dlg:height="12" dlg:help-text="&45.XhtmlOptions.FilesLabel.HelpText" dlg:value="&46.XhtmlOptions.FilesLabel.Label"/> + <dlg:checkbox dlg:id="Split" dlg:tab-index="19" dlg:left="10" dlg:top="204" dlg:width="180" dlg:height="12" dlg:help-text="&47.XhtmlOptions.Split.HelpText" dlg:value="&48.XhtmlOptions.Split.Label" dlg:checked="false"> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:SplitChange" script:language="UNO"/> + </dlg:checkbox> + <dlg:text dlg:id="SplitLevelLabel" dlg:tab-index="20" dlg:left="20" dlg:top="218" dlg:width="76" dlg:height="12" dlg:help-text="&49.XhtmlOptions.SplitLevelLabel.HelpText" dlg:value="&50.XhtmlOptions.SplitLevelLabel.Label"/> + <dlg:menulist dlg:id="SplitLevel" dlg:tab-index="21" dlg:left="152" dlg:top="216" dlg:width="37" dlg:height="12" dlg:help-text="&51.XhtmlOptions.SplitLevel.HelpText" dlg:spin="true" dlg:linecount="6"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&52.SplitLevel.StringItemList"/> + <dlg:menuitem dlg:value="&53.SplitLevel.StringItemList"/> + <dlg:menuitem dlg:value="&54.SplitLevel.StringItemList"/> + <dlg:menuitem dlg:value="&55.SplitLevel.StringItemList"/> + <dlg:menuitem dlg:value="&56.SplitLevel.StringItemList"/> + <dlg:menuitem dlg:value="&57.SplitLevel.StringItemList"/> + </dlg:menupopup> + </dlg:menulist> + <dlg:text dlg:id="RepeatLevelsLabel" dlg:tab-index="22" dlg:left="20" dlg:top="232" dlg:width="122" dlg:height="12" dlg:help-text="&58.XhtmlOptions.RepeatLevelsLabel.HelpText" dlg:value="&59.XhtmlOptions.RepeatLevelsLabel.Label"/> + <dlg:menulist dlg:id="RepeatLevels" dlg:tab-index="23" dlg:left="152" dlg:top="230" dlg:width="37" dlg:height="12" dlg:help-text="&60.XhtmlOptions.RepeatLevels.HelpText" dlg:spin="true" dlg:linecount="6"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&61.RepeatLevels.StringItemList"/> + <dlg:menuitem dlg:value="&62.RepeatLevels.StringItemList"/> + <dlg:menuitem dlg:value="&63.RepeatLevels.StringItemList"/> + <dlg:menuitem dlg:value="&64.RepeatLevels.StringItemList"/> + <dlg:menuitem dlg:value="&65.RepeatLevels.StringItemList"/> + <dlg:menuitem dlg:value="&66.RepeatLevels.StringItemList"/> + </dlg:menupopup> + </dlg:menulist> + <dlg:checkbox dlg:id="SaveImagesInSubdir" dlg:tab-index="24" dlg:left="10" dlg:top="246" dlg:width="177" dlg:height="12" dlg:help-text="&67.XhtmlOptions.SaveImagesInSubdir.HelpText" dlg:value="&68.XhtmlOptions.SaveImagesInSubdir.Label" dlg:checked="false"/> + <dlg:text dlg:id="XsltPathLabel" dlg:tab-index="25" dlg:left="10" dlg:top="260" dlg:width="36" dlg:height="12" dlg:help-text="&69.XhtmlOptions.XsltPathLabel.HelpText" dlg:value="&70.XhtmlOptions.XsltPathLabel.Label"/> + <dlg:textfield dlg:id="XsltPath" dlg:tab-index="26" dlg:left="57" dlg:top="258" dlg:width="132" dlg:height="12" dlg:help-text="&71.XhtmlOptions.XsltPath.HelpText" dlg:value="&72.XhtmlOptions.XsltPath.Text"/> + <dlg:button dlg:id="ExportButton" dlg:tab-index="27" dlg:left="5" dlg:top="286" dlg:width="55" dlg:height="12" dlg:help-text="&73.XhtmlOptions.ExportButton.HelpText" dlg:value="&74.XhtmlOptions.ExportButton.Label" dlg:button-type="ok"/> + <dlg:button dlg:id="CancelButton" dlg:tab-index="28" dlg:left="70" dlg:top="286" dlg:width="55" dlg:height="12" dlg:help-text="&75.XhtmlOptions.CancelButton.HelpText" dlg:value="&76.XhtmlOptions.CancelButton.Label" dlg:button-type="cancel"/> + </dlg:bulletinboard> +</dlg:window> \ No newline at end of file diff --git a/source/oxt/writer2xhtml/W2XDialogs/XhtmlOptionsCalc.xdl b/source/oxt/writer2xhtml/W2XDialogs/XhtmlOptionsCalc.xdl new file mode 100644 index 0000000..17ff8fe --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/XhtmlOptionsCalc.xdl @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd"> +<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="XhtmlOptionsCalc" dlg:left="139" dlg:top="84" dlg:width="203" dlg:height="306" dlg:help-text="&77.XhtmlOptionsCalc.HelpText" dlg:closeable="true" dlg:moveable="true" dlg:title="&78.XhtmlOptionsCalc.Title"> + <dlg:bulletinboard> + <dlg:text dlg:id="StyleLabel" dlg:tab-index="0" dlg:left="5" dlg:top="8" dlg:width="183" dlg:height="12" dlg:help-text="&79.XhtmlOptionsCalc.StyleLabel.HelpText" dlg:value="&80.XhtmlOptionsCalc.StyleLabel.Label"/> + <dlg:text dlg:id="ConfigLabel" dlg:tab-index="1" dlg:left="10" dlg:top="22" dlg:width="76" dlg:height="12" dlg:help-text="&81.XhtmlOptionsCalc.ConfigLabel.HelpText" dlg:value="&82.XhtmlOptionsCalc.ConfigLabel.Label"/> + <dlg:menulist dlg:id="Config" dlg:tab-index="2" dlg:left="97" dlg:top="20" dlg:width="92" dlg:height="12" dlg:help-text="&83.XhtmlOptionsCalc.Config.HelpText" dlg:spin="true" dlg:linecount="2"> + <dlg:menupopup> + <dlg:menuitem dlg:value="&140.Config.StringItemList"/> + <dlg:menuitem dlg:value="&141.Config.StringItemList"/> + </dlg:menupopup> + <script:event script:event-name="on-itemstatechange" script:macro-name="vnd.sun.star.UNO:ConfigChange" script:language="UNO"/> + </dlg:menulist> + <dlg:text dlg:id="ScalingLabel" dlg:tab-index="3" dlg:left="10" dlg:top="36" dlg:width="76" dlg:height="12" dlg:help-text="&86.XhtmlOptionsCalc.ScalingLabel.HelpText" dlg:value="&87.XhtmlOptionsCalc.ScalingLabel.Label"/> + <dlg:numericfield dlg:id="Scaling" dlg:tab-index="4" dlg:left="144" dlg:top="34" dlg:width="30" dlg:height="12" dlg:help-text="&88.XhtmlOptionsCalc.Scaling.HelpText" dlg:strict-format="true" dlg:decimal-accuracy="0" dlg:value="100" dlg:value-min="1" dlg:value-max="1000" dlg:value-step="10" dlg:spin="true"/> + <dlg:text dlg:id="ScalingPercentLabel" dlg:tab-index="5" dlg:left="179" dlg:top="36" dlg:width="10" dlg:height="12" dlg:help-text="&89.XhtmlOptionsCalc.ScalingPercentLabel.HelpText" dlg:value="&90.XhtmlOptionsCalc.ScalingPercentLabel.Label"/> + <dlg:text dlg:id="ColumnScalingLabel" dlg:tab-index="6" dlg:left="10" dlg:top="50" dlg:width="76" dlg:height="12" dlg:help-text="&91.XhtmlOptionsCalc.ColumnScalingLabel.HelpText" dlg:value="&92.XhtmlOptionsCalc.ColumnScalingLabel.Label"/> + <dlg:numericfield dlg:id="ColumnScaling" dlg:tab-index="7" dlg:left="144" dlg:top="48" dlg:width="30" dlg:height="12" dlg:help-text="&93.XhtmlOptionsCalc.ColumnScaling.HelpText" dlg:strict-format="true" dlg:decimal-accuracy="0" dlg:value="100" dlg:value-min="1" dlg:value-max="1000" dlg:value-step="10" dlg:spin="true"/> + <dlg:text dlg:id="ColumnScalingPercentLabel" dlg:tab-index="8" dlg:left="179" dlg:top="50" dlg:width="10" dlg:height="12" dlg:help-text="&94.XhtmlOptionsCalc.ColumnScalingPercentLabel.HelpText" dlg:value="&95.XhtmlOptionsCalc.ColumnScalingPercentLabel.Label"/> + <dlg:checkbox dlg:id="ConvertToPx" dlg:tab-index="9" dlg:left="10" dlg:top="64" dlg:width="177" dlg:height="12" dlg:help-text="&96.XhtmlOptionsCalc.ConvertToPx.HelpText" dlg:value="&97.XhtmlOptionsCalc.ConvertToPx.Label" dlg:checked="true"/> + <dlg:checkbox dlg:id="OriginalImageSize" dlg:tab-index="10" dlg:left="10" dlg:top="78" dlg:width="177" dlg:height="12" dlg:help-text="&98.XhtmlOptionsCalc.OriginalImageSize.HelpText" dlg:value="&99.XhtmlOptionsCalc.OriginalImageSize.Label" dlg:checked="false"/> + <dlg:text dlg:id="SpecialContentLabel" dlg:tab-index="11" dlg:left="5" dlg:top="92" dlg:width="184" dlg:height="12" dlg:help-text="&100.XhtmlOptionsCalc.SpecialContentLabel.HelpText" dlg:value="&101.XhtmlOptionsCalc.SpecialContentLabel.Label"/> + <dlg:checkbox dlg:id="Notes" dlg:tab-index="12" dlg:left="10" dlg:top="106" dlg:width="179" dlg:height="12" dlg:help-text="&102.XhtmlOptionsCalc.Notes.HelpText" dlg:value="&103.XhtmlOptionsCalc.Notes.Label" dlg:checked="true"/> + <dlg:checkbox dlg:id="UseDublinCore" dlg:tab-index="13" dlg:left="10" dlg:top="120" dlg:width="179" dlg:height="12" dlg:help-text="&104.XhtmlOptionsCalc.UseDublinCore.HelpText" dlg:value="&105.XhtmlOptionsCalc.UseDublinCore.Label" dlg:checked="true"/> + <dlg:text dlg:id="SheetsLabel" dlg:tab-index="14" dlg:left="5" dlg:top="134" dlg:width="184" dlg:height="12" dlg:help-text="&106.XhtmlOptionsCalc.SheetsLabel.HelpText" dlg:value="&107.XhtmlOptionsCalc.SheetsLabel.Label"/> + <dlg:checkbox dlg:id="DisplayHiddenSheets" dlg:tab-index="15" dlg:left="10" dlg:top="148" dlg:width="175" dlg:height="12" dlg:help-text="&108.XhtmlOptionsCalc.DisplayHiddenSheets.HelpText" dlg:value="&109.XhtmlOptionsCalc.DisplayHiddenSheets.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="DisplayHiddenRowsCols" dlg:tab-index="16" dlg:left="10" dlg:top="162" dlg:width="175" dlg:height="12" dlg:help-text="&110.XhtmlOptionsCalc.DisplayHiddenRowsCols.HelpText" dlg:value="&111.XhtmlOptionsCalc.DisplayHiddenRowsCols.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="DisplayFilteredRowsCols" dlg:tab-index="17" dlg:left="10" dlg:top="176" dlg:width="175" dlg:height="12" dlg:help-text="&112.XhtmlOptionsCalc.DisplayFilteredRowsCols.HelpText" dlg:value="&113.XhtmlOptionsCalc.DisplayFilteredRowsCols.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="ApplyPrintRanges" dlg:tab-index="18" dlg:left="10" dlg:top="190" dlg:width="175" dlg:height="12" dlg:help-text="&114.XhtmlOptionsCalc.ApplyPrintRanges.HelpText" dlg:value="&115.XhtmlOptionsCalc.ApplyPrintRanges.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="UseTitleAsHeading" dlg:tab-index="19" dlg:left="10" dlg:top="204" dlg:width="175" dlg:height="12" dlg:help-text="&116.XhtmlOptionsCalc.UseTitleAsHeading.HelpText" dlg:value="&117.XhtmlOptionsCalc.UseTitleAsHeading.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="UseSheetNamesAsHeadings" dlg:tab-index="20" dlg:left="10" dlg:top="218" dlg:width="175" dlg:height="12" dlg:help-text="&118.XhtmlOptionsCalc.UseSheetNamesAsHeadings.HelpText" dlg:value="&119.XhtmlOptionsCalc.UseSheetNamesAsHeadings.Label" dlg:checked="false"/> + <dlg:text dlg:id="FilesLabel" dlg:tab-index="21" dlg:left="5" dlg:top="232" dlg:width="175" dlg:height="12" dlg:help-text="&120.XhtmlOptionsCalc.FilesLabel.HelpText" dlg:value="&121.XhtmlOptionsCalc.FilesLabel.Label"/> + <dlg:checkbox dlg:id="CalcSplit" dlg:tab-index="22" dlg:left="10" dlg:top="246" dlg:width="175" dlg:height="12" dlg:help-text="&122.XhtmlOptionsCalc.CalcSplit.HelpText" dlg:value="&123.XhtmlOptionsCalc.CalcSplit.Label" dlg:checked="false"/> + <dlg:checkbox dlg:id="SaveImagesInSubdir" dlg:tab-index="23" dlg:left="10" dlg:top="260" dlg:width="175" dlg:height="12" dlg:help-text="&124.XhtmlOptionsCalc.SaveImagesInSubdir.HelpText" dlg:value="&125.XhtmlOptionsCalc.SaveImagesInSubdir.Label" dlg:checked="false"/> + <dlg:button dlg:id="ExportButton" dlg:tab-index="24" dlg:left="5" dlg:top="286" dlg:width="55" dlg:height="12" dlg:help-text="&126.XhtmlOptionsCalc.ExportButton.HelpText" dlg:value="&127.XhtmlOptionsCalc.ExportButton.Label" dlg:button-type="ok"/> + <dlg:button dlg:id="CancelButton" dlg:tab-index="25" dlg:left="70" dlg:top="286" dlg:width="55" dlg:height="12" dlg:help-text="&128.XhtmlOptionsCalc.CancelButton.HelpText" dlg:value="&129.XhtmlOptionsCalc.CancelButton.Label" dlg:button-type="cancel"/> + </dlg:bulletinboard> +</dlg:window> \ No newline at end of file diff --git a/source/oxt/writer2xhtml/W2XDialogs/dialog.xlb b/source/oxt/writer2xhtml/W2XDialogs/dialog.xlb new file mode 100644 index 0000000..983cb49 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/dialog.xlb @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="W2XDialogs" library:readonly="false" library:passwordprotected="false"> + <library:element library:name="XhtmlOptions"/> + <library:element library:name="XhtmlOptionsCalc"/> +</library:library> \ No newline at end of file diff --git a/source/oxt/writer2xhtml/W2XDialogs/script.xlb b/source/oxt/writer2xhtml/W2XDialogs/script.xlb new file mode 100644 index 0000000..e080e86 --- /dev/null +++ b/source/oxt/writer2xhtml/W2XDialogs/script.xlb @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd"> +<library:library xmlns:library="http://openoffice.org/2000/library" library:name="W2XDialogs" library:readonly="false" library:passwordprotected="false"> + <library:element library:name="Module1"/> +</library:library> \ No newline at end of file diff --git a/source/oxt/writer2xhtml/desc_da.txt b/source/oxt/writer2xhtml/desc_da.txt new file mode 100644 index 0000000..c0a8a9f --- /dev/null +++ b/source/oxt/writer2xhtml/desc_da.txt @@ -0,0 +1 @@ +Writer2xhtml installerer eksportfiltre til XHTML og XHTML+MathML i Writer og Calc \ No newline at end of file diff --git a/source/oxt/writer2xhtml/desc_en.txt b/source/oxt/writer2xhtml/desc_en.txt new file mode 100644 index 0000000..596929e --- /dev/null +++ b/source/oxt/writer2xhtml/desc_en.txt @@ -0,0 +1 @@ +Writer2xhtml provides Writer and Calc export filters for XHTML and XHTML+MathML \ No newline at end of file diff --git a/source/oxt/writer2xhtml/desc_fr.txt b/source/oxt/writer2xhtml/desc_fr.txt new file mode 100644 index 0000000..f2bf56d --- /dev/null +++ b/source/oxt/writer2xhtml/desc_fr.txt @@ -0,0 +1 @@ +Writer2xhtml permet d'exporter les documents Writer et Calc vers XHTML et XHTML+MathML \ No newline at end of file diff --git a/source/oxt/writer2xhtml/desc_ru.txt b/source/oxt/writer2xhtml/desc_ru.txt new file mode 100644 index 0000000..fa1d239 --- /dev/null +++ b/source/oxt/writer2xhtml/desc_ru.txt @@ -0,0 +1 @@ +Writer2xhtml обеспечивает для Writer и Calc фильтрами экспорта в XHTML и XHTML+MathML diff --git a/source/oxt/writer2xhtml/desc_uk.txt b/source/oxt/writer2xhtml/desc_uk.txt new file mode 100644 index 0000000..b1390ac --- /dev/null +++ b/source/oxt/writer2xhtml/desc_uk.txt @@ -0,0 +1 @@ +Writer2xhtml забезпечує Writer та Calc фільтрами експорту до XHTML та XHTML+MathML diff --git a/source/oxt/writer2xhtml/description.xml b/source/oxt/writer2xhtml/description.xml new file mode 100644 index 0000000..470181b --- /dev/null +++ b/source/oxt/writer2xhtml/description.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<description xmlns="http://openoffice.org/extensions/description/2006" + xmlns:d="http://openoffice.org/extensions/description/2006"> + + <identifier value="org.openoffice.da.writer2xhtml.oxt" /> + + <version value="0.9.4" /> + + <dependencies> + <OpenOffice.org-minimal-version value="2.2" d:name="OpenOffice.org 2.2"/> + </dependencies> + + <display-name> + <name lang="da">Writer2xhtml eksportfiltre</name> + <name lang="en">Writer2xhtml export filters</name> + <name lang="fr">Filtres d'exportation Writer2xhtml</name> + <name lang="ru">Фильтры экспорта Writer2xhtml</name> + <name lang="uk">Фільтри експорту Writer2xhtml</name> + </display-name> + + <extension-description> + <src xlink:href="description/desc_da.txt" lang="da" /> + <src xlink:href="description/desc_en.txt" lang="en" /> + <src xlink:href="description/desc_fr.txt" lang="fr" /> + <src xlink:href="description/desc_ru.txt" lang="ru" /> + <src xlink:href="description/desc_uk.txt" lang="uk" /> + </extension-description> + +</description> diff --git a/source/oxt/writer2xhtml/w2x_filters.xcu b/source/oxt/writer2xhtml/w2x_filters.xcu new file mode 100644 index 0000000..3ddba97 --- /dev/null +++ b/source/oxt/writer2xhtml/w2x_filters.xcu @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:package="org.openoffice.TypeDetection" oor:name="Filter"> + + <node oor:name="Filters"> + + <node oor:name="org.openoffice.da.writer2xhtml" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>writer_xhtml10_File</value></prop> + <prop oor:name="DocumentService"><value>com.sun.star.text.TextDocument</value></prop> + <prop oor:name="UIComponent"><value>org.openoffice.da.comp.writer2xhtml.XhtmlOptionsDialog</value></prop> + <prop oor:name="UserData"><value>org.openoffice.da.comp.writer2xhtml.W2XExportFilter unused com.sun.star.comp.Writer.XMLOasisImporter com.sun.star.comp.Writer.XMLOasisExporter staroffice/sxw text/html</value></prop> + <prop oor:name="FilterService"><value>com.sun.star.comp.Writer.XmlFilterAdaptor</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="UIName"> + <value>XHTML 1.0 strict</value> + </prop> + <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop> + </node> + + <node oor:name="org.openoffice.da.calc2xhtml" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>writer_xhtml10_File</value></prop> + <prop oor:name="DocumentService"><value>com.sun.star.sheet.SpreadsheetDocument</value></prop> + <prop oor:name="UIComponent"><value>org.openoffice.da.comp.writer2xhtml.XhtmlOptionsDialogCalc</value></prop> + <prop oor:name="UserData"><value>org.openoffice.da.comp.writer2xhtml.W2XExportFilter unused com.sun.star.comp.Calc.XMLOasisImporter com.sun.star.comp.Calc.XMLOasisExporter staroffice/sxc text/html</value></prop> + <prop oor:name="FilterService"><value>com.sun.star.comp.Writer.XmlFilterAdaptor</value></prop><!-- sic! --> + <prop oor:name="TemplateName"/> + <prop oor:name="UIName"> + <value>XHTML 1.0 strict</value> + </prop> + <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop> + </node> + + <node oor:name="org.openoffice.da.writer2xhtml.mathml" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>writer_xhtml_mathml_File</value></prop> + <prop oor:name="DocumentService"><value>com.sun.star.text.TextDocument</value></prop> + <prop oor:name="UIComponent"><value>org.openoffice.da.comp.writer2xhtml.XhtmlOptionsDialog</value></prop> + <prop oor:name="UserData"><value>org.openoffice.da.comp.writer2xhtml.W2XExportFilter unused com.sun.star.comp.Writer.XMLOasisImporter com.sun.star.comp.Writer.XMLOasisExporter staroffice/sxw application/xhtml+xml</value></prop> + <prop oor:name="FilterService"><value>com.sun.star.comp.Writer.XmlFilterAdaptor</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="UIName"> + <value>XHTML 1.1 + MathML 2.0</value> + </prop> + <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop> + </node> + + <node oor:name="org.openoffice.da.writer2xhtml.mathml.xsl" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="FileFormatVersion"><value>0</value></prop> + <prop oor:name="Type"><value>writer_xhtml_mathml_xsl_File</value></prop> + <prop oor:name="DocumentService"><value>com.sun.star.text.TextDocument</value></prop> + <prop oor:name="UIComponent"><value>org.openoffice.da.comp.writer2xhtml.XhtmlOptionsDialogXsl</value></prop> + <prop oor:name="UserData"><value>org.openoffice.da.comp.writer2xhtml.W2XExportFilter unused com.sun.star.comp.Writer.XMLOasisImporter com.sun.star.comp.Writer.XMLOasisExporter staroffice/sxw application/xml</value></prop> + <prop oor:name="FilterService"><value>com.sun.star.comp.Writer.XmlFilterAdaptor</value></prop> + <prop oor:name="TemplateName"/> + <prop oor:name="UIName"> + <value>XHTML 1.1 + MathML 2.0 (xsl)</value> + </prop> + <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop> + </node> + + </node> + +</oor:component-data> diff --git a/source/oxt/writer2xhtml/w2x_types.xcu b/source/oxt/writer2xhtml/w2x_types.xcu new file mode 100644 index 0000000..c9dad65 --- /dev/null +++ b/source/oxt/writer2xhtml/w2x_types.xcu @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:package="org.openoffice.TypeDetection" oor:name="Types"> + + <node oor:name="Types"> + + <node oor:name="writer_xhtml10_File" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="DetectService"/> + <prop oor:name="URLPattern"/> + <prop oor:name="Extensions"><value>html</value></prop> + <prop oor:name="MediaType"/> + <prop oor:name="Preferred"><value>false</value></prop> + <prop oor:name="UIName"> + <value>XHTML 1.0 strict</value> + </prop> + <prop oor:name="ClipboardFormat"/> + </node> + + <node oor:name="writer_xhtml_mathml_File" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="DetectService"/> + <prop oor:name="URLPattern"/> + <prop oor:name="Extensions"><value>xhtml</value></prop> + <prop oor:name="MediaType"/> + <prop oor:name="Preferred"><value>false</value></prop> + <prop oor:name="UIName"> + <value>XHTML 1.1 + MathML 2.0</value> + </prop> + <prop oor:name="ClipboardFormat"/> + </node> + + <node oor:name="writer_xhtml_mathml_xsl_File" oor:op="replace" oor:finalized="true" oor:mandatory="true"> + <prop oor:name="DetectService"/> + <prop oor:name="URLPattern"/> + <prop oor:name="Extensions"><value>xml</value></prop> + <prop oor:name="MediaType"/> + <prop oor:name="Preferred"><value>false</value></prop> + <prop oor:name="UIName"> + <value>XHTML 1.1 + MathML 2.0 (xsl)</value> + </prop> + <prop oor:name="ClipboardFormat"/> + </node> + + </node> + +</oor:component-data> diff --git a/source/oxt/xhtml-config-sample/META-INF/manifest.xml b/source/oxt/xhtml-config-sample/META-INF/manifest.xml new file mode 100644 index 0000000..db062bc --- /dev/null +++ b/source/oxt/xhtml-config-sample/META-INF/manifest.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE manifest:manifest PUBLIC "-//OpenOffice.org//DTD Manifest 1.0//EN" "Manifest.dtd"> +<manifest:manifest xmlns:manifest="http://openoffice.org/2001/manifest"> + <manifest:file-entry manifest:media-type="application/vnd.sun.star.configuration-data" + manifest:full-path ="Paths.xcu"/> + <manifest:file-entry manifest:media-type="application/vnd.sun.star.configuration-data" + manifest:full-path ="Options.xcu"/> +</manifest:manifest> diff --git a/source/oxt/xhtml-config-sample/Options.xcu b/source/oxt/xhtml-config-sample/Options.xcu new file mode 100644 index 0000000..2a18a6a --- /dev/null +++ b/source/oxt/xhtml-config-sample/Options.xcu @@ -0,0 +1,45 @@ +<?xml version='1.0' encoding='UTF-8'?> +<oor:component-data oor:name="Options" + oor:package="org.openoffice.da.Writer2xhtml" + xml:lang="en-US" + xmlns:oor="http://openoffice.org/2001/registry" + xmlns:xs="http://www.w3.org/2001/XMLSchema"> + <!-- This is a sample registry file for use with Writer2xhtml --> + <node oor:name="XhtmlOptions"> + <node oor:name="Configurations"> + <!-- This node adds a configuration to the ui --> + <node oor:name="sampleconfig1" oor:op="replace"> + <!-- A comma separated of options that should be locked in the ui --> + <prop oor:name="LockedOptions" oor:type="xs:string"> + <value>xhtml_notes,xhtml_split_level,xhtml_repeat_levels</value> + </prop> + <!-- The display name will be used in the list box --> + <prop oor:name="DisplayName" oor:type="xs:string" oor:localized="true"> + <value>Sample custom style</value> + </prop> + <!-- This url points to the configuration to use --> + <prop oor:name="ConfigURL" oor:type="xs:string"> + <value>%origin%/config/sampleconfig.xml</value> + </prop> + <!-- This url points to the target template to use --> + <prop oor:name="TargetTemplateURL" oor:type="xs:string"> + <value>%origin%/config/sampletemplate.xhtml</value> + </prop> + </node> + </node> + <node oor:name="Templates"> + <!-- This node maps an OOo template to the our configuration --> + <node oor:name="sampletemplate1" oor:op="replace"> + <prop oor:name="TemplateName" oor:type="xs:string"> + <value>XHTML template</value> + </prop> + <prop oor:name="ConfigName" oor:type="xs:string"> + <value>sampleconfig1</value> + </prop> + </node> + </node> + </node> +</oor:component-data> + + + \ No newline at end of file diff --git a/source/oxt/xhtml-config-sample/Paths.xcu b/source/oxt/xhtml-config-sample/Paths.xcu new file mode 100644 index 0000000..df903a6 --- /dev/null +++ b/source/oxt/xhtml-config-sample/Paths.xcu @@ -0,0 +1,11 @@ +<?xml version='1.0' encoding='UTF-8'?> +<oor:component-data oor:package="org.openoffice.Office" oor:name="Paths" xmlns:install="http://openoffice.org/2004/installation" xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <node oor:name="Paths"> + <node oor:name="Template" oor:op="fuse"> + <node oor:name="InternalPaths"> + <node oor:name="%origin%/template" oor:op="fuse"/> + </node> + </node> + </node> +</oor:component-data> + diff --git a/source/oxt/xhtml-config-sample/config/sampleconfig.xml b/source/oxt/xhtml-config-sample/config/sampleconfig.xml new file mode 100644 index 0000000..5da8e00 --- /dev/null +++ b/source/oxt/xhtml-config-sample/config/sampleconfig.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- This is a sample configuration file for Writer2xhtml + Except for a few changes it's identical to cleanxhtml.xml + --> + +<config> + <!-- options --> + <option name="xhtml_no_doctype" value="false" /> + <option name="xhtml_formatting" value="ignore_all" /> + <option name="xhtml_frame_formatting" value="ignore_all" /> + <option name="xhtml_section_formatting" value="convert_all" /> + <option name="xhtml_table_formatting" value="convert_all" /> + <option name="xhtml_ignore_table_dimensions" value="false" /> + <option name="xhtml_uplink" value="http://www.hj-gym.dk/~hj/writer2latex" /> + <!-- options usually set in the dialog --> + <option name="xhtml_notes" value="false" /> + <option name="xhtml_split_level" value="2" /> + <option name="xhtml_repeat_levels" value="1" /> + + <!-- map special OOo paragraph style to xhtml elements --> + <xhtml-style-map name="My Special Style" class="paragraph" element="p" css="special" /> + + <!-- map standard OOo paragraph styles to xhtml elements --> + <xhtml-style-map name="Text body" class="paragraph" element="p" css="(none)" /> + <xhtml-style-map name="Sender" class="paragraph" element="address" css="(none)" /> + <xhtml-style-map name="Preformatted Text" class="paragraph" element="pre" css="(none)" /> + <xhtml-style-map name="Quotations" class="paragraph" +block-element="blockquote" block-css="(none)" element="p" css="(none)" /> + <xhtml-style-map name="List Heading" class="paragraph" block-element="dl" block-css="(none)" element="dt" css="(none)" /> + <xhtml-style-map name="List Contents" class="paragraph" block-element="dl" block-css="(none)" element="dd" css="(none)" /> + <xhtml-style-map name="Horizontal Line" class="paragraph" element="hr" css="(none)" /> + + <!-- map standard OOo text styles to xhtml elements --> + <xhtml-style-map name="Citation" class="text" element="cite" css="(none)" /> + <xhtml-style-map name="Definition" class="text" element="dfn" css="(none)" /> + <xhtml-style-map name="Emphasis" class="text" element="em" css="(none)" /> + <xhtml-style-map name="Example" class="text" element="samp" css="(none)" /> + <xhtml-style-map name="Source Text" class="text" element="code" css="(none)" /> + <xhtml-style-map name="Strong Emphasis" class="text" element="strong" css="(none)" /> + <xhtml-style-map name="Teletype" class="text" element="tt" css="(none)" /> + <xhtml-style-map name="User entry" class="text" element="kbd" css="(none)" /> + <xhtml-style-map name="Variable" class="text" element="var" css="(none)" /> + + <!-- map OOo hard formatting attributes to xhtml elements --> + <xhtml-style-map name="bold" class="attribute" element="b" css="(none)" /> + <xhtml-style-map name="italics" class="attribute" element="i" css="(none)" /> + <xhtml-style-map name="fixed" class="attribute" element="tt" css="(none)" /> + <xhtml-style-map name="superscript" class="attribute" element="sup" css="(none)" /> + <xhtml-style-map name="subscript" class="attribute" element="sub" css="(none)" /> + +</config> + diff --git a/source/oxt/xhtml-config-sample/config/sampletemplate.xhtml b/source/oxt/xhtml-config-sample/config/sampletemplate.xhtml new file mode 100644 index 0000000..a523313 --- /dev/null +++ b/source/oxt/xhtml-config-sample/config/sampletemplate.xhtml @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<html xml:lang="en-US" xmlns="http://www.w3.org/1999/xhtml"> +<!-- This is a sample target template for use with Writer2xhtml --> +<head> +<!-- Writer2xhtml will extend the title with the document title --> +<title>Writer2xhtml sample - </title> +<link rel="stylesheet" type="text/css" media="all" href="http://www.w3.org/StyleSheets/Core/Oldstyle" /> +<style type="text/css"> +#panel { + width:250px; padding:10px; margin:10px; + border-left:2px solid black; border-bottom:2px solid black; + float:right; +} +#header,#footer { + left:0; right:0; top:0; bottom:0; height: 30px; padding:10px; + vertical-align: middle; background-color:black; color:gray; font-weight:bold; + clear:right; +} +#content { + left:0; right:0; top:0; bottom:0; +} +#header a,#footer a { + text-decoration: none; background-color: black; color: white; +} +#header a:before,#footer a:before, #header span.nolink:before, #footer span.nolink:before { + content:'['; +} +#header a:after,#footer a:after, #header span.nolink:after, #footer span.nolink:after { + content:']'; +} +#header a:hover,#footer a:hover { + text-decoration: none; background-color: white; color: black; +} +pre { clear:right; } +#panel p.level1 { margin-top:1px; margin-bottom:1px; margin-left:0px; margin-right:0px; font-size: 14px;} +#panel p.level2 { margin-top:1px; margin-bottom:1px; margin-left:10px; margin-right:0px; font-size: 12px;} +#panel p.level3 { margin-top:1px; margin-bottom:1px; margin-left:20px; margin-right:0px; font-size: 10px;} + +p.special { font-size:20pt; margin-top:20px; margin-bottom:20px; color:green; } +</style> +</head> +<body> +<!-- Writer2xhtml will add content after existing content in the div's --> +<div id="header">Writer2xhtml </div> +<div id="content"> +<div id="panel"/> +</div> +<div id="footer">Writer2xhtml </div> +</body> +</html> diff --git a/source/oxt/xhtml-config-sample/description.xml b/source/oxt/xhtml-config-sample/description.xml new file mode 100644 index 0000000..bf56138 --- /dev/null +++ b/source/oxt/xhtml-config-sample/description.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<description xmlns="http://openoffice.org/extensions/description/2006" + xmlns:d="http://openoffice.org/extensions/description/2006"> + <identifier value="org.openoffice.da.writer2latex.xhtml-config-sample.oxt" /> + <version value="0.9.4" /> +</description> diff --git a/source/oxt/xhtml-config-sample/template/xhtml-sample-config/XHTML template.ott b/source/oxt/xhtml-config-sample/template/xhtml-sample-config/XHTML template.ott new file mode 100644 index 0000000000000000000000000000000000000000..0c675049375fcce7bca8625abb61c49d4b2b3b85 GIT binary patch literal 7237 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 literal 0 HcmV?d00001 diff --git a/source/readme-source.txt b/source/readme-source.txt new file mode 100644 index 0000000..a75b598 --- /dev/null +++ b/source/readme-source.txt @@ -0,0 +1,68 @@ +Writer2LaTeX source version 0.9.4 +================================= + +Writer2LaTeX is (c) 2002-2009 by Henrik Just. +The source is available under the terms and conditions of the +GNU LESSER GENERAL PUBLIC LICENSE, version 2.1. +Please see the file COPYING.TXT for details. + +Overview +-------- + +The source of Writer2LaTeX consists of three major parts: + +* A general purpose java library for converting OpenDocument files into LaTeX, + BibTeX, xhtml and xhtml+MathML + This is to be found in the packages writer2latex.* and should only be used + through the provided api writer2latex.api.* +* A command line utility writer2latex.Application +* A collection of components for OpenOffice.org + These are to be found in the packages org.openoffice.da.comp.* + +Currently parts of the source for Writer2LaTeX are somewhat messy and +undocumented. This situation is improving from time to time :-) + + +Building Writer2LaTeX +--------------------- + +Writer2LaTeX uses Ant version 1.5 or later (http://ant.apache.org) to build. + + +Some java libraries from OOo are needed to build the filter part of Writer2LaTeX, +these are jurt.jar, unoil.jar, ridl.jar and juh.jar. + +To make these files available for the compiler, edit the file build.xml in +the writer2latex09 directory as follows: + +The line + <property name="OFFICE_HOME" location=""/> +should be edited to contain the path to your OOo installation, e.g. + <property name="OFFICE_HOME" location="C:/Program Files/OpenOffice.org 2.0.4"/> + + +To build, open a command shell, navigate to the writer2latex09 directory and type + +ant oxt + +(this assumes, that ant is in your path; otherwise specifify the full path.) + +In addition to oxt, the build file supports the following targets: + all + Build nearly everything + compile + Compile all file except the tests. + jar + Create the standalone jar file. + javadoc + Create the javadoc documentation in target/javadoc. + distro + Create distribution files + clean + + +Henrik Just, February 2009 + + +Thanks to Michael Niedermair for writing the original ant build file +