/************************************************************************ * * 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; /** *

This class handles conversion of footnotes and endnotes, including * references. It takes advantage of the packages endnotes.sty * and perpage.sty if allowed in the configuration.

*/ 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); } /**

Append declarations needed by the NoteConverter 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) { if (bContainsEndnotes) { pack.append("\\usepackage{endnotes}").nl(); } if (bContainsFootnotes) convertFootnotesConfiguration(decl); if (bContainsEndnotes) convertEndnotesConfiguration(decl); } /**

Process a footnote (text:footnote tag) * @param node The element containing the footnote * @param ldp the LaTeXDocumentPortion 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(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; iProcess an endnote (text:endnote tag) * @param node The element containing the endnote * @param ldp the LaTeXDocumentPortion 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("}"); } } } /**

Insert the endnotes into the documents. * @param ldp the LaTeXDocumentPortion to which * the endnotes should be added. */ public void insertEndnotes(LaTeXDocumentPortion ldp) { if (bContainsEndnotes) { ldp.append("\\clearpage").nl() .append("\\theendnotes").nl(); } } /**

Process a note reference (text:note-ref tag, oasis) * @param node The element containing the note reference * @param ldp the LaTeXDocumentPortion 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); } } /**

Process a footnote reference (text:footnote-ref tag) * @param node The element containing the footnote reference * @param ldp the LaTeXDocumentPortion 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); } } /**

Process an endnote reference (text:endnote-ref tag) * @param node The element containing the endnote reference * @param ldp the LaTeXDocumentPortion 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); } } /**

Add a footnote name. The method handleFootnote includes * a \label only if the footnote name is already known to the * NoteConverter. 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); } /**

Add an endnote name. The method handleEndnote includes * a \label only if the endnote name is already known to the * NoteConverter. 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=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(); } }