Initial import

git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@5 f0f2a975-2e09-46c8-9428-3b39399b9f3c
This commit is contained in:
henrikjust 2009-02-20 09:37:06 +00:00
parent 75e32b1e8f
commit b0b66fcae9
252 changed files with 49000 additions and 0 deletions

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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) {
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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));
}
}
}
}
}

View file

@ -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;
}
}

View file

@ -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; }
}

View file

@ -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;
}
}

View file

@ -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??)

View file

@ -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; }
}
}

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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>

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

File diff suppressed because it is too large Load diff

View file

@ -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);
}
}

View file

@ -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 = &lt;string&gt;</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(); }
}
}
}
}

View file

@ -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);
}
}

View file

@ -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="}&#10;\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="&#10;\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>

View file

@ -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/>

View file

@ -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>

View file

@ -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>

View file

@ -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="}&#10;\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="&#10;\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>

View file

@ -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;
}
}

View file

@ -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);
}

View file

@ -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>

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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; }
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -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();
}
}

File diff suppressed because it is too large Load diff

View file

@ -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; }
}

View file

@ -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;
}
}

View file

@ -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]; }
}

View file

@ -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("\"");
}
}
}

View file

@ -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>

View file

@ -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();
}
}

View file

@ -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;
}