More math and TexMaths work

git-svn-id: svn://svn.code.sf.net/p/writer2latex/code/trunk@160 f0f2a975-2e09-46c8-9428-3b39399b9f3c
This commit is contained in:
henrikjust 2014-08-13 06:39:15 +00:00
parent 79ae252419
commit 3dff4f20b4
15 changed files with 514 additions and 525 deletions

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2012-08-05)
* Version 1.4 (2012-08-13)
*
*/
@ -33,7 +33,7 @@ public class ConverterFactory {
// Version information
private static final String VERSION = "1.3.1";
private static final String DATE = "2014-08-05";
private static final String DATE = "2014-08-13";
/** Return the Writer2LaTeX version in the form
* (major version).(minor version).(patch level)<br/>

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-06)
* Version 1.4 (2014-08-13)
*
*/
@ -32,18 +32,13 @@ import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import writer2latex.api.GraphicConverter;
import writer2latex.api.Converter;
import writer2latex.api.ConverterResult;
import writer2latex.api.OutputFile;
import writer2latex.office.EmbeddedObject;
import writer2latex.office.EmbeddedXMLObject;
import writer2latex.office.ImageLoader;
import writer2latex.office.MIMETypes;
import writer2latex.office.MetaData;
import writer2latex.office.OfficeDocument;
import writer2latex.office.OfficeReader;
@ -53,6 +48,8 @@ import writer2latex.util.Misc;
/**<p>Abstract base implementation of <code>writer2latex.api.Converter</code></p>
*/
public abstract class ConverterBase implements Converter {
public enum TexMathsStyle {inline, display, latex};
// Helper
protected GraphicConverter graphicConverter;
@ -67,10 +64,6 @@ public abstract class ConverterBase implements Converter {
protected String sTargetFileName;
protected ConverterResultImpl converterResult;
// Result of latest parsing of a display equation
private Element theEquation = null;
private Element theSequence = null;
// Constructor
public ConverterBase() {
graphicConverter = null;
@ -151,22 +144,6 @@ public abstract class ConverterBase implements Converter {
return odDoc.getEmbeddedObject(sHref);
}
/** Get the equation found by the last invocation of <code>parseDisplayEquation</code>
*
* @return the equation or null if no equation was found
*/
public Element getEquation() {
return theEquation;
}
/** Get the sequence number found by the last invocation of <code>parseDisplayEquation</code>
*
* @return the sequence number or null if no sequence number was found
*/
public Element getSequence() {
return theSequence;
}
/** Get a TexMaths equation from a draw:frame (PNG formula) or draw:g element (SVG)
* Such an element is a TexMaths equation if it contains an svg:title element with content "TexMaths"
* The actual formula is the content of an svg:desc element
@ -181,133 +158,28 @@ public abstract class ConverterBase implements Converter {
}
return null;
}
/** Get a MathML formula from a draw:frame
*
* @param node the draw:frame
* @return the MathML element, or null if this is not a MathML formula
*/
public Element getMathmlEquation(Element node) {
if (node.getTagName().equals(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 = getEmbeddedObject(sHref);
if (object!=null) {
if (MIMETypes.MATH.equals(object.getType()) || MIMETypes.ODF.equals(object.getType())) { // Formula!
try {
Document formuladoc = ((EmbeddedXMLObject) object).getContentDOM();
Element formula = Misc.getChildByTagName(formuladoc,XMLString.MATH); // Since OOo 3.2
if (formula==null) {
formula = Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH);
}
return formula;
}
catch (org.xml.sax.SAXException e) {
e.printStackTrace();
}
catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
}
}
else { // flat XML, object is contained in node
Element formula = Misc.getChildByTagName(node,XMLString.MATH); // Since OOo 3.2
if (formula==null) {
formula = Misc.getChildByTagName(node,XMLString.MATH_MATH);
}
return formula;
}
return null;
public TexMathsStyle getTexMathsStyle(String s) {
String[] sContent = s.split("\u00a7");
if (sContent.length>=3) { // we only need 3 items of 6
if ("display".equals(sContent[1])) {
return TexMathsStyle.display;
}
else if ("latex".equals(sContent[1]) || "text".equals(sContent[1])) { // text is for OOoLaTeX
return TexMathsStyle.latex;
}
}
return TexMathsStyle.inline;
}
/** Determine whether or not a paragraph contains a display equation.
* A paragraph is a display equation if it contains a single formula and no text content except whitespace
* and an optional sequence number which may be in brackets.
* As a side effect, this method keeps a reference to the equation and the sequence number
*
* @param node the paragraph
* @return true if this is a display equation
*/
public boolean parseDisplayEquation(Node node) {
theEquation = null;
theSequence = null;
return doParseDisplayEquation(node);
}
private boolean doParseDisplayEquation(Node node) {
Node child = node.getFirstChild();
while (child!=null) {
if (Misc.isElement(child)) {
Element elm = (Element) child;
String sName = elm.getTagName();
// First check for MathML or TexMaths equation
Element equation = getMathmlEquation(elm);
if (equation==null) {
equation = getTexMathsEquation(elm);
}
if (equation!=null) {
if (theEquation==null) {
theEquation = equation;
}
else { // two or more equations -> not a display
return false;
}
}
else if (XMLString.TEXT_SEQUENCE.equals(sName)) {
if (theSequence==null) {
theSequence = elm;
}
else { // two sequence numbers -> not a display
return false;
}
}
else if (XMLString.TEXT_SPAN.equals(sName)) {
if (!doParseDisplayEquation(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 if (XMLString.TEXT_SOFT_PAGE_BREAK.equals(sName)) { // since ODF 1.1
// Soft page breaks 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;
public String getTexMathsEquation(String s) {
String[] sContent = s.split("\u00a7");
if (sContent.length>=3) { // we only need 3 items of 6
return sContent[2];
}
else {
return "";
}
}
}

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.2 (2012-02-26)
* Version 1.4 (2014-08-10)
*
*/
@ -80,7 +80,7 @@ public final class ConverterPalette extends ConverterBase {
private InlineConverter inlineCv;
private FieldConverter fieldCv;
private DrawConverter drawCv;
private MathmlConverter mathmlCv;
private MathConverter mathCv;
private Info info;
// Constructor
@ -117,7 +117,7 @@ public final class ConverterPalette extends ConverterBase {
public InlineConverter getInlineCv() { return inlineCv; }
public FieldConverter getFieldCv() { return fieldCv; }
public DrawConverter getDrawCv() { return drawCv; }
public MathmlConverter getMathmlCv() { return mathmlCv; }
public MathConverter getMathCv() { return mathCv; }
public Info getInfo() { return info; }
@ -172,7 +172,7 @@ public final class ConverterPalette extends ConverterBase {
inlineCv = new InlineConverter(ofr,config,this);
fieldCv = new FieldConverter(ofr,config,this);
drawCv = new DrawConverter(ofr,config,this);
mathmlCv = new MathmlConverter(ofr,config,this);
mathCv = new MathConverter(ofr,config,this);
info = new Info(ofr,config,this);
// Create master document and add this
@ -223,7 +223,7 @@ public final class ConverterPalette extends ConverterBase {
inlineCv.appendDeclarations(packages,declarations);
fieldCv.appendDeclarations(packages,declarations);
drawCv.appendDeclarations(packages,declarations);
mathmlCv.appendDeclarations(packages,declarations);
mathCv.appendDeclarations(packages,declarations);
// Add custom preamble
String sCustomPreamble = config.getCustomPreamble();

View file

@ -20,7 +20,7 @@
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-06)
* Version 1.4 (2014-08-11)
*
*/
@ -31,17 +31,13 @@ import java.util.Stack;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
//import org.w3c.dom.Node;
import writer2latex.latex.util.BeforeAfter;
import writer2latex.latex.util.Context;
//import writer2latex.office.ImageLoader;
import writer2latex.office.EmbeddedObject;
import writer2latex.office.EmbeddedXMLObject;
import writer2latex.office.MIMETypes;
import writer2latex.office.OfficeReader;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.XMLString;
import writer2latex.util.CSVList;
import writer2latex.util.Misc;
@ -53,9 +49,8 @@ import writer2latex.xmerge.BinaryGraphicsDocument;
public class DrawConverter extends ConverterHelper {
private boolean bNeedGraphicx = false;
private boolean bNeedOOoLaTeXPreamble = false;
// Keep track of floating frames (images, textboxes...)
// Keep track of floating frames (images, text boxes...)
private Stack<LinkedList<Element>> floatingFramesStack = new Stack<LinkedList<Element>>();
private Element getFrame(Element onode) {
@ -76,17 +71,6 @@ public class DrawConverter extends ConverterHelper {
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) {
@ -129,25 +113,16 @@ public class DrawConverter extends ConverterHelper {
palette.getFieldCv().handleAnchor(node,ldp,oc);
}
else if (sName.equals(XMLString.DRAW_FRAME)) {
Element equation = palette.getTexMathsEquation(node);
if (equation!=null) {
palette.getMathmlCv().handleTexMathsEquation(equation,ldp,oc);
}
else {
if (!palette.getMathCv().handleTexMathsEquation(node,ldp)) {
// OpenDocument: Get the actual draw element in the frame
handleDrawElement(Misc.getFirstChildElement(node),ldp,oc);
}
}
else if (sName.equals(XMLString.DRAW_G)) {
Element equation = palette.getTexMathsEquation(node);
if (equation!=null) {
palette.getMathmlCv().handleTexMathsEquation(equation,ldp,oc);
}
else {
if (!palette.getMathCv().handleTexMathsEquation(node,ldp)) {
// Shapes are currently not supported
ldp.append("[Warning: Draw object ignored]");
}
}
else {
// Other drawing objects are currently not supported
@ -159,6 +134,7 @@ public class DrawConverter extends ConverterHelper {
// 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
@ -169,13 +145,13 @@ public class DrawConverter extends ConverterHelper {
if (object!=null) {
if (MIMETypes.MATH.equals(object.getType()) || MIMETypes.ODF.equals(object.getType())) { // Formula!
try {
Document settings = ((EmbeddedXMLObject) object).getSettingsDOM();
Element settings = ((EmbeddedXMLObject) object).getSettingsDOM().getDocumentElement();
Document formuladoc = ((EmbeddedXMLObject) object).getContentDOM();
Element formula = Misc.getChildByTagName(formuladoc,XMLString.MATH); // Since OOo3.2
if (formula==null) {
formula = Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH);
}
String sLaTeX = palette.getMathmlCv().convert(settings,formula);
String sLaTeX = palette.getMathCv().convert(settings,formula);
if (!" ".equals(sLaTeX)) { // ignore empty formulas
ldp.append(" $")
.append(sLaTeX)
@ -214,7 +190,7 @@ public class DrawConverter extends ConverterHelper {
}
if (formula!=null) {
ldp.append(" $")
.append(palette.getMathmlCv().convert(null,formula))
.append(palette.getMathCv().convert(null,formula))
.append("$");
if (Character.isLetterOrDigit(OfficeReader.getNextChar(node))) { ldp.append(" "); }
}
@ -233,7 +209,6 @@ public class DrawConverter extends ConverterHelper {
}
}
}
//--------------------------------------------------------------------------
@ -288,47 +263,6 @@ public class DrawConverter extends ConverterHelper {
String sName = frame.getAttribute(XMLString.DRAW_NAME);
palette.getFieldCv().addTarget(sName,"|graphic",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)) {

View file

@ -0,0 +1,392 @@
/************************************************************************
*
* MathConverter.java
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-13)
*
*/
package writer2latex.latex;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.base.ConverterBase.TexMathsStyle;
import writer2latex.office.EmbeddedObject;
import writer2latex.office.EmbeddedXMLObject;
import writer2latex.office.MIMETypes;
import writer2latex.office.OfficeReader;
import writer2latex.office.StyleWithProperties;
import writer2latex.office.TableReader;
import writer2latex.office.XMLString;
import writer2latex.util.Misc;
/**
* This ConverterHelper converts mathematical content to LaTeX.
* It works slightly different than the other helpers: A number of elements may or may not
* have content that should be converted to math. Thus the methods offered first examines
* the content. If it turns out to be a mathematical formula, it is converted. Otherwise
* nothing is done, and the method returns false.
* Mathematical content may be MathML (with StarMath annotation), TexMaths or (the now obsolete) OOoLaTeX
*/
public final class MathConverter extends ConverterHelper {
private StarMathConverter smc;
private boolean bContainsFormulas = false;
private boolean bAddParAfterDisplay = false;
private boolean bNeedOOoLaTeXPreamble = false;
private Element theEquation = null;
private Element theSequence = null;
public MathConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
smc = new StarMathConverter(palette.getI18n(),config);
bAddParAfterDisplay = config.formatting()>=LaTeXConfig.CONVERT_MOST;
}
public void appendDeclarations(LaTeXDocumentPortion pack, LaTeXDocumentPortion decl) {
if (bContainsFormulas) {
if (config.useOoomath()) {
pack.append("\\usepackage{ooomath}").nl();
}
else {
smc.appendDeclarations(pack,decl);
}
}
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: Replace with a method "handleEquation"
public String convert(Element settings, Element 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.SEMANTICS); // Since OOo 3.2
if (semantics==null) {
semantics = Misc.getChildByTagName(formula,XMLString.MATH_SEMANTICS);
}
if (semantics!=null) {
Node annotation = Misc.getChildByTagName(semantics,XMLString.ANNOTATION); // Since OOo 3.2
if (annotation==null) {
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)
if (formula.hasChildNodes()) {
return "\\text{Warning: No StarMath annotation}";
}
else { // empty formula
return " ";
}
}
/** Try to convert a draw:frame or draw:g element as an (inline) TexMaths or OOoLaTeX equation
*
* @param the element containing the equation (draw:frame or draw:g)
* @param ldp the LaTeXDocumentPortion to contain the converted equation
*
* @return true if this elements happen to be a TexMaths equation, otherwise false
*/
public boolean handleTexMathsEquation(Element node, LaTeXDocumentPortion ldp) {
String sLaTeX = null;
Element equation = palette.getTexMathsEquation(node);
if (equation!=null) {
sLaTeX = Misc.getPCDATA(equation);
}
else { // Try OOoLaTeX
// The LaTeX code is embedded in a custom style attribute:
StyleWithProperties style = ofr.getFrameStyle(Misc.getAttribute(node, XMLString.DRAW_STYLE_NAME));
if (style!=null) {
sLaTeX = style.getProperty("OOoLatexArgs");
}
}
if (sLaTeX!=null) {
// Format is <point size>X<mode>X<TeX code>X<format>X<resolution>X<transparency>
// where X is a paragraph sign
switch (palette.getTexMathsStyle(sLaTeX)) {
case inline:
ldp.append("$").append(palette.getTexMathsEquation(sLaTeX)).append("$");
break;
case display:
ldp.append("$\\displaystyle ").append(palette.getTexMathsEquation(sLaTeX)).append("$");
break;
case latex:
ldp.append(palette.getTexMathsEquation(sLaTeX));
}
return true;
}
return false;
}
/** Try to convert a table as a display equation:
* A 1 row by 2 columns table in which each cell contains exactly one paragraph,
* the left cell contains exactly one formula and the right cell contains exactly
* one sequence number is treated as a (numbered) display equation.
* This happens to coincide with the AutoText provided with OOo Writer :-)
* @param table the table reader
* @param ldp the LaTeXDocumentPortion to contain the converted equation
* @return true if the conversion was successful, false if the table
* did not represent a display equation
*/
public boolean handleDisplayEquation(TableReader table, LaTeXDocumentPortion ldp) {
if (table.getRowCount()==1 && table.getColCount()==2 &&
OfficeReader.isSingleParagraph(table.getCell(0, 0)) && OfficeReader.isSingleParagraph(table.getCell(0, 1)) ) {
// Table of the desired form
if (parseDisplayEquation(Misc.getFirstChildElement(table.getCell(0, 0))) && theEquation!=null && theSequence==null) {
// Found equation in first cell
Element myEquation = theEquation;
if (parseDisplayEquation(Misc.getFirstChildElement(table.getCell(0, 1))) && theEquation==null && theSequence!=null) {
// Found sequence in second cell
handleDisplayEquation(myEquation, theSequence, ldp);
return true;
}
}
}
return false;
}
/**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 (possibly with formatting).
* @param node the paragraph
* @param ldp the LaTeXDocumentPortion to contain the converted equation
* @return true if the conversion was successful, false if the paragraph
* did not contain a display equation
*/
public boolean handleDisplayEquation(Element node, LaTeXDocumentPortion ldp) {
if (parseDisplayEquation(node) && theEquation!=null) {
handleDisplayEquation(theEquation, theSequence, ldp);
return true;
}
else {
return false;
}
}
private void handleDisplayEquation(Element equation, Element sequence, LaTeXDocumentPortion ldp) {
boolean bTexMaths = equation.getTagName().equals(XMLString.SVG_DESC);
TexMathsStyle style = TexMathsStyle.inline;
String sLaTeX;
if (bTexMaths) {
// TeXMaths equation
sLaTeX = palette.getTexMathsEquation(Misc.getPCDATA(equation));
style = palette.getTexMathsStyle(Misc.getPCDATA(equation));
}
else {
// MathML equation
sLaTeX = convert(null,equation);
}
if (!" ".equals(sLaTeX)) { // ignore empty formulas
if (!bTexMaths || style!=TexMathsStyle.latex) {
if (sequence!=null) {
// Numbered equation
ldp.append("\\begin{equation}");
palette.getFieldCv().handleSequenceLabel(sequence,ldp);
if (bTexMaths && style==TexMathsStyle.inline) {
ldp.append("\\textstyle ");
}
ldp.nl()
.append(sLaTeX).nl()
.append("\\end{equation}").nl();
}
else {
// Unnumbered equation
ldp.append("\\begin{equation*}");
if (bTexMaths && style==TexMathsStyle.inline) {
ldp.append("\\textstyle ");
}
ldp.nl()
.append(sLaTeX).nl()
.append("\\end{equation*}").nl();
}
}
else {
ldp.append(sLaTeX).nl();
}
if (bAddParAfterDisplay) { ldp.nl(); }
}
}
/** Determine whether or not a paragraph contains a display equation.
* A paragraph is a display equation if it contains a single formula and no text content except whitespace
* and an optional sequence number which may be in brackets.
* As a side effect, this method keeps a reference to the equation and the sequence number
*
* @param node the paragraph
* @return true if this is a display equation
*/
public boolean parseDisplayEquation(Node node) {
theEquation = null;
theSequence = null;
return doParseDisplayEquation(node);
}
private boolean doParseDisplayEquation(Node node) {
Node child = node.getFirstChild();
while (child!=null) {
if (Misc.isElement(child)) {
Element elm = (Element) child;
String sName = elm.getTagName();
// First check for MathML or TexMaths equation
Element equation = getMathmlEquation(elm);
if (equation==null) {
equation = palette.getTexMathsEquation(elm);
}
if (equation!=null) {
if (theEquation==null) {
theEquation = equation;
}
else { // two or more equations -> not a display
return false;
}
}
else if (XMLString.TEXT_SEQUENCE.equals(sName)) {
if (theSequence==null) {
theSequence = elm;
}
else { // two sequence numbers -> not a display
return false;
}
}
else if (XMLString.TEXT_SPAN.equals(sName)) {
if (!doParseDisplayEquation(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 if (XMLString.TEXT_SOFT_PAGE_BREAK.equals(sName)) { // since ODF 1.1
// Soft page breaks 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;
}
/** Get a MathML formula from a draw:frame
*
* @param node the draw:frame
* @return the MathML element, or null if this is not a MathML formula
*/
public Element getMathmlEquation(Element node) {
if (node.getTagName().equals(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();
Element formula = Misc.getChildByTagName(formuladoc,XMLString.MATH); // Since OOo 3.2
if (formula==null) {
formula = Misc.getChildByTagName(formuladoc,XMLString.MATH_MATH);
}
return formula;
}
catch (org.xml.sax.SAXException e) {
e.printStackTrace();
}
catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
}
}
else { // flat XML, object is contained in node
Element formula = Misc.getChildByTagName(node,XMLString.MATH); // Since OOo 3.2
if (formula==null) {
formula = Misc.getChildByTagName(node,XMLString.MATH_MATH);
}
return formula;
}
return null;
}
}

View file

@ -1,249 +0,0 @@
/************************************************************************
*
* 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-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.4 (2014-08-08)
*
*/
package writer2latex.latex;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import writer2latex.latex.util.Context;
import writer2latex.office.OfficeReader;
import writer2latex.office.TableReader;
import writer2latex.office.XMLString;
import writer2latex.util.Misc;
/**
* This class converts MathML nodes to LaTeX.
* The class name is slightly misleading:
* It only converts the StarMath annotation, if available
* and it also converts TexMaths formulas
*/
public final class MathmlConverter extends ConverterHelper {
private enum TexMathsStyle {inline, display, latex};
private StarMathConverter smc;
private boolean bContainsFormulas = false;
private boolean bAddParAfterDisplay = false;
public MathmlConverter(OfficeReader ofr,LaTeXConfig config, ConverterPalette palette) {
super(ofr,config,palette);
smc = new StarMathConverter(palette.getI18n(),config);
bAddParAfterDisplay = config.formatting()>=LaTeXConfig.CONVERT_MOST;
}
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.SEMANTICS); // Since OOo 3.2
if (semantics==null) {
semantics = Misc.getChildByTagName(formula,XMLString.MATH_SEMANTICS);
}
if (semantics!=null) {
Node annotation = Misc.getChildByTagName(semantics,XMLString.ANNOTATION); // Since OOo 3.2
if (annotation==null) {
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)
if (formula.hasChildNodes()) {
return "\\text{Warning: No StarMath annotation}";
}
else { // empty formula
return " ";
}
}
/** Handle an (inline) TexMaths equation
*
* @param node the equation (an svg:desc element containing the formula)
* @param ldp the LaTeXDocumentPortion to contain the converted equation
* @param oc the current context
*/
public void handleTexMathsEquation(Element node, LaTeXDocumentPortion ldp, Context oc) {
// LaTeX code is contained in svg:desc
// Format is <point size>X<mode>X<TeX code>X<format>X<resolution>X<transparency>
// where X is a paragraph sign
switch (getTexMathsStyle(node)) {
case inline:
ldp.append("$").append(getTexMathsEquation(node)).append("$");
break;
case display:
ldp.append("$\\displaystyle ").append(getTexMathsEquation(node)).append("$");
break;
case latex:
ldp.append(getTexMathsEquation(node));
}
}
private TexMathsStyle getTexMathsStyle(Element node) {
String[] sContent = Misc.getPCDATA(node).split("\u00a7");
if (sContent.length>=3) { // we only need 3 items of 6
if ("display".equals(sContent[1])) {
return TexMathsStyle.display;
}
else if ("latex".equals(sContent[1])) {
return TexMathsStyle.latex;
}
}
return TexMathsStyle.inline;
}
private String getTexMathsEquation(Element node) {
String[] sContent = Misc.getPCDATA(node).split("\u00a7");
if (sContent.length>=3) { // we only need 3 items of 6
return sContent[2];
}
else {
return "";
}
}
/** Try to convert a table as a display equation:
* A 1 row by 2 columns table in which each cell contains exactly one paragraph,
* the left cell contains exactly one formula and the right cell contains exactly
* one sequence number is treated as a (numbered) display equation.
* This happens to coincide with the AutoText provided with OOo Writer :-)
* @param table the table reader
* @param ldp the LaTeXDocumentPortion to contain the converted equation
* @return true if the conversion was successful, false if the table
* did not represent a display equation
*/
public boolean handleDisplayEquation(TableReader table, LaTeXDocumentPortion ldp) {
if (table.getRowCount()==1 && table.getColCount()==2 &&
OfficeReader.isSingleParagraph(table.getCell(0, 0)) && OfficeReader.isSingleParagraph(table.getCell(0, 1)) ) {
// Table of the desired form
if (palette.parseDisplayEquation(Misc.getFirstChildElement(table.getCell(0, 0))) && palette.getEquation()!=null && palette.getSequence()==null) {
// Found equation in first cell
Element myEquation = palette.getEquation();
if (palette.parseDisplayEquation(Misc.getFirstChildElement(table.getCell(0, 1))) && palette.getEquation()==null && palette.getSequence()!=null) {
// Found sequence in second cell
handleDisplayEquation(myEquation, palette.getSequence(), ldp);
return true;
}
}
}
return false;
}
/**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 (possibly with formatting).
* @param node the paragraph
* @param ldp the LaTeXDocumentPortion to contain the converted equation
* @return true if the conversion was successful, false if the paragraph
* did not contain a display equation
*/
public boolean handleDisplayEquation(Element node, LaTeXDocumentPortion ldp) {
if (palette.parseDisplayEquation(node) && palette.getEquation()!=null) {
handleDisplayEquation(palette.getEquation(), palette.getSequence(), ldp);
return true;
}
else {
return false;
}
}
private void handleDisplayEquation(Element equation, Element sequence, LaTeXDocumentPortion ldp) {
boolean bTexMaths = equation.getTagName().equals(XMLString.SVG_DESC);
TexMathsStyle style = TexMathsStyle.inline;
String sLaTeX;
if (bTexMaths) {
// TeXMaths equation
sLaTeX = getTexMathsEquation(equation);
style = getTexMathsStyle(equation);
}
else {
// MathML equation
sLaTeX = convert(null,equation);
}
if (!" ".equals(sLaTeX)) { // ignore empty formulas
if (!bTexMaths || style!=TexMathsStyle.latex) {
if (sequence!=null) {
// Numbered equation
ldp.append("\\begin{equation}");
palette.getFieldCv().handleSequenceLabel(sequence,ldp);
if (bTexMaths && style==TexMathsStyle.inline) {
ldp.append("\\textstyle ");
}
ldp.nl()
.append(sLaTeX).nl()
.append("\\end{equation}").nl();
}
else {
// Unnumbered equation
ldp.append("\\begin{equation*}");
if (bTexMaths && style==TexMathsStyle.inline) {
ldp.append("\\textstyle ");
}
ldp.nl()
.append(sLaTeX).nl()
.append("\\end{equation*}").nl();
}
}
else {
ldp.append(sLaTeX).nl();
}
if (bAddParAfterDisplay) { ldp.nl(); }
}
}
}

View file

@ -98,7 +98,7 @@ public class ParConverter extends StyleConverter {
* \end{enumerate}).
*/
public void handleParagraph(Element node, LaTeXDocumentPortion ldp, Context oc, boolean bLastInBlock) {
if (palette.getMathmlCv().handleDisplayEquation(node,ldp)) { return; }
if (palette.getMathCv().handleDisplayEquation(node,ldp)) { return; }
// Get the style name for this paragraph
String sStyleName = node.getAttribute(XMLString.TEXT_STYLE_NAME);

View file

@ -166,7 +166,7 @@ public class TableConverter extends ConverterHelper {
// Read the table
table = ofr.getTableReader(node);
if (palette.getMathmlCv().handleDisplayEquation(table,ldp)) { return; }
if (palette.getMathCv().handleDisplayEquation(table,ldp)) { return; }
// Get formatter and update flags according to formatter
formatter = new TableFormatter(ofr,config,palette,table,!oc.isInMulticols(),oc.isInTable());

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-07)
* Version 1.4 (2014-08-13)
*
*/
@ -351,7 +351,7 @@ public class Converter extends ConverterBase {
// Load MathJax
// TODO: Should we support different configurations of MathJax?
if (nType==XhtmlDocument.HTML5 && config.useMathJax()) {
if ((nType==XhtmlDocument.HTML5 || nType==XhtmlDocument.XHTML_MATHML) && config.useMathJax()) {
for (int i=0; i<=nOutFileIndex; i++) {
if (outFiles.get(i).hasMath()) {
XhtmlDocument doc = outFiles.get(i);
@ -360,7 +360,7 @@ public class Converter extends ConverterBase {
Element script = doc.getContentDOM().createElement("script");
head.appendChild(script);
script.setAttribute("type", "text/javascript");
script.setAttribute("src", "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=MML_HTMLorMML");
script.setAttribute("src", "http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML");
}
}
}

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.4 (2012-03-30)
* Version 1.4 (2014-08-13)
*
*/
@ -58,7 +58,7 @@ public class ConverterHelper {
protected PresentationStyleConverter getPresentationSc() { return converter.getStyleCv().getPresentationSc(); }
protected PageStyleConverter getPageSc() { return converter.getStyleCv().getPageSc(); }
protected TextConverter getTextCv() { return converter.getTextCv(); }
protected TableConverter getTableCv() { return converter.getTableCv(); }

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-07)
* Version 1.4 (2014-08-12)
*
*/
@ -331,16 +331,22 @@ public class DrawConverter extends ConverterHelper {
}
}
else if (sName.equals(XMLString.DRAW_FRAME)) {
// OpenDocument embeds the draw element in a frame element
handleDrawElement(Misc.getFirstChildElement(onode),hnodeBlock,hnodeInline,nMode);
// First check for TexMaths equation
if (!getMathCv().convertTexMathsEquation(onode, hnodeBlock, hnodeInline, nMode)) {
// OpenDocument embeds the draw element in a frame element
handleDrawElement(Misc.getFirstChildElement(onode),hnodeBlock,hnodeInline,nMode);
}
}
else if (sName.equals(XMLString.DRAW_G)) {
handleDrawGroup(onode,hnodeBlock,hnodeInline,nMode);
// First check for TexMaths equation
if (!getMathCv().convertTexMathsEquation(onode, hnodeBlock, hnodeInline, nMode)) {
handleDrawGroup(onode,hnodeBlock,hnodeInline,nMode);
}
}
else if (sName.equals(XMLString.DRAW_CONTROL)) {
handleDrawControl(onode,hnodeBlock,hnodeInline,nMode);
}
}
}
private void handleDrawObject(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) {
// TODO: Placement if not inline

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-07)
* Version 1.4 (2014-08-13)
*
*/
@ -72,9 +72,6 @@ public class MathConverter extends ConverterHelper {
*/
public void convert(Node image, Element onode, Node hnode) {
if (bSupportMathML) {
if (converter.getTextCv().isDisplayEquation()) {
onode.setAttribute("display", "block");
}
convertAsMathML(onode,hnode);
}
else {
@ -82,6 +79,57 @@ public class MathConverter extends ConverterHelper {
}
}
public boolean convertTexMathsEquation(Element onode, Element hnodeBlock, Element hnodeInline, int nMode) {
// If possible, add the object inline. In pure block context, add a div.
Element hnode;
if (hnodeInline!=null) {
hnode = hnodeInline;
}
else {
Element div = converter.createElement("div");
hnodeBlock.appendChild(div);
hnode = div;
}
String sLaTeX = null;
Element equation = converter.getTexMathsEquation(onode);
if (equation!=null) {
sLaTeX = Misc.getPCDATA(equation);
}
else { // Try OOoLaTeX
// The LaTeX code is embedded in a custom style attribute:
StyleWithProperties style = ofr.getFrameStyle(Misc.getAttribute(onode, XMLString.DRAW_STYLE_NAME));
if (style!=null) {
sLaTeX = style.getProperty("OOoLatexArgs");
}
}
if (sLaTeX!=null) {
// Format is <point size>X<mode>X<TeX code>X<format>X<resolution>X<transparency>
// where X is a paragraph sign
String sMathJax;
if (config.useMathJax() && bSupportMathML) {
switch (converter.getTexMathsStyle(sLaTeX)) {
case inline:
sMathJax = "\\("+converter.getTexMathsEquation(sLaTeX)+"\\)";
break;
case display:
sMathJax = "\\["+converter.getTexMathsEquation(sLaTeX)+"\\]";
break;
case latex:
default: // Arbitrary LaTeX; this is the tricky bit
sMathJax = "\\("+converter.getTexMathsEquation(sLaTeX)+"\\)";
}
}
else {
sMathJax = " "+converter.getTexMathsEquation(sLaTeX)+" ";
}
hnode.appendChild(converter.createTextNode(sMathJax));
return true;
}
return false;
}
// For plain xhtml: Convert the formula as an image or as plain text
private void convertAsImageOrText(Node image, Node onode, Node hnode) {
NodeList annotationList = ((Element) onode).getElementsByTagName(XMLString.ANNOTATION); // Since OOo 3.2

View file

@ -16,18 +16,16 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2011 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.2 (2011-02-17)
* Version 1.4 (2014-08-13)
*
*/
package writer2latex.xhtml;
//import java.util.Enumeration;
//import java.util.Hashtable;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@ -64,7 +62,7 @@ class StyleConverter extends ConverterHelper {
// Helper for page styles
private PageStyleConverter pageSc;
/** <p>Create a new <code>StyleConverter</code></p>
*/
public StyleConverter(OfficeReader ofr, XhtmlConfig config, Converter converter, int nType) {

View file

@ -16,11 +16,11 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* Copyright: 2002-2012 by Henrik Just
* Copyright: 2002-2014 by Henrik Just
*
* All Rights Reserved.
*
* Version 1.4 (2012-04-07)
* Version 1.4 (2014-08-13)
*
*/
@ -141,9 +141,6 @@ public class TextConverter extends ConverterHelper {
// We put it here and insert it in the first paragraph/heading to come:
private Node asapNode = null;
// Are we within a display equation?
private boolean bDisplayEquation = false;
// When generating toc, a few things should be done differently
private boolean bInToc = false;
@ -361,10 +358,6 @@ public class TextConverter extends ConverterHelper {
return inline;
}
public boolean isDisplayEquation() {
return bDisplayEquation;
}
////////////////////////////////////////////////////////////////////////
// BLOCK TEXT (returns current html node at end of block)
////////////////////////////////////////////////////////////////////////
@ -820,9 +813,7 @@ public class TextConverter extends ConverterHelper {
insertListLabel(currentListStyle, nCurrentListLevel, "ItemNumber", null, sCurrentListLabel, par);
}
sCurrentListLabel = null;
bDisplayEquation=converter.parseDisplayEquation(onode);
traverseInlineText(onode,par);
bDisplayEquation=false;
}
else {
// An empty paragraph (this includes paragraphs that only contains